Skip to content

Commit 95fa4e5

Browse files
committed
Respect the base's constraint for extra-ed package
1 parent 0a05d76 commit 95fa4e5

File tree

3 files changed

+37
-1
lines changed

3 files changed

+37
-1
lines changed

news/10233.bugfix.rst

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
New resolver: When a package is specified with extras in constraints, and with
2+
extras in non-constraint requirements, the resolver now correctly identifies the
3+
constraint's existence and avoids backtracking.

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

+16-1
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,21 @@ def get_preference(
143143
identifier,
144144
)
145145

146+
def _get_constraint(self, identifier: str) -> Constraint:
147+
if identifier in self._constraints:
148+
return self._constraints[identifier]
149+
150+
# HACK: Theoratically we should check whether this identifier is a valid
151+
# "NAME[EXTRAS]" format, and parse out the name part with packaging or
152+
# some regular expression. But since pip's resolver only spits out
153+
# three kinds of identifiers: normalized PEP 503 names, normalized names
154+
# plus extras, and Requires-Python, we can cheat a bit here.
155+
name, open_bracket, _ = identifier.partition("[")
156+
if open_bracket and name in self._constraints:
157+
return self._constraints[name]
158+
159+
return Constraint.empty()
160+
146161
def find_matches(
147162
self,
148163
identifier: str,
@@ -169,7 +184,7 @@ def _eligible_for_upgrade(name: str) -> bool:
169184
return self._factory.find_candidates(
170185
identifier=identifier,
171186
requirements=requirements,
172-
constraint=self._constraints.get(identifier, Constraint.empty()),
187+
constraint=self._get_constraint(identifier),
173188
prefers_installed=(not _eligible_for_upgrade(identifier)),
174189
incompatibilities=incompatibilities,
175190
)

tests/functional/test_new_resolver.py

+18
Original file line numberDiff line numberDiff line change
@@ -2009,3 +2009,21 @@ def test_new_resolver_file_url_normalize(script, format_dep, format_input):
20092009
format_input(lib_a), lib_b,
20102010
)
20112011
script.assert_installed(lib_a="1", lib_b="1")
2012+
2013+
2014+
def test_new_resolver_dont_backtrack_on_extra_if_base_constrained(script):
2015+
create_basic_wheel_for_package(script, "dep", "1.0")
2016+
create_basic_wheel_for_package(script, "pkg", "1.0", extras={"ext": ["dep"]})
2017+
create_basic_wheel_for_package(script, "pkg", "2.0", extras={"ext": ["dep"]})
2018+
constraints_file = script.scratch_path / "constraints.txt"
2019+
constraints_file.write_text("pkg==1.0")
2020+
2021+
result = script.pip(
2022+
"install",
2023+
"--no-cache-dir", "--no-index",
2024+
"--find-links", script.scratch_path,
2025+
"--constraint", constraints_file,
2026+
"pkg[ext]",
2027+
)
2028+
assert "pkg-2.0" not in result.stdout, "Should not try 2.0 due to constraint"
2029+
script.assert_installed(pkg="1.0", dep="1.0")

0 commit comments

Comments
 (0)