mirror of
https://github.com/pypa/pip
synced 2023-12-13 21:30:23 +01:00
Upgrade resolvelib to 0.5.2
This commit is contained in:
parent
e76b1ddeaa
commit
910b304a97
7 changed files with 139 additions and 71 deletions
1
news/resolvelib.vendor.rst
Normal file
1
news/resolvelib.vendor.rst
Normal file
|
@ -0,0 +1 @@
|
|||
Upgrade resolvelib to 0.5.2
|
|
@ -11,7 +11,7 @@ __all__ = [
|
|||
"ResolutionTooDeep",
|
||||
]
|
||||
|
||||
__version__ = "0.4.0"
|
||||
__version__ = "0.5.2"
|
||||
|
||||
|
||||
from .providers import AbstractProvider, AbstractResolver
|
||||
|
|
|
@ -1,32 +1,36 @@
|
|||
class AbstractProvider(object):
|
||||
"""Delegate class to provide requirement interface for the resolver.
|
||||
"""
|
||||
"""Delegate class to provide requirement interface for the resolver."""
|
||||
|
||||
def identify(self, dependency):
|
||||
"""Given a dependency, return an identifier for it.
|
||||
def identify(self, requirement_or_candidate):
|
||||
"""Given a requirement or candidate, return an identifier for it.
|
||||
|
||||
This is used in many places to identify the dependency, e.g. whether
|
||||
two requirements should have their specifier parts merged, whether
|
||||
two specifications would conflict with each other (because they the
|
||||
same name but different versions).
|
||||
This is used in many places to identify a requirement or candidate,
|
||||
e.g. whether two requirements should have their specifier parts merged,
|
||||
whether two candidates would conflict with each other (because they
|
||||
have same name but different versions).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_preference(self, resolution, candidates, information):
|
||||
"""Produce a sort key for given specification based on preference.
|
||||
"""Produce a sort key for given requirement based on preference.
|
||||
|
||||
The preference is defined as "I think this requirement should be
|
||||
resolved first". The lower the return value is, the more preferred
|
||||
this group of arguments is.
|
||||
|
||||
:param resolution: Currently pinned candidate, or `None`.
|
||||
:param candidates: A list of possible candidates.
|
||||
:param candidates: An iterable of possible candidates.
|
||||
:param information: A list of requirement information.
|
||||
|
||||
Each information instance is a named tuple with two entries:
|
||||
The `candidates` iterable's exact type depends on the return type of
|
||||
`find_matches()`. A sequence is passed-in as-is if possible. If it
|
||||
returns a callble, the iterator returned by that callable is passed
|
||||
in here.
|
||||
|
||||
Each element in `information` is a named tuple with two entries:
|
||||
|
||||
* `requirement` specifies a requirement contributing to the current
|
||||
candidate list
|
||||
candidate list.
|
||||
* `parent` specifies the candidate that provides (dependend on) the
|
||||
requirement, or `None` to indicate a root requirement.
|
||||
|
||||
|
@ -43,7 +47,7 @@ class AbstractProvider(object):
|
|||
|
||||
A sortable value should be returned (this will be used as the `key`
|
||||
parameter of the built-in sorting function). The smaller the value is,
|
||||
the more preferred this specification is (i.e. the sorting function
|
||||
the more preferred this requirement is (i.e. the sorting function
|
||||
is called with `reverse=False`).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
@ -56,11 +60,18 @@ class AbstractProvider(object):
|
|||
returned, and for a "named" requirement, the index(es) should be
|
||||
consulted to find concrete candidates for this requirement.
|
||||
|
||||
:param requirements: A collection of requirements which all of the the
|
||||
The return value should produce candidates ordered by preference; the
|
||||
most preferred candidate should come first. The return type may be one
|
||||
of the following:
|
||||
|
||||
* A callable that returns an iterator that yields candidates.
|
||||
* An collection of candidates.
|
||||
* An iterable of candidates. This will be consumed immediately into a
|
||||
list of candidates.
|
||||
|
||||
:param requirements: A collection of requirements which all of 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
|
||||
|
||||
|
@ -85,8 +96,7 @@ class AbstractProvider(object):
|
|||
|
||||
|
||||
class AbstractResolver(object):
|
||||
"""The thing that performs the actual resolution work.
|
||||
"""
|
||||
"""The thing that performs the actual resolution work."""
|
||||
|
||||
base_exception = Exception
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
class BaseReporter(object):
|
||||
"""Delegate class to provider progress reporting for the resolver.
|
||||
"""
|
||||
"""Delegate class to provider progress reporting for the resolver."""
|
||||
|
||||
def starting(self):
|
||||
"""Called before the resolution actually starts.
|
||||
"""
|
||||
"""Called before the resolution actually starts."""
|
||||
|
||||
def starting_round(self, index):
|
||||
"""Called before each round of resolution starts.
|
||||
|
@ -20,8 +18,7 @@ class BaseReporter(object):
|
|||
"""
|
||||
|
||||
def ending(self, state):
|
||||
"""Called before the resolution ends successfully.
|
||||
"""
|
||||
"""Called before the resolution ends successfully."""
|
||||
|
||||
def adding_requirement(self, requirement, parent):
|
||||
"""Called when adding a new requirement into the resolve criteria.
|
||||
|
@ -34,9 +31,7 @@ class BaseReporter(object):
|
|||
"""
|
||||
|
||||
def backtracking(self, candidate):
|
||||
"""Called when rejecting a candidate during backtracking.
|
||||
"""
|
||||
"""Called when rejecting a candidate during backtracking."""
|
||||
|
||||
def pinning(self, candidate):
|
||||
"""Called when adding a candidate to the potential solution.
|
||||
"""
|
||||
"""Called when adding a candidate to the potential solution."""
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import collections
|
||||
|
||||
from .compat import collections_abc
|
||||
from .providers import AbstractResolver
|
||||
from .structs import DirectedGraph
|
||||
from .structs import DirectedGraph, build_iter_view
|
||||
|
||||
|
||||
RequirementInformation = collections.namedtuple(
|
||||
|
@ -76,17 +75,11 @@ class Criterion(object):
|
|||
|
||||
@classmethod
|
||||
def from_requirement(cls, provider, requirement, parent):
|
||||
"""Build an instance from a requirement.
|
||||
"""
|
||||
candidates = provider.find_matches([requirement])
|
||||
if not isinstance(candidates, collections_abc.Sequence):
|
||||
candidates = list(candidates)
|
||||
criterion = cls(
|
||||
candidates=candidates,
|
||||
information=[RequirementInformation(requirement, parent)],
|
||||
incompatibilities=[],
|
||||
)
|
||||
if not candidates:
|
||||
"""Build an instance from a requirement."""
|
||||
cands = build_iter_view(provider.find_matches([requirement]))
|
||||
infos = [RequirementInformation(requirement, parent)]
|
||||
criterion = cls(cands, infos, incompatibilities=[])
|
||||
if not cands:
|
||||
raise RequirementsConflicted(criterion)
|
||||
return criterion
|
||||
|
||||
|
@ -97,15 +90,12 @@ class Criterion(object):
|
|||
return (i.parent for i in self.information)
|
||||
|
||||
def merged_with(self, provider, requirement, parent):
|
||||
"""Build a new instance from this and a new requirement.
|
||||
"""
|
||||
"""Build a new instance from this and a new requirement."""
|
||||
infos = list(self.information)
|
||||
infos.append(RequirementInformation(requirement, parent))
|
||||
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:
|
||||
cands = build_iter_view(provider.find_matches([r for r, _ in infos]))
|
||||
criterion = type(self)(cands, infos, list(self.incompatibilities))
|
||||
if not cands:
|
||||
raise RequirementsConflicted(criterion)
|
||||
return criterion
|
||||
|
||||
|
@ -114,13 +104,12 @@ class Criterion(object):
|
|||
|
||||
Returns the new instance, or None if we still have no valid candidates.
|
||||
"""
|
||||
cands = self.candidates.excluding(candidate)
|
||||
if not cands:
|
||||
return None
|
||||
incompats = list(self.incompatibilities)
|
||||
incompats.append(candidate)
|
||||
candidates = [c for c in self.candidates if c != candidate]
|
||||
if not candidates:
|
||||
return None
|
||||
criterion = type(self)(candidates, list(self.information), incompats)
|
||||
return criterion
|
||||
return type(self)(cands, list(self.information), incompats)
|
||||
|
||||
|
||||
class ResolutionError(ResolverException):
|
||||
|
@ -175,7 +164,8 @@ class Resolution(object):
|
|||
state = State(mapping=collections.OrderedDict(), criteria={})
|
||||
else:
|
||||
state = State(
|
||||
mapping=base.mapping.copy(), criteria=base.criteria.copy(),
|
||||
mapping=base.mapping.copy(),
|
||||
criteria=base.criteria.copy(),
|
||||
)
|
||||
self._states.append(state)
|
||||
|
||||
|
@ -192,12 +182,10 @@ class Resolution(object):
|
|||
|
||||
def _get_criterion_item_preference(self, item):
|
||||
name, criterion = item
|
||||
try:
|
||||
pinned = self.state.mapping[name]
|
||||
except KeyError:
|
||||
pinned = None
|
||||
return self._p.get_preference(
|
||||
pinned, criterion.candidates, criterion.information,
|
||||
self.state.mapping.get(name),
|
||||
criterion.candidates.for_preference(),
|
||||
criterion.information,
|
||||
)
|
||||
|
||||
def _is_current_pin_satisfying(self, name, criterion):
|
||||
|
@ -390,8 +378,7 @@ def _build_result(state):
|
|||
|
||||
|
||||
class Resolver(AbstractResolver):
|
||||
"""The thing that performs the actual resolution work.
|
||||
"""
|
||||
"""The thing that performs the actual resolution work."""
|
||||
|
||||
base_exception = ResolverException
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from .compat import collections_abc
|
||||
|
||||
|
||||
class DirectedGraph(object):
|
||||
"""A graph structure with directed edges.
|
||||
"""
|
||||
"""A graph structure with directed edges."""
|
||||
|
||||
def __init__(self):
|
||||
self._vertices = set()
|
||||
|
@ -17,8 +19,7 @@ class DirectedGraph(object):
|
|||
return key in self._vertices
|
||||
|
||||
def copy(self):
|
||||
"""Return a shallow copy of this graph.
|
||||
"""
|
||||
"""Return a shallow copy of this graph."""
|
||||
other = DirectedGraph()
|
||||
other._vertices = set(self._vertices)
|
||||
other._forwards = {k: set(v) for k, v in self._forwards.items()}
|
||||
|
@ -26,8 +27,7 @@ class DirectedGraph(object):
|
|||
return other
|
||||
|
||||
def add(self, key):
|
||||
"""Add a new vertex to the graph.
|
||||
"""
|
||||
"""Add a new vertex to the graph."""
|
||||
if key in self._vertices:
|
||||
raise ValueError("vertex exists")
|
||||
self._vertices.add(key)
|
||||
|
@ -35,8 +35,7 @@ class DirectedGraph(object):
|
|||
self._backwards[key] = set()
|
||||
|
||||
def remove(self, key):
|
||||
"""Remove a vertex from the graph, disconnecting all edges from/to it.
|
||||
"""
|
||||
"""Remove a vertex from the graph, disconnecting all edges from/to it."""
|
||||
self._vertices.remove(key)
|
||||
for f in self._forwards.pop(key):
|
||||
self._backwards[f].remove(key)
|
||||
|
@ -66,3 +65,79 @@ class DirectedGraph(object):
|
|||
|
||||
def iter_parents(self, key):
|
||||
return iter(self._backwards[key])
|
||||
|
||||
|
||||
class _FactoryIterableView(object):
|
||||
"""Wrap an iterator factory returned by `find_matches()`.
|
||||
|
||||
Calling `iter()` on this class would invoke the underlying iterator
|
||||
factory, making it a "collection with ordering" that can be iterated
|
||||
through multiple times, but lacks random access methods presented in
|
||||
built-in Python sequence types.
|
||||
"""
|
||||
|
||||
def __init__(self, factory):
|
||||
self._factory = factory
|
||||
|
||||
def __bool__(self):
|
||||
try:
|
||||
next(self._factory())
|
||||
except StopIteration:
|
||||
return False
|
||||
return True
|
||||
|
||||
__nonzero__ = __bool__ # XXX: Python 2.
|
||||
|
||||
def __iter__(self):
|
||||
return self._factory()
|
||||
|
||||
def for_preference(self):
|
||||
"""Provide an candidate iterable for `get_preference()`"""
|
||||
return self._factory()
|
||||
|
||||
def excluding(self, candidate):
|
||||
"""Create a new `Candidates` instance excluding `candidate`."""
|
||||
|
||||
def factory():
|
||||
return (c for c in self._factory() if c != candidate)
|
||||
|
||||
return type(self)(factory)
|
||||
|
||||
|
||||
class _SequenceIterableView(object):
|
||||
"""Wrap an iterable returned by find_matches().
|
||||
|
||||
This is essentially just a proxy to the underlying sequence that provides
|
||||
the same interface as `_FactoryIterableView`.
|
||||
"""
|
||||
|
||||
def __init__(self, sequence):
|
||||
self._sequence = sequence
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._sequence)
|
||||
|
||||
__nonzero__ = __bool__ # XXX: Python 2.
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._sequence)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._sequence)
|
||||
|
||||
def for_preference(self):
|
||||
"""Provide an candidate iterable for `get_preference()`"""
|
||||
return self._sequence
|
||||
|
||||
def excluding(self, candidate):
|
||||
"""Create a new instance excluding `candidate`."""
|
||||
return type(self)([c for c in self._sequence if c != candidate])
|
||||
|
||||
|
||||
def build_iter_view(matches):
|
||||
"""Build an iterable view from the value returned by `find_matches()`."""
|
||||
if callable(matches):
|
||||
return _FactoryIterableView(matches)
|
||||
if not isinstance(matches, collections_abc.Sequence):
|
||||
matches = list(matches)
|
||||
return _SequenceIterableView(matches)
|
||||
|
|
|
@ -16,7 +16,7 @@ requests==2.25.0
|
|||
chardet==3.0.4
|
||||
idna==2.10
|
||||
urllib3==1.26.2
|
||||
resolvelib==0.4.0
|
||||
resolvelib==0.5.2
|
||||
retrying==1.3.3
|
||||
setuptools==44.0.0
|
||||
six==1.15.0
|
||||
|
|
Loading…
Reference in a new issue