From 775f9ff6ca75173d8b5c0cb2260c54ed1c1f60e6 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 20:36:51 +0530 Subject: [PATCH 1/6] Add a helper for making ExplicitRequirement objects --- src/pip/_internal/resolution/resolvelib/factory.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index c59944d7ea5..7e0748c42e6 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -180,12 +180,15 @@ def make_requirement_from_install_req(self, ireq): # TODO: Get name and version from ireq, if possible? # Specifically, this might be needed in "name @ URL" # syntax - need to check where that syntax is handled. - cand = self._make_candidate_from_link( + candidate = self._make_candidate_from_link( ireq.link, extras=set(ireq.extras), parent=ireq, ) - return ExplicitRequirement(cand) + return self.make_requirement_from_candidate(candidate) return SpecifierRequirement(ireq, factory=self) + def make_requirement_from_candidate(self, candidate): + return ExplicitRequirement(candidate) + def make_requirement_from_spec(self, specifier, comes_from): # type: (str, InstallRequirement) -> Requirement ireq = self._make_install_req_from_spec(specifier, comes_from) From 95347df102a692ee31346bc97ca4cb71bf48cc7b Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 20:37:59 +0530 Subject: [PATCH 2/6] Directly require BaseCandidate in ExtrasCandidate --- src/pip/_internal/resolution/resolvelib/candidates.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 2937c29424f..b13415d5ebf 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -425,11 +425,7 @@ def iter_dependencies(self): yield requirement # Add a dependency on the exact base. - # (See note 2b in the class docstring) - # FIXME: This does not work if the base candidate is specified by - # link, e.g. "pip install .[dev]" will fail. - spec = "{}=={}".format(self.base.name, self.base.version) - yield factory.make_requirement_from_spec(spec, self.base._ireq) + yield factory.make_requirement_from_candidate(self.base) def get_install_requirement(self): # type: () -> Optional[InstallRequirement] From 9506a281147a5f07c8eba4d6767e1f8bae05b828 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 21:08:01 +0530 Subject: [PATCH 3/6] ExtrasCandidate depends on exact base, before optional dependencies --- src/pip/_internal/resolution/resolvelib/candidates.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index b13415d5ebf..8d32c590a9a 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -417,6 +417,9 @@ def iter_dependencies(self): extra ) + # Add a dependency on the exact base. + yield factory.make_requirement_from_candidate(self.base) + for r in self.base.dist.requires(valid_extras): requirement = factory.make_requirement_from_spec_matching_extras( str(r), self.base._ireq, valid_extras, @@ -424,9 +427,6 @@ def iter_dependencies(self): if requirement: yield requirement - # Add a dependency on the exact base. - yield factory.make_requirement_from_candidate(self.base) - def get_install_requirement(self): # type: () -> Optional[InstallRequirement] # We don't return anything here, because we always From 901898c84f83bf767102b49683bd51d32650d8eb Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 21:18:44 +0530 Subject: [PATCH 4/6] Make mypy happy --- src/pip/_internal/resolution/resolvelib/factory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pip/_internal/resolution/resolvelib/factory.py b/src/pip/_internal/resolution/resolvelib/factory.py index 7e0748c42e6..046119cfe45 100644 --- a/src/pip/_internal/resolution/resolvelib/factory.py +++ b/src/pip/_internal/resolution/resolvelib/factory.py @@ -187,6 +187,7 @@ def make_requirement_from_install_req(self, ireq): return SpecifierRequirement(ireq, factory=self) def make_requirement_from_candidate(self, candidate): + # type: (Candidate) -> ExplicitRequirement return ExplicitRequirement(candidate) def make_requirement_from_spec(self, specifier, comes_from): From 24e4cf71186750d152ce0d8438653e525079f4b2 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 21:25:19 +0530 Subject: [PATCH 5/6] Update comments and documentation --- src/pip/_internal/resolution/resolvelib/candidates.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index 8d32c590a9a..da11c4fe785 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -347,8 +347,8 @@ class ExtrasCandidate(Candidate): to treat it as a separate node in the dependency graph. 2. When we're getting the candidate's dependencies, a) We specify that we want the extra dependencies as well. - b) We add a dependency on the base candidate (matching the name and - version). See below for why this is needed. + b) We add a dependency on the base candidate. + See below for why this is needed. 3. We return None for the underlying InstallRequirement, as the base candidate will provide it, and we don't want to end up with duplicates. @@ -417,7 +417,8 @@ def iter_dependencies(self): extra ) - # Add a dependency on the exact base. + # Add a dependency on the exact base + # (See note 2b in the class docstring) yield factory.make_requirement_from_candidate(self.base) for r in self.base.dist.requires(valid_extras): From 50c9ea2fe0ea69990a4a01ab6d9d409555a16077 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Thu, 21 May 2020 21:28:59 +0530 Subject: [PATCH 6/6] This works now! --- tests/functional/test_new_resolver.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/functional/test_new_resolver.py b/tests/functional/test_new_resolver.py index b0da43279e6..693ede21a78 100644 --- a/tests/functional/test_new_resolver.py +++ b/tests/functional/test_new_resolver.py @@ -825,9 +825,7 @@ def _wheel_from_index(script, name, version, requires, extras): @pytest.mark.parametrize( "pkg_builder", [ - pytest.param( - _local_with_setup, marks=pytest.mark.xfail(strict=True), - ), + _local_with_setup, _direct_wheel, _wheel_from_index, ],