Skip to content

Commit

Permalink
Respect the base's constraint for extra-ed package
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Jul 31, 2021
1 parent 3d7b9c5 commit a2cbacf
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 1 deletion.
3 changes: 3 additions & 0 deletions news/10233.bugfix.rst
@@ -0,0 +1,3 @@
New resolver: When a package is specified with extras in constraints, and with
extras in non-constraint requirements, the resolver now correctly identifies the
constraint's existence and avoids backtracking.
17 changes: 16 additions & 1 deletion src/pip/_internal/resolution/resolvelib/provider.py
Expand Up @@ -143,6 +143,21 @@ def get_preference(
identifier,
)

def _get_constraint(self, identifier: str) -> Constraint:
if identifier in self._constraints:
return self._constraints[identifier]

# HACK: Theoratically we should check whether this identifier is a valid
# "NAME[EXTRAS]" format, and parse out the name part with packaging or
# some regular expression. But since pip's resolver only spits out
# three kinds of identifiers: normalized PEP 503 names, normalized names
# plus extras, and Requires-Python, we can cheat a bit here.
name, open_bracket, _ = identifier.partition("[")
if open_bracket and name in self._constraints:
return self._constraints[name]

return Constraint.empty()

def find_matches(
self,
identifier: str,
Expand All @@ -169,7 +184,7 @@ def _eligible_for_upgrade(name: str) -> bool:
return self._factory.find_candidates(
identifier=identifier,
requirements=requirements,
constraint=self._constraints.get(identifier, Constraint.empty()),
constraint=self._get_constraint(identifier),
prefers_installed=(not _eligible_for_upgrade(identifier)),
incompatibilities=incompatibilities,
)
Expand Down
18 changes: 18 additions & 0 deletions tests/functional/test_new_resolver.py
Expand Up @@ -2009,3 +2009,21 @@ def test_new_resolver_file_url_normalize(script, format_dep, format_input):
format_input(lib_a), lib_b,
)
script.assert_installed(lib_a="1", lib_b="1")


def test_new_resolver_dont_backtrack_on_extra_if_base_constrained(script):
create_basic_wheel_for_package(script, "dep", "1.0")
create_basic_wheel_for_package(script, "pkg", "1.0", extras={"ext": ["dep"]})
create_basic_wheel_for_package(script, "pkg", "2.0", extras={"ext": ["dep"]})
constraints_file = script.scratch_path / "constraints.txt"
constraints_file.write_text("pkg==1.0")

result = script.pip(
"install",
"--no-cache-dir", "--no-index",
"--find-links", script.scratch_path,
"--constraint", constraints_file,
"pkg[ext]",
)
assert "pkg-2.0" not in result.stdout, "Should not try 2.0 due to constraint"
script.assert_installed(pkg="1.0", dep="1.0")

0 comments on commit a2cbacf

Please sign in to comment.