mirror of https://github.com/pypa/pip
Update vendored ResolveLib to 0.4.0
This commit is contained in:
parent
be48ec0d15
commit
46f433615e
|
@ -11,7 +11,7 @@ __all__ = [
|
||||||
"ResolutionTooDeep",
|
"ResolutionTooDeep",
|
||||||
]
|
]
|
||||||
|
|
||||||
__version__ = "0.3.0"
|
__version__ = "0.4.0"
|
||||||
|
|
||||||
|
|
||||||
from .providers import AbstractProvider, AbstractResolver
|
from .providers import AbstractProvider, AbstractResolver
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
__all__ = ["Sequence"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
from collections.abc import Sequence
|
||||||
|
except ImportError:
|
||||||
|
from collections import Sequence
|
|
@ -27,7 +27,7 @@ class AbstractProvider(object):
|
||||||
|
|
||||||
* `requirement` specifies a requirement contributing to the current
|
* `requirement` specifies a requirement contributing to the current
|
||||||
candidate list
|
candidate list
|
||||||
* `parent` specifies the candidate that provids (dependend on) the
|
* `parent` specifies the candidate that provides (dependend on) the
|
||||||
requirement, or `None` to indicate a root requirement.
|
requirement, or `None` to indicate a root requirement.
|
||||||
|
|
||||||
The preference could depend on a various of issues, including (not
|
The preference could depend on a various of issues, including (not
|
||||||
|
@ -48,23 +48,28 @@ class AbstractProvider(object):
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def find_matches(self, requirement):
|
def find_matches(self, requirements):
|
||||||
"""Find all possible candidates that satisfy a requirement.
|
"""Find all possible candidates that satisfy the given requirements.
|
||||||
|
|
||||||
This should try to get candidates based on the requirement's type.
|
This should try to get candidates based on the requirements' types.
|
||||||
For VCS, local, and archive requirements, the one-and-only match is
|
For VCS, local, and archive requirements, the one-and-only match is
|
||||||
returned, and for a "named" requirement, the index(es) should be
|
returned, and for a "named" requirement, the index(es) should be
|
||||||
consulted to find concrete candidates for this requirement.
|
consulted to find concrete candidates for this requirement.
|
||||||
|
|
||||||
The returned candidates should be sorted by reversed preference, e.g.
|
:param requirements: A collection of requirements which all of the the
|
||||||
the most preferred should be LAST. This is done so list-popping can be
|
returned candidates must match. All requirements are guaranteed to
|
||||||
as efficient as possible.
|
have the same identifier. The collection is never empty.
|
||||||
|
:returns: An iterable that orders candidates by preference, e.g. the
|
||||||
|
most preferred candidate should come first.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def is_satisfied_by(self, requirement, candidate):
|
def is_satisfied_by(self, requirement, candidate):
|
||||||
"""Whether the given requirement can be satisfied by a candidate.
|
"""Whether the given requirement can be satisfied by a candidate.
|
||||||
|
|
||||||
|
The candidate is guarenteed to have been generated from the
|
||||||
|
requirement.
|
||||||
|
|
||||||
A boolean should be returned to indicate whether `candidate` is a
|
A boolean should be returned to indicate whether `candidate` is a
|
||||||
viable solution to the requirement.
|
viable solution to the requirement.
|
||||||
"""
|
"""
|
||||||
|
@ -92,30 +97,13 @@ class AbstractResolver(object):
|
||||||
def resolve(self, requirements, **kwargs):
|
def resolve(self, requirements, **kwargs):
|
||||||
"""Take a collection of constraints, spit out the resolution result.
|
"""Take a collection of constraints, spit out the resolution result.
|
||||||
|
|
||||||
Parameters
|
This returns a representation of the final resolution state, with one
|
||||||
----------
|
guarenteed attribute ``mapping`` that contains resolved candidates as
|
||||||
requirements : Collection
|
values. The keys are their respective identifiers.
|
||||||
A collection of constraints
|
|
||||||
kwargs : optional
|
|
||||||
Additional keyword arguments that subclasses may accept.
|
|
||||||
|
|
||||||
Raises
|
:param requirements: A collection of constraints.
|
||||||
------
|
:param kwargs: Additional keyword arguments that subclasses may accept.
|
||||||
self.base_exception
|
|
||||||
Any raised exception is guaranteed to be a subclass of
|
|
||||||
self.base_exception. The string representation of an exception
|
|
||||||
should be human readable and provide context for why it occurred.
|
|
||||||
|
|
||||||
Returns
|
:raises: ``self.base_exception`` or its subclass.
|
||||||
-------
|
|
||||||
retval : object
|
|
||||||
A representation of the final resolution state. It can be any object
|
|
||||||
with a `mapping` attribute that is a Mapping. Other attributes can
|
|
||||||
be used to provide resolver-specific information.
|
|
||||||
|
|
||||||
The `mapping` attribute MUST be key-value pair is an identifier of a
|
|
||||||
requirement (as returned by the provider's `identify` method) mapped
|
|
||||||
to the resolved candidate (chosen from the return value of the
|
|
||||||
provider's `find_matches` method).
|
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
|
@ -23,12 +23,18 @@ class BaseReporter(object):
|
||||||
"""Called before the resolution ends successfully.
|
"""Called before the resolution ends successfully.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def adding_requirement(self, requirement):
|
def adding_requirement(self, requirement, parent):
|
||||||
"""Called when the resolver adds a new requirement into the resolve criteria.
|
"""Called when adding a new requirement into the resolve criteria.
|
||||||
|
|
||||||
|
:param requirement: The additional requirement to be applied to filter
|
||||||
|
the available candidaites.
|
||||||
|
:param parent: The candidate that requires ``requirement`` as a
|
||||||
|
dependency, or None if ``requirement`` is one of the root
|
||||||
|
requirements passed in from ``Resolver.resolve()``.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def backtracking(self, candidate):
|
def backtracking(self, candidate):
|
||||||
"""Called when the resolver rejects a candidate during backtracking.
|
"""Called when rejecting a candidate during backtracking.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def pinning(self, candidate):
|
def pinning(self, candidate):
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
from .compat import collections_abc
|
||||||
from .providers import AbstractResolver
|
from .providers import AbstractResolver
|
||||||
from .structs import DirectedGraph
|
from .structs import DirectedGraph
|
||||||
|
|
||||||
|
@ -68,16 +69,18 @@ class Criterion(object):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
requirements = ", ".join(
|
requirements = ", ".join(
|
||||||
"{!r} from {!r}".format(req, parent)
|
"({!r}, via={!r})".format(req, parent)
|
||||||
for req, parent in self.information
|
for req, parent in self.information
|
||||||
)
|
)
|
||||||
return "<Criterion {}>".format(requirements)
|
return "Criterion({})".format(requirements)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_requirement(cls, provider, requirement, parent):
|
def from_requirement(cls, provider, requirement, parent):
|
||||||
"""Build an instance from a requirement.
|
"""Build an instance from a requirement.
|
||||||
"""
|
"""
|
||||||
candidates = provider.find_matches(requirement)
|
candidates = provider.find_matches([requirement])
|
||||||
|
if not isinstance(candidates, collections_abc.Sequence):
|
||||||
|
candidates = list(candidates)
|
||||||
criterion = cls(
|
criterion = cls(
|
||||||
candidates=candidates,
|
candidates=candidates,
|
||||||
information=[RequirementInformation(requirement, parent)],
|
information=[RequirementInformation(requirement, parent)],
|
||||||
|
@ -98,11 +101,9 @@ class Criterion(object):
|
||||||
"""
|
"""
|
||||||
infos = list(self.information)
|
infos = list(self.information)
|
||||||
infos.append(RequirementInformation(requirement, parent))
|
infos.append(RequirementInformation(requirement, parent))
|
||||||
candidates = [
|
candidates = provider.find_matches([r for r, _ in infos])
|
||||||
c
|
if not isinstance(candidates, collections_abc.Sequence):
|
||||||
for c in self.candidates
|
candidates = list(candidates)
|
||||||
if provider.is_satisfied_by(requirement, c)
|
|
||||||
]
|
|
||||||
criterion = type(self)(candidates, infos, list(self.incompatibilities))
|
criterion = type(self)(candidates, infos, list(self.incompatibilities))
|
||||||
if not candidates:
|
if not candidates:
|
||||||
raise RequirementsConflicted(criterion)
|
raise RequirementsConflicted(criterion)
|
||||||
|
@ -179,7 +180,7 @@ class Resolution(object):
|
||||||
self._states.append(state)
|
self._states.append(state)
|
||||||
|
|
||||||
def _merge_into_criterion(self, requirement, parent):
|
def _merge_into_criterion(self, requirement, parent):
|
||||||
self._r.adding_requirement(requirement)
|
self._r.adding_requirement(requirement, parent)
|
||||||
name = self._p.identify(requirement)
|
name = self._p.identify(requirement)
|
||||||
try:
|
try:
|
||||||
crit = self.state.criteria[name]
|
crit = self.state.criteria[name]
|
||||||
|
@ -218,13 +219,24 @@ class Resolution(object):
|
||||||
|
|
||||||
def _attempt_to_pin_criterion(self, name, criterion):
|
def _attempt_to_pin_criterion(self, name, criterion):
|
||||||
causes = []
|
causes = []
|
||||||
for candidate in reversed(criterion.candidates):
|
for candidate in criterion.candidates:
|
||||||
try:
|
try:
|
||||||
criteria = self._get_criteria_to_update(candidate)
|
criteria = self._get_criteria_to_update(candidate)
|
||||||
except RequirementsConflicted as e:
|
except RequirementsConflicted as e:
|
||||||
causes.append(e.criterion)
|
causes.append(e.criterion)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Check the newly-pinned candidate actually works. This should
|
||||||
|
# always pass under normal circumstances, but in the case of a
|
||||||
|
# faulty provider, we will raise an error to notify the implementer
|
||||||
|
# to fix find_matches() and/or is_satisfied_by().
|
||||||
|
satisfied = all(
|
||||||
|
self._p.is_satisfied_by(r, candidate)
|
||||||
|
for r in criterion.iter_requirement()
|
||||||
|
)
|
||||||
|
if not satisfied:
|
||||||
|
raise InconsistentCandidate(candidate, criterion)
|
||||||
|
|
||||||
# Put newly-pinned candidate at the end. This is essential because
|
# Put newly-pinned candidate at the end. This is essential because
|
||||||
# backtracking looks at this mapping to get the last pin.
|
# backtracking looks at this mapping to get the last pin.
|
||||||
self._r.pinning(candidate)
|
self._r.pinning(candidate)
|
||||||
|
@ -232,13 +244,6 @@ class Resolution(object):
|
||||||
self.state.mapping[name] = candidate
|
self.state.mapping[name] = candidate
|
||||||
self.state.criteria.update(criteria)
|
self.state.criteria.update(criteria)
|
||||||
|
|
||||||
# Check the newly-pinned candidate actually works. This should
|
|
||||||
# always pass under normal circumstances, but in the case of a
|
|
||||||
# faulty provider, we will raise an error to notify the implementer
|
|
||||||
# to fix find_matches() and/or is_satisfied_by().
|
|
||||||
if not self._is_current_pin_satisfying(name, criterion):
|
|
||||||
raise InconsistentCandidate(candidate, criterion)
|
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# All candidates tried, nothing works. This criterion is a dead
|
# All candidates tried, nothing works. This criterion is a dead
|
||||||
|
@ -246,23 +251,32 @@ class Resolution(object):
|
||||||
return causes
|
return causes
|
||||||
|
|
||||||
def _backtrack(self):
|
def _backtrack(self):
|
||||||
# We need at least 3 states here:
|
# Drop the current state, it's known not to work.
|
||||||
# (a) One known not working, to drop.
|
del self._states[-1]
|
||||||
# (b) One to backtrack to.
|
|
||||||
# (c) One to restore state (b) to its state prior to candidate-pinning,
|
|
||||||
# so we can pin another one instead.
|
|
||||||
while len(self._states) >= 3:
|
|
||||||
del self._states[-1]
|
|
||||||
|
|
||||||
# Retract the last candidate pin, and create a new (b).
|
# We need at least 2 states here:
|
||||||
name, candidate = self._states.pop().mapping.popitem()
|
# (a) One to backtrack to.
|
||||||
|
# (b) One to restore state (a) to its state prior to candidate-pinning,
|
||||||
|
# so we can pin another one instead.
|
||||||
|
|
||||||
|
while len(self._states) >= 2:
|
||||||
|
# Retract the last candidate pin.
|
||||||
|
prev_state = self._states.pop()
|
||||||
|
try:
|
||||||
|
name, candidate = prev_state.mapping.popitem()
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
self._r.backtracking(candidate)
|
self._r.backtracking(candidate)
|
||||||
|
|
||||||
|
# Create a new state to work on, with the newly known not-working
|
||||||
|
# candidate excluded.
|
||||||
self._push_new_state()
|
self._push_new_state()
|
||||||
|
|
||||||
# Mark the retracted candidate as incompatible.
|
# Mark the retracted candidate as incompatible.
|
||||||
criterion = self.state.criteria[name].excluded_of(candidate)
|
criterion = self.state.criteria[name].excluded_of(candidate)
|
||||||
if criterion is None:
|
if criterion is None:
|
||||||
# This state still does not work. Try the still previous state.
|
# This state still does not work. Try the still previous state.
|
||||||
|
del self._states[-1]
|
||||||
continue
|
continue
|
||||||
self.state.criteria[name] = criterion
|
self.state.criteria[name] = criterion
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ requests==2.23.0
|
||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
idna==2.9
|
idna==2.9
|
||||||
urllib3==1.25.8
|
urllib3==1.25.8
|
||||||
resolvelib==0.3.0
|
resolvelib==0.4.0
|
||||||
retrying==1.3.3
|
retrying==1.3.3
|
||||||
setuptools==44.0.0
|
setuptools==44.0.0
|
||||||
six==1.14.0
|
six==1.14.0
|
||||||
|
|
Loading…
Reference in New Issue