|
2 | 2 | import os
|
3 | 3 | import sys
|
4 | 4 | from collections import defaultdict
|
5 |
| -from pathlib import Path |
6 | 5 | from typing import Dict, Set, Tuple
|
7 | 6 |
|
8 |
| -from pipenv.exceptions import JSONParseError, PipenvCmdError |
9 | 7 | from pipenv.patched.pip._vendor.packaging.specifiers import SpecifierSet
|
10 | 8 | from pipenv.patched.pip._vendor.packaging.version import InvalidVersion, Version
|
11 | 9 | from pipenv.routines.outdated import do_outdated
|
|
17 | 15 | get_lockfile_section_using_pipfile_category,
|
18 | 16 | get_pipfile_category_using_lockfile_section,
|
19 | 17 | )
|
20 |
| -from pipenv.utils.processes import run_command |
21 | 18 | from pipenv.utils.project import ensure_project
|
22 | 19 | from pipenv.utils.requirements import add_index_to_pipfile
|
23 | 20 | from pipenv.utils.resolver import venv_resolve_deps
|
24 |
| -from pipenv.vendor import pipdeptree |
| 21 | +from pipenv.vendor.pipdeptree._discovery import get_installed_distributions |
| 22 | +from pipenv.vendor.pipdeptree._models import PackageDAG |
25 | 23 |
|
26 | 24 |
|
27 | 25 | def do_update(
|
@@ -106,44 +104,25 @@ def do_update(
|
106 | 104 |
|
107 | 105 |
|
108 | 106 | def get_reverse_dependencies(project) -> Dict[str, Set[Tuple[str, str]]]:
|
109 |
| - """Get reverse dependencies using pipdeptree.""" |
110 |
| - pipdeptree_path = Path(pipdeptree.__file__).parent |
111 |
| - python_path = project.python() |
112 |
| - cmd_args = [python_path, str(pipdeptree_path), "-l", "--reverse", "--json-tree"] |
113 |
| - |
114 |
| - c = run_command(cmd_args, is_verbose=project.s.is_verbose()) |
115 |
| - if c.returncode != 0: |
116 |
| - raise PipenvCmdError(c.err, c.out, c.returncode) |
117 |
| - try: |
118 |
| - dep_tree = json.loads(c.stdout.strip()) |
119 |
| - except json.JSONDecodeError: |
120 |
| - raise JSONParseError(c.stdout, c.stderr) |
121 |
| - |
122 |
| - # Build reverse dependency map: package -> set of (dependent_package, required_version) |
123 |
| - reverse_deps = defaultdict(set) |
| 107 | + """Get reverse dependencies without running pipdeptree as a subprocess.""" |
124 | 108 |
|
125 |
| - def process_tree_node(n, parents=None): |
126 |
| - if parents is None: |
127 |
| - parents = [] |
| 109 | + # Use the project's specified Python interpreter |
| 110 | + python_interpreter = project.python() |
128 | 111 |
|
129 |
| - package_name = n["package_name"] |
130 |
| - required_version = n.get("required_version", "Any") |
| 112 | + # Get installed packages for the specified interpreter |
| 113 | + pkgs = get_installed_distributions(interpreter=python_interpreter) |
131 | 114 |
|
132 |
| - # Add the current node to its parents' reverse dependencies |
133 |
| - for parent in parents: |
134 |
| - reverse_deps[parent].add((package_name, required_version)) |
| 115 | + # Create a package dependency tree (DAG) and reverse it |
| 116 | + dep_tree = PackageDAG.from_pkgs(pkgs).reverse() |
135 | 117 |
|
136 |
| - # Process dependencies recursively, keeping track of parent path |
137 |
| - for dep in n.get("dependencies", []): |
138 |
| - process_tree_node(dep, parents + [package_name]) |
| 118 | + # Initialize reverse dependency map |
| 119 | + reverse_deps = defaultdict(set) |
139 | 120 |
|
140 |
| - # Start processing the tree from the root nodes |
141 |
| - for node in dep_tree: |
142 |
| - try: |
143 |
| - process_tree_node(node) |
144 |
| - except Exception as e: # noqa: PERF203 |
145 |
| - err.print( |
146 |
| - f"[red bold]Warning[/red bold]: Unable to analyze dependencies: {str(e)}" |
| 121 | + # Populate the reverse dependency map |
| 122 | + for package, dependents in dep_tree.items(): |
| 123 | + for dep in dependents: |
| 124 | + reverse_deps[dep.project_name].add( |
| 125 | + (package.project_name, getattr(package, "installed_version", "Any")) |
147 | 126 | )
|
148 | 127 |
|
149 | 128 | return reverse_deps
|
@@ -290,8 +269,13 @@ def upgrade(
|
290 | 269 | # Early conflict detection
|
291 | 270 | conflicts_found = False
|
292 | 271 | for package in package_args:
|
293 |
| - if "==" in package: |
294 |
| - name, version = package.split("==") |
| 272 | + package_parts = [package] |
| 273 | + if ";" in package: |
| 274 | + package_parts = package.split(";") |
| 275 | + # Not using markers here for now |
| 276 | + # markers = ";".join(package_parts[1:]) if len(package_parts) > 1 else None |
| 277 | + if "==" in package_parts[0]: |
| 278 | + name, version = package_parts[0].split("==") |
295 | 279 | conflicts = check_version_conflicts(name, version, reverse_deps, lockfile)
|
296 | 280 | if conflicts:
|
297 | 281 | conflicts_found = True
|
|
0 commit comments