Skip to content

Commit 60bbcfb

Browse files
committed
Take responsibility to remove incompatibilities
1 parent 778778c commit 60bbcfb

File tree

3 files changed

+56
-33
lines changed

3 files changed

+56
-33
lines changed

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

+3-6
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,6 @@ def find_matches(
149149
requirements: Mapping[str, Iterator[Requirement]],
150150
incompatibilities: Mapping[str, Iterator[Candidate]],
151151
) -> Iterable[Candidate]:
152-
try:
153-
current_requirements = requirements[identifier]
154-
except KeyError:
155-
return []
156-
157152
def _eligible_for_upgrade(name):
158153
# type: (str) -> bool
159154
"""Are upgrades allowed for this project?
@@ -173,9 +168,11 @@ def _eligible_for_upgrade(name):
173168
return False
174169

175170
return self._factory.find_candidates(
176-
list(current_requirements),
171+
identifier=identifier,
172+
requirements=requirements,
177173
constraint=self._constraints.get(identifier, Constraint.empty()),
178174
prefers_installed=(not _eligible_for_upgrade(identifier)),
175+
incompatibilities=incompatibilities,
179176
)
180177

181178
def is_satisfied_by(self, requirement, candidate):

0 commit comments

Comments
 (0)