Skip to content

Commit 9f18a40

Browse files
authored
Merge pull request #10481 from notatallshaw/prefer_failures
2 parents db496cb + 1e3c127 commit 9f18a40

File tree

9 files changed

+51
-10
lines changed

9 files changed

+51
-10
lines changed

Diff for: news/10479.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
When backtracking during dependency resolution, prefer the dependencies which are involved in the most recent conflict. This can significantly reduce the amount of backtracking required.

Diff for: news/resolvelib.vendor.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Upgrade resolvelib to 0.8.0

Diff for: noxfile.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def vendoring(session: nox.Session) -> None:
174174
session.install("vendoring~=1.0.0")
175175

176176
if "--upgrade" not in session.posargs:
177-
session.run("vendoring", "sync", ".", "-v")
177+
session.run("vendoring", "sync", "-v")
178178
return
179179

180180
def pinned_requirements(path: Path) -> Iterator[Tuple[str, str]]:

Diff for: src/pip/_internal/resolution/resolvelib/provider.py

+19-1
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@ def __init__(
6666
def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
6767
return requirement_or_candidate.name
6868

69-
def get_preference(
69+
def get_preference( # type: ignore
7070
self,
7171
identifier: str,
7272
resolutions: Mapping[str, Candidate],
7373
candidates: Mapping[str, Iterator[Candidate]],
7474
information: Mapping[str, Iterable["PreferenceInformation"]],
75+
backtrack_causes: Sequence["PreferenceInformation"],
7576
) -> "Preference":
7677
"""Produce a sort key for given requirement based on preference.
7778
@@ -132,11 +133,17 @@ def get_preference(
132133
# while we work on "proper" branch pruning techniques.
133134
delay_this = identifier == "setuptools"
134135

136+
# Prefer the causes of backtracking on the assumption that the problem
137+
# resolving the dependency tree is related to the failures that caused
138+
# the backtracking
139+
backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes)
140+
135141
return (
136142
not requires_python,
137143
delay_this,
138144
not direct,
139145
not pinned,
146+
not backtrack_cause,
140147
inferred_depth,
141148
requested_order,
142149
not unfree,
@@ -195,3 +202,14 @@ def is_satisfied_by(self, requirement: Requirement, candidate: Candidate) -> boo
195202
def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]:
196203
with_requires = not self._ignore_dependencies
197204
return [r for r in candidate.iter_dependencies(with_requires) if r is not None]
205+
206+
@staticmethod
207+
def is_backtrack_cause(
208+
identifier: str, backtrack_causes: Sequence["PreferenceInformation"]
209+
) -> bool:
210+
for backtrack_cause in backtrack_causes:
211+
if identifier == backtrack_cause.requirement.name:
212+
return True
213+
if backtrack_cause.parent and identifier == backtrack_cause.parent.name:
214+
return True
215+
return False

Diff for: src/pip/_vendor/resolvelib/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"ResolutionTooDeep",
1212
]
1313

14-
__version__ = "0.7.1"
14+
__version__ = "0.8.0"
1515

1616

1717
from .providers import AbstractProvider, AbstractResolver

Diff for: src/pip/_vendor/resolvelib/providers.py

+10-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ def identify(self, requirement_or_candidate):
99
"""
1010
raise NotImplementedError
1111

12-
def get_preference(self, identifier, resolutions, candidates, information):
12+
def get_preference(
13+
self,
14+
identifier,
15+
resolutions,
16+
candidates,
17+
information,
18+
backtrack_causes,
19+
):
1320
"""Produce a sort key for given requirement based on preference.
1421
1522
The preference is defined as "I think this requirement should be
@@ -25,6 +32,8 @@ def get_preference(self, identifier, resolutions, candidates, information):
2532
Each value is an iterator of candidates.
2633
:param information: Mapping of requirement information of each package.
2734
Each value is an iterator of *requirement information*.
35+
:param backtrack_causes: Sequence of requirement information that were
36+
the requirements that caused the resolver to most recently backtrack.
2837
2938
A *requirement information* instance is a named tuple with two members:
3039

Diff for: src/pip/_vendor/resolvelib/resolvers.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def __init__(self, round_count):
9999

100100

101101
# Resolution state in a round.
102-
State = collections.namedtuple("State", "mapping criteria")
102+
State = collections.namedtuple("State", "mapping criteria backtrack_causes")
103103

104104

105105
class Resolution(object):
@@ -131,6 +131,7 @@ def _push_new_state(self):
131131
state = State(
132132
mapping=base.mapping.copy(),
133133
criteria=base.criteria.copy(),
134+
backtrack_causes=base.backtrack_causes[:],
134135
)
135136
self._states.append(state)
136137

@@ -185,6 +186,7 @@ def _get_preference(self, name):
185186
self.state.criteria,
186187
operator.attrgetter("information"),
187188
),
189+
backtrack_causes=self.state.backtrack_causes,
188190
)
189191

190192
def _is_current_pin_satisfying(self, name, criterion):
@@ -335,7 +337,13 @@ def resolve(self, requirements, max_rounds):
335337
self._r.starting()
336338

337339
# Initialize the root state.
338-
self._states = [State(mapping=collections.OrderedDict(), criteria={})]
340+
self._states = [
341+
State(
342+
mapping=collections.OrderedDict(),
343+
criteria={},
344+
backtrack_causes=[],
345+
)
346+
]
339347
for r in requirements:
340348
try:
341349
self._add_to_criteria(self.state.criteria, r, parent=None)
@@ -369,11 +377,13 @@ def resolve(self, requirements, max_rounds):
369377
# Backtrack if pinning fails. The backtrack process puts us in
370378
# an unpinned state, so we can work on it in the next round.
371379
success = self._backtrack()
380+
self.state.backtrack_causes[:] = [
381+
i for c in failure_causes for i in c.information
382+
]
372383

373384
# Dead ends everywhere. Give up.
374385
if not success:
375-
causes = [i for c in failure_causes for i in c.information]
376-
raise ResolutionImpossible(causes)
386+
raise ResolutionImpossible(self.state.backtrack_causes)
377387
else:
378388
# Pinning was successful. Push a new state to do another pin.
379389
self._push_new_state()

Diff for: src/pip/_vendor/vendor.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ requests==2.26.0
1414
chardet==4.0.0
1515
idna==3.2
1616
urllib3==1.26.7
17-
resolvelib==0.7.1
17+
resolvelib==0.8.0
1818
setuptools==44.0.0
1919
six==1.16.0
2020
tenacity==8.0.1

Diff for: tests/unit/resolution_resolvelib/test_provider.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def build_requirement_information(
1919
install_requirement = install_req_from_req_string(name)
2020
# RequirementInformation is typed as a tuple, but it is a namedtupled.
2121
# https://github.com/sarugaku/resolvelib/blob/7bc025aa2a4e979597c438ad7b17d2e8a08a364e/src/resolvelib/resolvers.pyi#L20-L22
22-
requirement_information: PreferenceInformation = RequirementInformation(
22+
requirement_information: "PreferenceInformation" = RequirementInformation(
2323
requirement=SpecifierRequirement(install_requirement), # type: ignore[call-arg]
2424
parent=parent,
2525
)
@@ -46,6 +46,7 @@ def test_provider_known_depths(factory: Factory) -> None:
4646
resolutions={},
4747
candidates={},
4848
information={root_requirement_name: root_requirement_information},
49+
backtrack_causes=[],
4950
)
5051
assert provider._known_depths == {root_requirement_name: 1.0}
5152

@@ -69,6 +70,7 @@ def test_provider_known_depths(factory: Factory) -> None:
6970
root_requirement_name: root_requirement_information,
7071
transative_requirement_name: transative_package_information,
7172
},
73+
backtrack_causes=[],
7274
)
7375
assert provider._known_depths == {
7476
transative_requirement_name: 2.0,

0 commit comments

Comments
 (0)