Skip to content

Commit d1543e8

Browse files
committed
Upgrade vendored resolvelib to 0.6.0
1 parent a118afe commit d1543e8

File tree

9 files changed

+131
-66
lines changed

9 files changed

+131
-66
lines changed

news/resolvelib.vendor.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Upgrade vendored resolvelib to 0.5.5.
1+
Upgrade vendored resolvelib to 0.6.0.

src/pip/_internal/resolution/resolvelib/provider.py

+22-8
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Union
1+
from typing import (
2+
TYPE_CHECKING,
3+
Dict,
4+
Iterable,
5+
Iterator,
6+
Mapping,
7+
Optional,
8+
Sequence,
9+
Union,
10+
)
211

312
from pip._vendor.resolvelib.providers import AbstractProvider
413

@@ -134,11 +143,16 @@ def _get_restrictive_rating(requirements):
134143

135144
return (delay_this, restrictive, order, key)
136145

137-
def find_matches(self, requirements):
138-
# type: (Sequence[Requirement]) -> Iterable[Candidate]
139-
if not requirements:
146+
def find_matches(
147+
self,
148+
identifier: str,
149+
requirements: Mapping[str, Iterator[Requirement]],
150+
incompatibilities: Mapping[str, Iterator[Candidate]],
151+
) -> Iterable[Candidate]:
152+
try:
153+
current_requirements = requirements[identifier]
154+
except KeyError:
140155
return []
141-
name = requirements[0].project_name
142156

143157
def _eligible_for_upgrade(name):
144158
# type: (str) -> bool
@@ -159,9 +173,9 @@ def _eligible_for_upgrade(name):
159173
return False
160174

161175
return self._factory.find_candidates(
162-
requirements,
163-
constraint=self._constraints.get(name, Constraint.empty()),
164-
prefers_installed=(not _eligible_for_upgrade(name)),
176+
list(current_requirements),
177+
constraint=self._constraints.get(identifier, Constraint.empty()),
178+
prefers_installed=(not _eligible_for_upgrade(identifier)),
165179
)
166180

167181
def is_satisfied_by(self, requirement, candidate):

src/pip/_vendor/resolvelib/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"ResolutionTooDeep",
1212
]
1313

14-
__version__ = "0.5.5"
14+
__version__ = "0.6.0"
1515

1616

1717
from .providers import AbstractProvider, AbstractResolver
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
__all__ = ["Sequence"]
1+
__all__ = ["Mapping", "Sequence"]
22

33
try:
4-
from collections.abc import Sequence
4+
from collections.abc import Mapping, Sequence
55
except ImportError:
6-
from collections import Sequence
6+
from collections import Mapping, Sequence

src/pip/_vendor/resolvelib/providers.py

+12-6
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,18 @@ def get_preference(self, resolution, candidates, information):
5050
"""
5151
raise NotImplementedError
5252

53-
def find_matches(self, requirements):
54-
"""Find all possible candidates that satisfy the given requirements.
53+
def find_matches(self, identifier, requirements, incompatibilities):
54+
"""Find all possible candidates that satisfy given constraints.
55+
56+
:param identifier: An identifier as returned by ``identify()``. This
57+
identifies the dependency matches of which should be returned.
58+
:param requirements: A mapping of requirements that all returned
59+
candidates must satisfy. Each key is an identifier, and the value
60+
an iterator of requirements for that dependency.
61+
:param incompatibilities: A mapping of known incompatibilities of
62+
each dependency. Each key is an identifier, and the value an
63+
iterator of incompatibilities known to the resolver. All
64+
incompatibilities *must* be excluded from the return value.
5565
5666
This should try to get candidates based on the requirements' types.
5767
For VCS, local, and archive requirements, the one-and-only match is
@@ -66,10 +76,6 @@ def find_matches(self, requirements):
6676
* An collection of candidates.
6777
* An iterable of candidates. This will be consumed immediately into a
6878
list of candidates.
69-
70-
:param requirements: A collection of requirements which all of the
71-
returned candidates must match. All requirements are guaranteed to
72-
have the same identifier. The collection is never empty.
7379
"""
7480
raise NotImplementedError
7581

