Skip to content

Commit 8b3ef88

Browse files
committed
Take responsibility to remove incompatibilities
1 parent 778778c commit 8b3ef88

File tree

3 files changed

+57
-35
lines changed

3 files changed

+57
-35
lines changed

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

Lines changed: 43 additions & 23 deletions
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,
@@ -101,9 +102,10 @@ def __init__(
101102
self._build_failures = {} # type: Cache[InstallationError]
102103
self._link_candidate_cache = {} # type: Cache[LinkCandidate]
103104
self._editable_candidate_cache = {} # type: Cache[EditableCandidate]
104-
self._installed_candidate_cache = (
105-
{}
106-
) # type: Dict[str, AlreadyInstalledCandidate]
105+
self._installed_candidate_cache = {
106+
} # type: Dict[str, AlreadyInstalledCandidate]
107+
self._extras_candidate_cache = {
108+
} # type: Dict[Tuple[int, FrozenSet[str]], ExtrasCandidate]
107109

108110
if not ignore_installed:
109111
self._installed_dists = {
@@ -118,6 +120,16 @@ def force_reinstall(self):
118120
# type: () -> bool
119121
return self._force_reinstall
120122

123+
def _make_extras_candidate(self, base, extras):
124+
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
125+
cache_key = (id(base), extras)
126+
try:
127+
candidate = self._extras_candidate_cache[cache_key]
128+
except KeyError:
129+
candidate = ExtrasCandidate(base, extras)
130+
self._extras_candidate_cache[cache_key] = candidate
131+
return candidate
132+
121133
def _make_candidate_from_dist(
122134
self,
123135
dist, # type: Distribution
@@ -130,9 +142,9 @@ def _make_candidate_from_dist(
130142
except KeyError:
131143
base = AlreadyInstalledCandidate(dist, template, factory=self)
132144
self._installed_candidate_cache[dist.key] = base
133-
if extras:
134-
return ExtrasCandidate(base, extras)
135-
return base
145+
if not extras:
146+
return base
147+
return self._make_extras_candidate(base, extras)
136148

137149
def _make_candidate_from_link(
138150
self,
@@ -182,18 +194,18 @@ def _make_candidate_from_link(
182194
return None
183195
base = self._link_candidate_cache[link]
184196

185-
if extras:
186-
return ExtrasCandidate(base, extras)
187-
return base
197+
if not extras:
198+
return base
199+
return self._make_extras_candidate(base, extras)
188200

189201
def _iter_found_candidates(
190202
self,
191-
ireqs, # type: Sequence[InstallRequirement]
192-
specifier, # type: SpecifierSet
193-
hashes, # type: Hashes
194-
prefers_installed, # type: bool
195-
):
196-
# type: (...) -> Iterable[Candidate]
203+
ireqs: Sequence[InstallRequirement],
204+
specifier: SpecifierSet,
205+
hashes: Hashes,
206+
prefers_installed: bool,
207+
incompatible_ids: Set[int],
208+
) -> Iterable[Candidate]:
197209
if not ireqs:
198210
return ()
199211

@@ -257,20 +269,27 @@ def iter_index_candidate_infos():
257269
iter_index_candidate_infos,
258270
installed_candidate,
259271
prefers_installed,
272+
incompatible_ids,
260273
)
261274

262275
def find_candidates(
263276
self,
264-
requirements, # type: Sequence[Requirement]
265-
constraint, # type: Constraint
266-
prefers_installed, # type: bool
267-
):
268-
# type: (...) -> Iterable[Candidate]
277+
identifier: str,
278+
requirements: Mapping[str, Iterator[Requirement]],
279+
incompatibilities: Mapping[str, Iterator[Candidate]],
280+
constraint: Constraint,
281+
prefers_installed: bool,
282+
) -> Iterable[Candidate]:
283+
284+
# Since we cache all the candidates, incompatibility identification
285+
# can be made quicker by comparing only the id() values.
286+
incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
287+
269288
explicit_candidates = set() # type: Set[Candidate]
270289
ireqs = [] # type: List[InstallRequirement]
271-
for req in requirements:
290+
for req in requirements[identifier]:
272291
cand, ireq = req.get_candidate_lookup()
273-
if cand is not None:
292+
if cand is not None and id(cand) not in incompat_ids:
274293
explicit_candidates.add(cand)
275294
if ireq is not None:
276295
ireqs.append(ireq)
@@ -283,13 +302,14 @@ def find_candidates(
283302
constraint.specifier,
284303
constraint.hashes,
285304
prefers_installed,
305+
incompat_ids,
286306
)
287307

288308
return (
289309
c
290310
for c in explicit_candidates
291311
if constraint.is_satisfied_by(c)
292-
and all(req.is_satisfied_by(c) for req in requirements)
312+
and all(req.is_satisfied_by(c) for req in requirements[identifier])
293313
)
294314

295315
def make_requirement_from_install_req(self, ireq, requested_extras):

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

Lines changed: 11 additions & 7 deletions
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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +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 []
156152

157153
def _eligible_for_upgrade(name):
158154
# type: (str) -> bool
@@ -173,9 +169,11 @@ def _eligible_for_upgrade(name):
173169
return False
174170

175171
return self._factory.find_candidates(
176-
list(current_requirements),
172+
identifier=identifier,
173+
requirements=requirements,
177174
constraint=self._constraints.get(identifier, Constraint.empty()),
178175
prefers_installed=(not _eligible_for_upgrade(identifier)),
176+
incompatibilities=incompatibilities,
179177
)
180178

181179
def is_satisfied_by(self, requirement, candidate):

0 commit comments

Comments
 (0)