Skip to content

Commit 11a9990

Browse files
authored
Merge pull request #10084 from uranusjr/new-resolver-local-dependencies
2 parents ad9d46e + 73edd74 commit 11a9990

File tree

4 files changed

+81
-42
lines changed

4 files changed

+81
-42
lines changed

news/9204.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
New resolver: Detect an unnamed requirement is user-specified (by building its
2+
metadata for the project name) so it can be correctly ordered in the resolver.

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
Iterator,
1010
List,
1111
Mapping,
12+
NamedTuple,
1213
Optional,
1314
Sequence,
1415
Set,
@@ -38,7 +39,10 @@
3839
from pip._internal.models.wheel import Wheel
3940
from pip._internal.operations.prepare import RequirementPreparer
4041
from pip._internal.req.constructors import install_req_from_link_and_ireq
41-
from pip._internal.req.req_install import InstallRequirement
42+
from pip._internal.req.req_install import (
43+
InstallRequirement,
44+
check_invalid_constraint_type,
45+
)
4246
from pip._internal.resolution.base import InstallRequirementProvider
4347
from pip._internal.utils.compatibility_tags import get_supported
4448
from pip._internal.utils.hashes import Hashes
@@ -81,6 +85,12 @@ class ConflictCause(Protocol):
8185
Cache = Dict[Link, C]
8286

8387