src/pip/_vendor/resolvelib/providers.pyi

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ from typing import (
33
Collection,
44
Generic,
55
Iterable,
6+
Iterator,
67
Mapping,
78
Optional,
89
Protocol,
9-
Sequence,
1010
Union,
1111
)
1212

@@ -31,7 +31,12 @@ class AbstractProvider(Generic[RT, CT, KT]):
3131
candidates: IterableView[CT],
3232
information: Collection[RequirementInformation[RT, CT]],
3333
) -> Preference: ...
34-
def find_matches(self, requirements: Sequence[RT]) -> Matches: ...
34+
def find_matches(
35+
self,
36+
identifier: KT,
37+
requirements: Mapping[KT, Iterator[RT]],
38+
incompatibilities: Mapping[KT, Iterator[CT]],
39+
) -> Matches: ...
3540
def is_satisfied_by(self, requirement: RT, candidate: CT) -> bool: ...
3641
def get_dependencies(self, candidate: CT) -> Iterable[RT]: ...
3742

src/pip/_vendor/resolvelib/resolvers.py

+58-44
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import collections
2+
import operator
23

34
from .providers import AbstractResolver
4-
from .structs import DirectedGraph, build_iter_view
5+
from .structs import DirectedGraph, IteratorMapping, build_iter_view
56

67

78
RequirementInformation = collections.namedtuple(
@@ -73,45 +74,12 @@ def __repr__(self):
7374
)
7475
return "Criterion({})".format(requirements)
7576

76-
@classmethod
77-
def from_requirement(cls, provider, requirement, parent):
78-
"""Build an instance from a requirement."""
79-
matches = provider.find_matches(requirements=[requirement])
80-
cands = build_iter_view(matches)
81-
infos = [RequirementInformation(requirement, parent)]
82-
criterion = cls(cands, infos, incompatibilities=[])
83-
if not cands:
84-
raise RequirementsConflicted(criterion)
85-
return criterion
86-
8777
def iter_requirement(self):
8878
return (i.requirement for i in self.information)
8979

9080
def iter_parent(self):
9181
return (i.parent for i in self.information)
9282

93-
def merged_with(self, provider, requirement, parent):
94-
"""Build a new instance from this and a new requirement."""
95-
infos = list(self.information)
96-
infos.append(RequirementInformation(requirement, parent))
97-
matches = provider.find_matches([r for r, _ in infos])
98-
cands = build_iter_view(matches)
99-
criterion = type(self)(cands, infos, list(self.incompatibilities))
100-
if not cands:
101-
raise RequirementsConflicted(criterion)
102-
return criterion
103-
104-
def excluded_of(self, candidates):
105-
"""Build a new instance from this, but excluding specified candidates.
106-
107-
Returns the new instance, or None if we still have no valid candidates.
108-
"""
109-
cands = self.candidates.excluding(candidates)
110-
if not cands:
111-
return None
112-
incompats = self.incompatibilities + candidates
113-
return type(self)(cands, list(self.information), incompats)
114-
11583

11684
class ResolutionError(ResolverException):
11785
pass
@@ -168,13 +136,42 @@ def _push_new_state(self):
168136

