Skip to content

Commit e6a65fc

Browse files
authored
Merge pull request #9771 from uranusjr/resolvelib-060
2 parents a0f6041 + 6468108 commit e6a65fc

File tree

12 files changed

+193
-97
lines changed

12 files changed

+193
-97
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/factory.py

+42-20
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Iterable,
88
Iterator,
99
List,
10+
Mapping,
1011
Optional,
1112
Sequence,
1213
Set,
@@ -104,6 +105,9 @@ def __init__(
104105
self._installed_candidate_cache = (
105106
{}
106107
) # type: Dict[str, AlreadyInstalledCandidate]
108+
self._extras_candidate_cache = (
109+
{}
110+
) # type: Dict[Tuple[int, FrozenSet[str]], ExtrasCandidate]
107111

108112
if not ignore_installed:
109113
self._installed_dists = {
@@ -118,6 +122,16 @@ def force_reinstall(self):
118122
# type: () -> bool
119123
return self._force_reinstall
120124

125+
def _make_extras_candidate(self, base, extras):
126+
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
127+
cache_key = (id(base), extras)
128+
try:
129+
candidate = self._extras_candidate_cache[cache_key]
130+
except KeyError:
131+
candidate = ExtrasCandidate(base, extras)
132+
self._extras_candidate_cache[cache_key] = candidate
133+
return candidate
134+
121135
def _make_candidate_from_dist(
122136
self,
123137
dist, # type: Distribution
@@ -130,9 +144,9 @@ def _make_candidate_from_dist(
130144
except KeyError:
131145
base = AlreadyInstalledCandidate(dist, template, factory=self)
132146
self._installed_candidate_cache[dist.key] = base
133-
if extras:
134-
return ExtrasCandidate(base, extras)
135-
return base
147+
if not extras:
148+
return base
149+
return self._make_extras_candidate(base, extras)
136150

137151
def _make_candidate_from_link(
138152
self,
@@ -182,18 +196,18 @@ def _make_candidate_from_link(
182196
return None
183197
base = self._link_candidate_cache[link]
184198

185-
if extras:
186-
return ExtrasCandidate(base, extras)
187-
return base
199+
if not extras:
200+
return base
201+
return self._make_extras_candidate(base, extras)
188202

189203
def _iter_found_candidates(
190204
self,
191-
ireqs, # type: Sequence[InstallRequirement]
192-
specifier, # type: SpecifierSet
193-
hashes, # type: Hashes
194-
prefers_installed, # type: bool
195-
):
196-
# type: (...) -> Iterable[Candidate]
205+
ireqs: Sequence[InstallRequirement],
206+
specifier: SpecifierSet,
207+
hashes: Hashes,
208+
prefers_installed: bool,
209+
incompatible_ids: Set[int],
210+
) -> Iterable[Candidate]:
197211
if not ireqs:
198212
return ()
199213

@@ -257,20 +271,27 @@ def iter_index_candidate_infos():
257271
iter_index_candidate_infos,
258272
installed_candidate,
259273
prefers_installed,
274+
incompatible_ids,
260275
)
261276

262277
def find_candidates(
263278
self,
264-
requirements, # type: Sequence[Requirement]
265-
constraint, # type: Constraint
266-
prefers_installed, # type: bool
267-
):
268-
# type: (...) -> Iterable[Candidate]
279+
identifier: str,
280+
requirements: Mapping[str, Iterator[Requirement]],
281+
incompatibilities: Mapping[str, Iterator[Candidate]],
282+
constraint: Constraint,
283+
prefers_installed: bool,
284+
) -> Iterable[Candidate]:
285+
286+
# Since we cache all the candidates, incompatibility identification
287+
# can be made quicker by comparing only the id() values.
288+
incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
289+
269290
explicit_candidates = set() # type: Set[Candidate]
270291
ireqs = [] # type: List[InstallRequirement]
271-
for req in requirements:
292+
for req in requirements[identifier]:
272293
cand, ireq = req.get_candidate_lookup()
273-
if cand is not None:
294+
if cand is not None and id(cand) not in incompat_ids:
274295
explicit_candidates.add(cand)
275296
if ireq is not None:
276297
ireqs.append(ireq)
@@ -283,13 +304,14 @@ def find_candidates(
283304
constraint.specifier,
284305
constraint.hashes,
285306
prefers_installed,
307+
incompat_ids,
286308
)
287309

288310
return (
289311
c
290312
for c in explicit_candidates
291313
if constraint.is_satisfied_by(c)
292-
and all(req.is_satisfied_by(c) for req in requirements)
314+
and all(req.is_satisfied_by(c) for req in requirements[identifier])
293315
)
294316

295317
def make_requirement_from_install_req(self, ireq, requested_extras):

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

+11-7
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,15 @@ class FoundCandidates(collections_abc.Sequence):
100100

101101
def __init__(
102102
self,
103-
get_infos, # type: Callable[[], Iterator[IndexCandidateInfo]]
104-
installed, # type: Optional[Candidate]
105-
prefers_installed, # type: bool
103+
get_infos: Callable[[], Iterator[IndexCandidateInfo]],
104+
installed: Optional[Candidate],
105+
prefers_installed: bool,
106+
incompatible_ids: Set[int],
106107
):
107108
self._get_infos = get_infos
108109
self._installed = installed
109110
self._prefers_installed = prefers_installed
111+
self._incompatible_ids = incompatible_ids
110112

111113
def __getitem__(self, index):
112114
# type: (int) -> Candidate
@@ -119,10 +121,12 @@ def __iter__(self):
119121
# type: () -> Iterator[Candidate]
120122
infos = self._get_infos()
121123
if not self._installed:
122-
return _iter_built(infos)
123-
if self._prefers_installed:
124-
return _iter_built_with_prepended(self._installed, infos)
125-
return _iter_built_with_inserted(self._installed, infos)
124+
iterator = _iter_built(infos)
125+
elif self._prefers_installed:
126+
iterator = _iter_built_with_prepended(self._installed, infos)
127+
else:
128+
iterator = _iter_built_with_inserted(self._installed, infos)
129+
return (c for c in iterator if id(c) not in self._incompatible_ids)
126130

127131
def __len__(self):
128132
# type: () -> int

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

+21-10
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,12 +143,12 @@ 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:
140-
return []
141-
name = requirements[0].project_name
142-
146+
def find_matches(
147+
self,
148+
identifier: str,
149+
requirements: Mapping[str, Iterator[Requirement]],
150+
incompatibilities: Mapping[str, Iterator[Candidate]],
151+
) -> Iterable[Candidate]:
143152
def _eligible_for_upgrade(name):
144153
# type: (str) -> bool
145154
"""Are upgrades allowed for this project?
@@ -159,9 +168,11 @@ def _eligible_for_upgrade(name):
159168
return False
160169

161170
return self._factory.find_candidates(
162-
requirements,
163-
constraint=self._constraints.get(name, Constraint.empty()),
164-
prefers_installed=(not _eligible_for_upgrade(name)),
171+
identifier=identifier,
172+
requirements=requirements,
173+
constraint=self._constraints.get(identifier, Constraint.empty()),
174+
prefers_installed=(not _eligible_for_upgrade(identifier)),
175+
incompatibilities=incompatibilities,
165176
)
166177

167178
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

0 commit comments

Comments
 (0)