Skip to content

Commit

Permalink
Upgrade vendored resolvelib to 0.6.0
Browse files Browse the repository at this point in the history
  • Loading branch information
uranusjr committed Apr 3, 2021
1 parent fb57da1 commit 10488be
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 66 deletions.
2 changes: 1 addition & 1 deletion news/resolvelib.vendor.rst
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Upgrade vendored resolvelib to 0.5.5.
Upgrade vendored resolvelib to 0.6.0.
30 changes: 22 additions & 8 deletions src/pip/_internal/resolution/resolvelib/provider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Union
from typing import (
TYPE_CHECKING,
Dict,
Iterable,
Iterator,
Mapping,
Optional,
Sequence,
Union,
)

from pip._vendor.resolvelib.providers import AbstractProvider

Expand Down Expand Up @@ -134,11 +143,16 @@ def _get_restrictive_rating(requirements):

return (delay_this, restrictive, order, key)

def find_matches(self, requirements):
# type: (Sequence[Requirement]) -> Iterable[Candidate]
if not requirements:
def find_matches(
self,
identifier: str,
requirements: Mapping[str, Iterator[Requirement]],
incompatibilities: Mapping[str, Iterator[Candidate]],
) -> Iterable[Candidate]:
try:
current_requirements = requirements[identifier]
except KeyError:
return []
name = requirements[0].project_name

def _eligible_for_upgrade(name):
# type: (str) -> bool
Expand All @@ -159,9 +173,9 @@ def _eligible_for_upgrade(name):
return False

return self._factory.find_candidates(
requirements,
constraint=self._constraints.get(name, Constraint.empty()),
prefers_installed=(not _eligible_for_upgrade(name)),
list(current_requirements),
constraint=self._constraints.get(identifier, Constraint.empty()),
prefers_installed=(not _eligible_for_upgrade(identifier)),
)

def is_satisfied_by(self, requirement, candidate):
Expand Down
2 changes: 1 addition & 1 deletion src/pip/_vendor/resolvelib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"ResolutionTooDeep",
]

__version__ = "0.5.5"
__version__ = "0.6.0"


from .providers import AbstractProvider, AbstractResolver
Expand Down
6 changes: 3 additions & 3 deletions src/pip/_vendor/resolvelib/compat/collections_abc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__all__ = ["Sequence"]
__all__ = ["Mapping", "Sequence"]

try:
from collections.abc import Sequence
from collections.abc import Mapping, Sequence
except ImportError:
from collections import Sequence
from collections import Mapping, Sequence
18 changes: 12 additions & 6 deletions src/pip/_vendor/resolvelib/providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,18 @@ def get_preference(self, resolution, candidates, information):
"""
raise NotImplementedError

def find_matches(self, requirements):
"""Find all possible candidates that satisfy the given requirements.
def find_matches(self, identifier, requirements, incompatibilities):
"""Find all possible candidates that satisfy given constraints.
:param identifier: An identifier as returned by ``identify()``. This
identifies the dependency matches of which should be returned.
:param requirements: A mapping of requirements that all returned
candidates must satisfy. Each key is an identifier, and the value
an iterator of requirements for that dependency.
:param incompatibilities: A mapping of known incompatibilities of
each dependency. Each key is an identifier, and the value an
iterator of incompatibilities known to the resolver. All
incompatibilities *must* be excluded from the return value.
This should try to get candidates based on the requirements' types.
For VCS, local, and archive requirements, the one-and-only match is
Expand All @@ -66,10 +76,6 @@ def find_matches(self, requirements):
* 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.
"""
raise NotImplementedError

Expand Down
9 changes: 7 additions & 2 deletions src/pip/_vendor/resolvelib/providers.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ from typing import (
Collection,
Generic,
Iterable,
Iterator,
Mapping,
Optional,
Protocol,
Sequence,
Union,
)

Expand All @@ -31,7 +31,12 @@ class AbstractProvider(Generic[RT, CT, KT]):
candidates: IterableView[CT],
information: Collection[RequirementInformation[RT, CT]],
) -> Preference: ...
def find_matches(self, requirements: Sequence[RT]) -> Matches: ...
def find_matches(
self,
identifier: KT,
requirements: Mapping[KT, Iterator[RT]],
incompatibilities: Mapping[KT, Iterator[CT]],
) -> Matches: ...
def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool: ...
def get_dependencies(self, candidate: CT) -> Iterable[RT]: ...

Expand Down
102 changes: 58 additions & 44 deletions src/pip/_vendor/resolvelib/resolvers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import collections
import operator

from .providers import AbstractResolver
from .structs import DirectedGraph, build_iter_view
from .structs import DirectedGraph, IteratorMapping, build_iter_view


RequirementInformation = collections.namedtuple(
Expand Down Expand Up @@ -73,45 +74,12 @@ def __repr__(self):
)
return "Criterion({})".format(requirements)