169137
def _merge_into_criterion(self, requirement, parent):
170138
self._r.adding_requirement(requirement=requirement, parent=parent)
171-
name = self._p.identify(requirement_or_candidate=requirement)
172-
if name in self.state.criteria:
173-
crit = self.state.criteria[name]
174-
crit = crit.merged_with(self._p, requirement, parent)
139+
140+
identifier = self._p.identify(requirement_or_candidate=requirement)
141+
criterion = self.state.criteria.get(identifier)
142+
if criterion:
143+
incompatibilities = list(criterion.incompatibilities)
144+
else:
145+
incompatibilities = []
146+
147+
matches = self._p.find_matches(
148+
identifier=identifier,
149+
requirements=IteratorMapping(
150+
self.state.criteria,
151+
operator.methodcaller("iter_requirement"),
152+
{identifier: [requirement]},
153+
),
154+
incompatibilities=IteratorMapping(
155+
self.state.criteria,
156+
operator.attrgetter("incompatibilities"),
157+
{identifier: incompatibilities},
158+
),
159+
)
160+
161+
if criterion:
162+
information = list(criterion.information)
163+
information.append(RequirementInformation(requirement, parent))
175164
else:
176-
crit = Criterion.from_requirement(self._p, requirement, parent)
177-
return name, crit
165+
information = [RequirementInformation(requirement, parent)]
166+
167+
criterion = Criterion(
168+
candidates=build_iter_view(matches),
169+
information=information,
170+
incompatibilities=incompatibilities,
171+
)
172+
if not criterion.candidates:
173+
raise RequirementsConflicted(criterion)
174+
return identifier, criterion
178175

179176
def _get_criterion_item_preference(self, item):
180177
name, criterion = item
@@ -268,7 +265,7 @@ def _backtrack(self):
268265
broken_state = self._states.pop()
269266
name, candidate = broken_state.mapping.popitem()
270267
incompatibilities_from_broken = [
271-
(k, v.incompatibilities)
268+
(k, list(v.incompatibilities))
272269
for k, v in broken_state.criteria.items()
273270
]
274271

@@ -287,10 +284,27 @@ def _patch_criteria():
287284
criterion = self.state.criteria[k]
288285
except KeyError:
289286
continue
290-
criterion = criterion.excluded_of(incompatibilities)
291-
if criterion is None:
287+
matches = self._p.find_matches(
288+
identifier=k,
289+
requirements=IteratorMapping(
290+
self.state.criteria,
291+
operator.methodcaller("iter_requirement"),
292+
),
293+
incompatibilities=IteratorMapping(
294+
self.state.criteria,
295+
operator.attrgetter("incompatibilities"),
296+
{k: incompatibilities},
297+
),
298+
)
299+
candidates = build_iter_view(matches)
300+
if not candidates:
292301
return False
293-
self.state.criteria[k] = criterion
302+
incompatibilities.extend(criterion.incompatibilities)
303+
self.state.criteria[k] = Criterion(
304+
candidates=candidates,
305+
information=list(criterion.information),
306+
incompatibilities=incompatibilities,
307+
)
294308
return True
295309

296310
self._push_new_state()

src/pip/_vendor/resolvelib/structs.py

+26
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import itertools
12
from .compat import collections_abc
23

34

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

6970

71+
class IteratorMapping(collections_abc.Mapping):
72+
def __init__(self, mapping, accessor, appends=None):
73+
self._mapping = mapping
74+
self._accessor = accessor
75+
self._appends = appends or {}
76+
77+
def __contains__(self, key):
78+
return key in self._mapping or key in self._appends
79+
80+
def __getitem__(self, k):
81+
try:
82+
v = self._mapping[k]
83+
except KeyError:
84+
return iter(self._appends[k])
85+
return itertools.chain(self._accessor(v), self._appends.get(k, ()))
86+
87+
def __iter__(self):
88+
more = (k for k in self._appends if k not in self._mapping)
89+
return itertools.chain(self._mapping, more)
90+
91+
def __len__(self):
92+
more = len(k for k in self._appends if k not in self._mapping)
93+
return len(self._mapping) + more
94+
95+
7096
class _FactoryIterableView(object):
7197
"""Wrap an iterator factory returned by `find_matches()`.
7298

src/pip/_vendor/vendor.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ requests==2.25.1
1414
chardet==4.0.0
1515
idna==2.10
1616
urllib3==1.26.4
17-
resolvelib==0.5.5
17+
resolvelib==0.6.0
1818
setuptools==44.0.0
1919
six==1.15.0
2020
tenacity==6.3.1

0 commit comments

Comments
 (0)