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:
commit
9f18a403ca
1
news/10479.feature.rst
Normal file
1
news/10479.feature.rst
Normal 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.
|
1
news/resolvelib.vendor.rst
Normal file
1
news/resolvelib.vendor.rst
Normal file
|
@ -0,0 +1 @@
|
|||
Upgrade resolvelib to 0.8.0
|
|
@ -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]]:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@ __all__ = [
|
|||
"ResolutionTooDeep",
|
||||
]
|
||||
|
||||
__version__ = "0.7.1"
|
||||
__version__ = "0.8.0"
|
||||
|
||||
|
||||
from .providers import AbstractProvider, AbstractResolver
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue