|
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