88+
class CollectedRootRequirements(NamedTuple):
89+
requirements: List[Requirement]
90+
constraints: Dict[str, Constraint]
91+
user_requested: Dict[str, int]
92+
93+
8494
class Factory:
8595
def __init__(
8696
self,
@@ -408,7 +418,7 @@ def find_candidates(
408418
and all(req.is_satisfied_by(c) for req in requirements[identifier])
409419
)
410420

411-
def make_requirement_from_install_req(
421+
def _make_requirement_from_install_req(
412422
self, ireq: InstallRequirement, requested_extras: Iterable[str]
413423
) -> Optional[Requirement]:
414424
if not ireq.match_markers(requested_extras):
@@ -440,6 +450,36 @@ def make_requirement_from_install_req(
440450
return UnsatisfiableRequirement(canonicalize_name(ireq.name))
441451
return self.make_requirement_from_candidate(cand)
442452

453+
def collect_root_requirements(
454+
self, root_ireqs: List[InstallRequirement]
455+
) -> CollectedRootRequirements:
456+
collected = CollectedRootRequirements([], {}, {})
457+
for i, ireq in enumerate(root_ireqs):
458+
if ireq.constraint:
459+
# Ensure we only accept valid constraints
460+
problem = check_invalid_constraint_type(ireq)
461+
if problem:
462+
raise InstallationError(problem)
463+
if not ireq.match_markers():
464+
continue
465+
assert ireq.name, "Constraint must be named"
466+
name = canonicalize_name(ireq.name)
467+
if name in collected.constraints:
468+
collected.constraints[name] &= ireq
469+
else:
470+
collected.constraints[name] = Constraint.from_ireq(ireq)
471+
else:
472+
req = self._make_requirement_from_install_req(
473+
ireq,
474+
requested_extras=(),
475+
)
476+
if req is None:
477+
continue
478+
if ireq.user_supplied and req.name not in collected.user_requested:
479+
collected.user_requested[req.name] = i
480+
collected.requirements.append(req)
481+
return collected
482+
443483
def make_requirement_from_candidate(
444484
self, candidate: Candidate
445485
) -> ExplicitRequirement:
@@ -452,7 +492,7 @@ def make_requirement_from_spec(
452492
requested_extras: Iterable[str] = (),
453493
) -> Optional[Requirement]:
454494
ireq = self._make_install_req_from_spec(specifier, comes_from)
455-
return self.make_requirement_from_install_req(ireq, requested_extras)
495+
return self._make_requirement_from_install_req(ireq, requested_extras)
456496

457497
def make_requires_python_requirement(
458498
self, specifier: Optional[SpecifierSet]

src/pip/_internal/resolution/resolvelib/resolver.py

Lines changed: 7 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,9 @@
1010
from pip._vendor.resolvelib.structs import DirectedGraph
1111

1212
from pip._internal.cache import WheelCache
13-
from pip._internal.exceptions import InstallationError
1413
from pip._internal.index.package_finder import PackageFinder
1514
from pip._internal.operations.prepare import RequirementPreparer
16-
from pip._internal.req.req_install import (
17-
InstallRequirement,
18-
check_invalid_constraint_type,
19-
)
15+
from pip._internal.req.req_install import InstallRequirement
2016
from pip._internal.req.req_set import RequirementSet
2117
from pip._internal.resolution.base import BaseResolver, InstallRequirementProvider
2218
from pip._internal.resolution.resolvelib.provider import PipProvider
@@ -28,7 +24,7 @@
2824
from pip._internal.utils.filetypes import is_archive_file
2925
from pip._internal.utils.misc import dist_is_editable
3026

31-
from .base import Candidate, Constraint, Requirement
27+
from .base import Candidate, Requirement
3228
from .factory import Factory
3329

3430
if TYPE_CHECKING:
@@ -78,41 +74,13 @@ def __init__(
7874
def resolve(
7975
self, root_reqs: List[InstallRequirement], check_supported_wheels: bool
8076
) -> RequirementSet:
81-
82-
constraints: Dict[str, Constraint] = {}
83-
user_requested: Dict[str, int] = {}
84-
requirements = []
85-
for i, req in enumerate(root_reqs):
86-
if req.constraint:
87-
# Ensure we only accept valid constraints
88-
problem = check_invalid_constraint_type(req)
89-
if problem:
90-
raise InstallationError(problem)
91-
if not req.match_markers():
92-
continue
93-
assert req.name, "Constraint must be named"
94-
name = canonicalize_name(req.name)
95-
if name in constraints:
96-
constraints[name] &= req
97-
else:
98-
constraints[name] = Constraint.from_ireq(req)
99-
else:
100-
if req.user_supplied and req.name:
101-
canonical_name = canonicalize_name(req.name)
102-
if canonical_name not in user_requested:
103-
user_requested[canonical_name] = i
104-
r = self.factory.make_requirement_from_install_req(
105-
req, requested_extras=()
106-
)
107-
if r is not None:
108-
requirements.append(r)
109-
77+
collected = self.factory.collect_root_requirements(root_reqs)
11078
provider = PipProvider(
11179
factory=self.factory,
112-
constraints=constraints,
80+
constraints=collected.constraints,
11381
ignore_dependencies=self.ignore_dependencies,
11482
upgrade_strategy=self.upgrade_strategy,
115-
user_requested=user_requested,
83+
user_requested=collected.user_requested,
11684
)
11785
if "PIP_RESOLVER_DEBUG" in os.environ:
11886
reporter: BaseReporter = PipDebuggingReporter()
@@ -126,13 +94,13 @@ def resolve(
12694
try:
12795
try_to_avoid_resolution_too_deep = 2000000
12896
result = self._result = resolver.resolve(
129-
requirements, max_rounds=try_to_avoid_resolution_too_deep
97+
collected.requirements, max_rounds=try_to_avoid_resolution_too_deep
13098
)
13199

132100
except ResolutionImpossible as e:
133101
error = self.factory.get_installation_error(
134102
cast("ResolutionImpossible[Requirement, Candidate]", e),
135-
constraints,
103+
collected.constraints,
136104
)
137105
raise error from e
138106

tests/functional/test_new_resolver.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,3 +1843,32 @@ def test_new_resolver_modifies_installed_incompatible(script):
18431843
"d==1",
18441844
)
18451845
assert_installed(script, d="1", c="2", b="2", a="2")
1846+
1847+
1848+
def test_new_resolver_transitively_depends_on_unnamed_local(script):
1849+
create_basic_wheel_for_package(script, name="certbot-docs", version="1")
1850+
certbot = create_test_package_with_setup(
1851+
script,
1852+
name="certbot",
1853+
version="99.99.0.dev0",
1854+
extras_require={"docs": ["certbot-docs"]}
1855+
)
1856+
certbot_apache = create_test_package_with_setup(
1857+
script,
1858+
name="certbot-apache",
1859+
version="99.99.0.dev0",
1860+
install_requires=["certbot>=99.99.0.dev0"],
1861+
)
1862+
1863+
script.pip(
1864+
"install",
1865+
"--no-cache-dir", "--no-index",
1866+
"--find-links", script.scratch_path,
1867+
f"{certbot}[docs]", certbot_apache,
1868+
)
1869+
assert_installed(
1870+
script,
1871+
certbot="99.99.0.dev0",
1872+
certbot_apache="99.99.0.dev0",
1873+
certbot_docs="1",
1874+
)

0 commit comments

Comments
 (0)