1
1
Fork 0
mirror of https://github.com/pypa/pip synced 2023-12-13 21:30:23 +01:00

Merge pull request #10481 from notatallshaw/prefer_failures

This commit is contained in:
Pradyun Gedam 2021-10-10 00:06:30 +01:00 committed by GitHub
commit 9f18a403ca
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 51 additions and 10 deletions

1
news/10479.feature.rst Normal file
View file

@ -0,0 +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.

View file

@ -0,0 +1 @@
Upgrade resolvelib to 0.8.0

View file

@ -174,7 +174,7 @@ def vendoring(session: nox.Session) -> None:
session.install("vendoring~=1.0.0")
if "--upgrade" not in session.posargs:
session.run("vendoring", "sync", ".", "-v")
session.run("vendoring", "sync", "-v")
return
def pinned_requirements(path: Path) -> Iterator[Tuple[str, str]]:

View file

@ -66,12 +66,13 @@ class PipProvider(_ProviderBase):
def identify(self, requirement_or_candidate: Union[Requirement, Candidate]) -> str:
return requirement_or_candidate.name
def get_preference(
def get_preference( # type: ignore
self,
identifier: str,
resolutions: Mapping[str, Candidate],
candidates: Mapping[str, Iterator[Candidate]],
information: Mapping[str, Iterable["PreferenceInformation"]],
backtrack_causes: Sequence["PreferenceInformation"],
) -> "Preference":
"""Produce a sort key for given requirement based on preference.
@ -132,11 +133,17 @@ class PipProvider(_ProviderBase):
# while we work on "proper" branch pruning techniques.
delay_this = identifier == "setuptools"
# Prefer the causes of backtracking on the assumption that the problem
# resolving the dependency tree is related to the failures that caused
# the backtracking
backtrack_cause = self.is_backtrack_cause(identifier, backtrack_causes)
return (
not requires_python,
delay_this,
not direct,
not pinned,
not backtrack_cause,
inferred_depth,
requested_order,
not unfree,
@ -195,3 +202,14 @@ class PipProvider(_ProviderBase):
def get_dependencies(self, candidate: Candidate) -> Sequence[Requirement]:
with_requires = not self._ignore_dependencies
return [r for r in candidate.iter_dependencies(with_requires) if r is not None]
@staticmethod
def is_backtrack_cause(
identifier: str, backtrack_causes: Sequence["PreferenceInformation"]
) -> bool:
for backtrack_cause in backtrack_causes:
if identifier == backtrack_cause.requirement.name:
return True
if backtrack_cause.parent and identifier == backtrack_cause.parent.name:
return True
return False

View file

@ -11,7 +11,7 @@ __all__ = [
"ResolutionTooDeep",
]
__version__ = "0.7.1"
__version__ = "0.8.0"
from .providers import AbstractProvider, AbstractResolver

View file

@ -9,7 +9,14 @@ class AbstractProvider(object):
"""
raise NotImplementedError
def get_preference(self, identifier, resolutions, candidates, information):
def get_preference(
self,
identifier,
resolutions,
candidates,
information,
backtrack_causes,
):
"""Produce a sort key for given requirement based on preference.
The preference is defined as "I think this requirement should be
@ -25,6 +32,8 @@ class AbstractProvider(object):
Each value is an iterator of candidates.
:param information: Mapping of requirement information of each package.
Each value is an iterator of *requirement information*.
:param backtrack_causes: Sequence of requirement information that were
the requirements that caused the resolver to most recently backtrack.
A *requirement information* instance is a named tuple with two members:

View file

@ -99,7 +99,7 @@ class ResolutionTooDeep(ResolutionError):
# Resolution state in a round.
State = collections.namedtuple("State", "mapping criteria")
State = collections.namedtuple("State", "mapping criteria backtrack_causes")
class Resolution(object):
@ -131,6 +131,7 @@ class Resolution(object):
state = State(
mapping=base.mapping.copy(),
criteria=base.criteria.copy(),
backtrack_causes=base.backtrack_causes[:],
)
self._states.append(state)
@ -185,6 +186,7 @@ class Resolution(object):
self.state.criteria,
operator.attrgetter("information"),
),
backtrack_causes=self.state.backtrack_causes,
)
def _is_current_pin_satisfying(self, name, criterion):
@ -335,7 +337,13 @@ class Resolution(object):
self._r.starting()
# Initialize the root state.
self._states = [State(mapping=collections.OrderedDict(), criteria={})]
self._states = [
State(
mapping=collections.OrderedDict(),
criteria={},
backtrack_causes=[],
)
]
for r in requirements:
try:
self._add_to_criteria(self.state.criteria, r, parent=None)
@ -369,11 +377,13 @@ class Resolution(object):
# Backtrack if pinning fails. The backtrack process puts us in
# an unpinned state, so we can work on it in the next round.
success = self._backtrack()
self.state.backtrack_causes[:] = [
i for c in failure_causes for i in c.information
]
# Dead ends everywhere. Give up.
if not success:
causes = [i for c in failure_causes for i in c.information]
raise ResolutionImpossible(causes)
raise ResolutionImpossible(self.state.backtrack_causes)
else:
# Pinning was successful. Push a new state to do another pin.
self._push_new_state()

View file

@ -14,7 +14,7 @@ requests==2.26.0
chardet==4.0.0
idna==3.2
urllib3==1.26.7
resolvelib==0.7.1
resolvelib==0.8.0
setuptools==44.0.0
six==1.16.0
tenacity==8.0.1

View file

@ -19,7 +19,7 @@ def build_requirement_information(
install_requirement = install_req_from_req_string(name)
# RequirementInformation is typed as a tuple, but it is a namedtupled.
# https://github.com/sarugaku/resolvelib/blob/7bc025aa2a4e979597c438ad7b17d2e8a08a364e/src/resolvelib/resolvers.pyi#L20-L22
requirement_information: PreferenceInformation = RequirementInformation(
requirement_information: "PreferenceInformation" = RequirementInformation(
requirement=SpecifierRequirement(install_requirement), # type: ignore[call-arg]
parent=parent,
)
@ -46,6 +46,7 @@ def test_provider_known_depths(factory: Factory) -> None:
resolutions={},
candidates={},
information={root_requirement_name: root_requirement_information},
backtrack_causes=[],
)
assert provider._known_depths == {root_requirement_name: 1.0}
@ -69,6 +70,7 @@ def test_provider_known_depths(factory: Factory) -> None:
root_requirement_name: root_requirement_information,
transative_requirement_name: transative_package_information,
},
backtrack_causes=[],
)
assert provider._known_depths == {
transative_requirement_name: 2.0,