From 46f433615e943f9c54754c50d55d2679010a0ce5 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Tue, 19 May 2020 17:08:12 +0800 Subject: [PATCH] Update vendored ResolveLib to 0.4.0 --- src/pip/_vendor/resolvelib/__init__.py | 2 +- src/pip/_vendor/resolvelib/compat/__init__.py | 0 .../resolvelib/compat/collections_abc.py | 6 ++ src/pip/_vendor/resolvelib/providers.py | 48 +++++--------- src/pip/_vendor/resolvelib/reporters.py | 12 +++- src/pip/_vendor/resolvelib/resolvers.py | 66 +++++++++++-------- src/pip/_vendor/vendor.txt | 2 +- 7 files changed, 75 insertions(+), 61 deletions(-) create mode 100644 src/pip/_vendor/resolvelib/compat/__init__.py create mode 100644 src/pip/_vendor/resolvelib/compat/collections_abc.py diff --git a/src/pip/_vendor/resolvelib/__init__.py b/src/pip/_vendor/resolvelib/__init__.py index aaba5b3a1..3b444545d 100644 --- a/src/pip/_vendor/resolvelib/__init__.py +++ b/src/pip/_vendor/resolvelib/__init__.py @@ -11,7 +11,7 @@ __all__ = [ "ResolutionTooDeep", ] -__version__ = "0.3.0" +__version__ = "0.4.0" from .providers import AbstractProvider, AbstractResolver diff --git a/src/pip/_vendor/resolvelib/compat/__init__.py b/src/pip/_vendor/resolvelib/compat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/pip/_vendor/resolvelib/compat/collections_abc.py b/src/pip/_vendor/resolvelib/compat/collections_abc.py new file mode 100644 index 000000000..366cc5e2e --- /dev/null +++ b/src/pip/_vendor/resolvelib/compat/collections_abc.py @@ -0,0 +1,6 @@ +__all__ = ["Sequence"] + +try: + from collections.abc import Sequence +except ImportError: + from collections import Sequence diff --git a/src/pip/_vendor/resolvelib/providers.py b/src/pip/_vendor/resolvelib/providers.py index db1682195..68b7290df 100644 --- a/src/pip/_vendor/resolvelib/providers.py +++ b/src/pip/_vendor/resolvelib/providers.py @@ -27,7 +27,7 @@ class AbstractProvider(object): * `requirement` specifies a requirement contributing to the current 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. The preference could depend on a various of issues, including (not @@ -48,23 +48,28 @@ class AbstractProvider(object): """ raise NotImplementedError - def find_matches(self, requirement): - """Find all possible candidates that satisfy a requirement. + def find_matches(self, requirements): + """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 returned, and for a "named" requirement, the index(es) should be consulted to find concrete candidates for this requirement. - The returned candidates should be sorted by reversed preference, e.g. - the most preferred should be LAST. This is done so list-popping can be - as efficient as possible. + :param requirements: A collection of requirements which all of the the + returned candidates must match. All requirements are guaranteed to + 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 def is_satisfied_by(self, requirement, 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 viable solution to the requirement. """ @@ -92,30 +97,13 @@ class AbstractResolver(object): def resolve(self, requirements, **kwargs): """Take a collection of constraints, spit out the resolution result. - Parameters - ---------- - requirements : Collection - A collection of constraints - kwargs : optional - Additional keyword arguments that subclasses may accept. + This returns a representation of the final resolution state, with one + guarenteed attribute ``mapping`` that contains resolved candidates as + values. The keys are their respective identifiers. - Raises - ------ - 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. + :param requirements: A collection of constraints. + :param kwargs: Additional keyword arguments that subclasses may accept. - Returns - ------- - 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). + :raises: ``self.base_exception`` or its subclass. """ raise NotImplementedError diff --git a/src/pip/_vendor/resolvelib/reporters.py b/src/pip/_vendor/resolvelib/reporters.py index c7e9e88b8..a0a2a4588 100644 --- a/src/pip/_vendor/resolvelib/reporters.py +++ b/src/pip/_vendor/resolvelib/reporters.py @@ -23,12 +23,18 @@ class BaseReporter(object): """Called before the resolution ends successfully. """ - def adding_requirement(self, requirement): - """Called when the resolver adds a new requirement into the resolve criteria. + def adding_requirement(self, requirement, parent): + """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): - """Called when the resolver rejects a candidate during backtracking. + """Called when rejecting a candidate during backtracking. """ def pinning(self, candidate): diff --git a/src/pip/_vendor/resolvelib/resolvers.py b/src/pip/_vendor/resolvelib/resolvers.py index b51d337d2..4497f976a 100644 --- a/src/pip/_vendor/resolvelib/resolvers.py +++ b/src/pip/_vendor/resolvelib/resolvers.py @@ -1,5 +1,6 @@ import collections +from .compat import collections_abc from .providers import AbstractResolver from .structs import DirectedGraph @@ -68,16 +69,18 @@ class Criterion(object): def __repr__(self): requirements = ", ".join( - "{!r} from {!r}".format(req, parent) + "({!r}, via={!r})".format(req, parent) for req, parent in self.information ) - return "".format(requirements) + return "Criterion({})".format(requirements) @classmethod def from_requirement(cls, provider, requirement, parent): """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( candidates=candidates, information=[RequirementInformation(requirement, parent)], @@ -98,11 +101,9 @@ class Criterion(object): """ infos = list(self.information) infos.append(RequirementInformation(requirement, parent)) - candidates = [ - c - for c in self.candidates - if provider.is_satisfied_by(requirement, c) - ] + candidates = provider.find_matches([r for r, _ in infos]) + if not isinstance(candidates, collections_abc.Sequence): + candidates = list(candidates) criterion = type(self)(candidates, infos, list(self.incompatibilities)) if not candidates: raise RequirementsConflicted(criterion) @@ -179,7 +180,7 @@ class Resolution(object): self._states.append(state) def _merge_into_criterion(self, requirement, parent): - self._r.adding_requirement(requirement) + self._r.adding_requirement(requirement, parent) name = self._p.identify(requirement) try: crit = self.state.criteria[name] @@ -218,13 +219,24 @@ class Resolution(object): def _attempt_to_pin_criterion(self, name, criterion): causes = [] - for candidate in reversed(criterion.candidates): + for candidate in criterion.candidates: try: criteria = self._get_criteria_to_update(candidate) except RequirementsConflicted as e: causes.append(e.criterion) 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 # backtracking looks at this mapping to get the last pin. self._r.pinning(candidate) @@ -232,13 +244,6 @@ class Resolution(object): self.state.mapping[name] = candidate 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 [] # All candidates tried, nothing works. This criterion is a dead @@ -246,23 +251,32 @@ class Resolution(object): return causes def _backtrack(self): - # We need at least 3 states here: - # (a) One known not working, to drop. - # (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] + # Drop the current state, it's known not to work. + del self._states[-1] - # Retract the last candidate pin, and create a new (b). - name, candidate = self._states.pop().mapping.popitem() + # We need at least 2 states here: + # (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) + + # Create a new state to work on, with the newly known not-working + # candidate excluded. self._push_new_state() # Mark the retracted candidate as incompatible. criterion = self.state.criteria[name].excluded_of(candidate) if criterion is None: # This state still does not work. Try the still previous state. + del self._states[-1] continue self.state.criteria[name] = criterion diff --git a/src/pip/_vendor/vendor.txt b/src/pip/_vendor/vendor.txt index 74ecca425..e032f5f73 100644 --- a/src/pip/_vendor/vendor.txt +++ b/src/pip/_vendor/vendor.txt @@ -16,7 +16,7 @@ requests==2.23.0 chardet==3.0.4 idna==2.9 urllib3==1.25.8 -resolvelib==0.3.0 +resolvelib==0.4.0 retrying==1.3.3 setuptools==44.0.0 six==1.14.0