@classmethod
def from_requirement(cls, provider, requirement, parent):
"""Build an instance from a requirement."""
matches = provider.find_matches(requirements=[requirement])
cands = build_iter_view(matches)
infos = [RequirementInformation(requirement, parent)]
criterion = cls(cands, infos, incompatibilities=[])
if not cands:
raise RequirementsConflicted(criterion)
return criterion

def iter_requirement(self):
return (i.requirement for i in self.information)

def iter_parent(self):
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."""
infos = list(self.information)
infos.append(RequirementInformation(requirement, parent))
matches = provider.find_matches([r for r, _ in infos])
cands = build_iter_view(matches)
criterion = type(self)(cands, infos, list(self.incompatibilities))
if not cands:
raise RequirementsConflicted(criterion)
return criterion

def excluded_of(self, candidates):
"""Build a new instance from this, but excluding specified candidates.
Returns the new instance, or None if we still have no valid candidates.
"""
cands = self.candidates.excluding(candidates)
if not cands:
return None
incompats = self.incompatibilities + candidates
return type(self)(cands, list(self.information), incompats)


class ResolutionError(ResolverException):
pass
Expand Down Expand Up @@ -168,13 +136,42 @@ def _push_new_state(self):

def _merge_into_criterion(self, requirement, parent):
self._r.adding_requirement(requirement=requirement, parent=parent)
name = self._p.identify(requirement_or_candidate=requirement)
if name in self.state.criteria:
crit = self.state.criteria[name]
crit = crit.merged_with(self._p, requirement, parent)

identifier = self._p.identify(requirement_or_candidate=requirement)
criterion = self.state.criteria.get(identifier)
if criterion:
incompatibilities = list(criterion.incompatibilities)
else:
incompatibilities = []

matches = self._p.find_matches(
identifier=identifier,
requirements=IteratorMapping(
self.state.criteria,
operator.methodcaller("iter_requirement"),
{identifier: [requirement]},
),
incompatibilities=IteratorMapping(
self.state.criteria,
operator.attrgetter("incompatibilities"),
{identifier: incompatibilities},
),
)

if criterion:
information = list(criterion.information)
information.append(RequirementInformation(requirement, parent))
else:
crit = Criterion.from_requirement(self._p, requirement, parent)
return name, crit
information = [RequirementInformation(requirement, parent)]

criterion = Criterion(
candidates=build_iter_view(matches),
information=information,
incompatibilities=incompatibilities,
)
if not criterion.candidates:
raise RequirementsConflicted(criterion)
return identifier, criterion

def _get_criterion_item_preference(self, item):
name, criterion = item
Expand Down Expand Up @@ -268,7 +265,7 @@ def _backtrack(self):
broken_state = self._states.pop()
name, candidate = broken_state.mapping.popitem()
incompatibilities_from_broken = [
(k, v.incompatibilities)
(k, list(v.incompatibilities))
for k, v in broken_state.criteria.items()
]

Expand All @@ -287,10 +284,27 @@ def _patch_criteria():
criterion = self.state.criteria[k]
except KeyError:
continue
criterion = criterion.excluded_of(incompatibilities)
if criterion is None:
matches = self._p.find_matches(
identifier=k,
requirements=IteratorMapping(
self.state.criteria,
operator.methodcaller("iter_requirement"),
),
incompatibilities=IteratorMapping(
self.state.criteria,
operator.attrgetter("incompatibilities"),
{k: incompatibilities},
),
)
candidates = build_iter_view(matches)
if not candidates:
return False
self.state.criteria[k] = criterion
incompatibilities.extend(criterion.incompatibilities)
self.state.criteria[k] = Criterion(
candidates=candidates,
information=list(criterion.information),
incompatibilities=incompatibilities,
)
return True

self._push_new_state()
Expand Down
26 changes: 26 additions & 0 deletions src/pip/_vendor/resolvelib/structs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import itertools
from .compat import collections_abc


Expand Down Expand Up @@ -67,6 +68,31 @@ def iter_parents(self, key):
return iter(self._backwards[key])


class IteratorMapping(collections_abc.Mapping):
def __init__(self, mapping, accessor, appends=None):
self._mapping = mapping
self._accessor = accessor
self._appends = appends or {}

def __contains__(self, key):
return key in self._mapping or key in self._appends

def __getitem__(self, k):
try:
v = self._mapping[k]
except KeyError:
return iter(self._appends[k])
return itertools.chain(self._accessor(v), self._appends.get(k, ()))

def __iter__(self):
more = (k for k in self._appends if k not in self._mapping)
return itertools.chain(self._mapping, more)

def __len__(self):
more = len(k for k in self._appends if k not in self._mapping)
return len(self._mapping) + more


class _FactoryIterableView(object):
"""Wrap an iterator factory returned by `find_matches()`.
Expand Down
2 changes: 1 addition & 1 deletion src/pip/_vendor/vendor.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ requests==2.25.1
chardet==4.0.0
idna==2.10
urllib3==1.26.4
resolvelib==0.5.5
resolvelib==0.6.0
setuptools==44.0.0
six==1.15.0
tenacity==6.3.1
Expand Down

0 comments on commit 10488be

Please sign in to comment.