Skip to content

Commit 8c2d448

Browse files
committed
Refactor Factory.find_candidates() for readability
1 parent 0305e0d commit 8c2d448

File tree

1 file changed

+99
-67
lines changed

1 file changed

+99
-67
lines changed

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

Lines changed: 99 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ def force_reinstall(self):
126126
# type: () -> bool
127127
return self._force_reinstall
128128

129+
def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
130+
if not link.is_wheel:
131+
return
132+
wheel = Wheel(link.filename)
133+
if wheel.supported(self._finder.target_python.get_tags()):
134+
return
135+
msg = f"{link.filename} is not a supported wheel on this platform."
136+
raise UnsupportedWheel(msg)
137+
129138
def _make_extras_candidate(self, base, extras):
130139
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
131140
cache_key = (id(base), extras)
@@ -278,6 +287,62 @@ def iter_index_candidate_infos():
278287
incompatible_ids,
279288
)
280289

290+
def _iter_explicit_candidates_from_base(
291+
self,
292+
identifier: str,
293+
requirements: Mapping[str, Iterator[Requirement]],
294+
) -> Iterator[Candidate]:
295+
"""Produce explicit candidates from the base given an extra-ed package.
296+
297+
:param identifier: A dependency identifier in the resolver. If this
298+
points to an extra-ed package (e.g. ``package[foo]``), explcit
299+
candidates known for the base package (identified by ``package``)
300+
are returned. No candidates are generated if this does not point to
301+
an extra-ed Python package.
302+
:param requirements: A mapping of requirements known to the resolver,
303+
as passed in by ``find_matches()``.
304+
"""
305+
try:
306+
pkg_req = PackagingRequirement(identifier)
307+
except InvalidRequirement:
308+
return
309+
base_identifier = pkg_req.name
310+
if base_identifier not in requirements:
311+
return
312+
extras = frozenset(pkg_req.extras)
313+
for req in requirements[base_identifier]:
314+
lookup_cand, _ = req.get_candidate_lookup()
315+
if lookup_cand is None: # Not explicit.
316+
continue
317+
# We've stripped extras from the identifier, and should always
318+
# get a BaseCandidate here, unless there's a bug elsewhere.
319+
base_cand = as_base_candidate(lookup_cand)
320+
assert base_cand is not None
321+
yield self._make_extras_candidate(base_cand, extras)
322+
323+
def _iter_explicit_candidates_from_constraints(
324+
self,
325+
identifier: str,
326+
constraint: Constraint,
327+
template: InstallRequirement,
328+
) -> Iterator[Candidate]:
329+
"""Produce explicit candidates from constraints.
330+
331+
This creates "fake" InstallRequirement objects that are basically clones
332+
of what "should" be the template, but with original_link set to link.
333+
"""
334+
for link in constraint.links:
335+
# If we're constrained to install a wheel incompatible with the
336+
# target architecture, no candidates will ever be valid.
337+
self._fail_if_link_is_unsupported_wheel(link)
338+
yield self._make_candidate_from_link(
339+
link,
340+
extras=frozenset(),
341+
template=install_req_from_link_and_ireq(link, template),
342+
name=identifier,
343+
version=None,
344+
)
345+
281346
def find_candidates(
282347
self,
283348
identifier: str,
@@ -291,76 +356,49 @@ def find_candidates(
291356
# can be made quicker by comparing only the id() values.
292357
incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
293358

359+
# Collect basic lookup information from the requirements.
294360
explicit_candidates = set() # type: Set[Candidate]
295361
ireqs = [] # type: List[InstallRequirement]
296362
for req in requirements[identifier]:
297363
cand, ireq = req.get_candidate_lookup()
298-
if cand is not None and id(cand) not in incompat_ids:
364+
if cand is not None and id(cand):
299365
explicit_candidates.add(cand)
300366
if ireq is not None:
301367
ireqs.append(ireq)
302368

303-
for link in constraint.links:
304-
if not ireqs:
305-
# If we hit this condition, then we cannot construct a candidate.
306-
# However, if we hit this condition, then none of the requirements
307-
# provided an ireq, so they must have provided an explicit candidate.
308-
# In that case, either the candidate matches, in which case this loop
309-
# doesn't need to do anything, or it doesn't, in which case there's
310-
# nothing this loop can do to recover.
311-
break
312-
if link.is_wheel:
313-
wheel = Wheel(link.filename)
314-
# Check whether the provided wheel is compatible with the target
315-
# platform.
316-
if not wheel.supported(self._finder.target_python.get_tags()):
317-
# We are constrained to install a wheel that is incompatible with
318-
# the target architecture, so there are no valid candidates.
319-
# Return early, with no candidates.
320-
return ()
321-
# Create a "fake" InstallRequirement that's basically a clone of
322-
# what "should" be the template, but with original_link set to link.
323-
# Using the given requirement is necessary for preserving hash
324-
# requirements, but without the original_link, direct_url.json
325-
# won't be created.
326-
ireq = install_req_from_link_and_ireq(link, ireqs[0])
327-
candidate = self._make_candidate_from_link(
328-
link,
329-
extras=frozenset(),
330-
template=ireq,
331-
name=canonicalize_name(ireq.name) if ireq.name else None,
332-
version=None,
333-
)
334-
if candidate is None:
335-
# _make_candidate_from_link returns None if the wheel fails to build.
336-
# We are constrained to install this wheel, so there are no valid
337-
# candidates.
338-
# Return early, with no candidates.
339-
return ()
340-
341-
explicit_candidates.add(candidate)
369+
# If the current identifier contains extras, add explicit candidates
370+
# from entries from extra-less identifier.
371+
explicit_candidates = explicit_candidates.union(
372+
self._iter_explicit_candidates_from_base(identifier, requirements),
373+
)
342374

343-
# If the current identifier contains extras, also add explicit
344-
# candidates from entries from extra-less identifier.
345-
try:
346-
identifier_req = PackagingRequirement(identifier)
347-
except InvalidRequirement:
348-
base_identifier = None
349-
extras: FrozenSet[str] = frozenset()
375+
# Add explicit candidates from constraints. We only do this if there are
376+
# kown ireqs, which represent requirements not already explicit. If
377+
# there are no ireqs, we're constraining already-explicit requirements,
378+
# which is covered by the else block.
379+
if ireqs:
380+
try:
381+
explicit_candidates.update(
382+
self._iter_explicit_candidates_from_constraints(
383+
identifier,
384+
constraint,
385+
template=ireqs[0],
386+
),
387+
)
388+
except UnsupportedWheel:
389+
return ()
350390
else:
351-
base_identifier = identifier_req.name
352-
extras = frozenset(identifier_req.extras)
353-
if base_identifier and base_identifier in requirements:
354-
for req in requirements[base_identifier]:
355-
lookup_cand, _ = req.get_candidate_lookup()
356-
if lookup_cand is None: # Not explicit.
357-
continue
358-
# We've stripped extras from the identifier, and should always
359-
# get a BaseCandidate here, unless there's a bug elsewhere.
360-
base_cand = as_base_candidate(lookup_cand)
361-
assert base_cand is not None
362-
candidate = self._make_extras_candidate(base_cand, extras)
363-
explicit_candidates.add(candidate)
391+
explicit_candidates = {
392+
candidate
393+
for candidate in explicit_candidates
394+
if constraint.is_satisfied_by(candidate)
395+
}
396+
397+
explicit_candidates = {
398+
candidate
399+
for candidate in explicit_candidates
400+
if id(candidate) not in incompat_ids
401+
}
364402

365403
# If none of the requirements want an explicit candidate, we can ask
366404
# the finder for candidates.
@@ -391,13 +429,7 @@ def make_requirement_from_install_req(self, ireq, requested_extras):
391429
return None
392430
if not ireq.link:
393431
return SpecifierRequirement(ireq)
394-
if ireq.link.is_wheel:
395-
wheel = Wheel(ireq.link.filename)
396-
if not wheel.supported(self._finder.target_python.get_tags()):
397-
msg = "{} is not a supported wheel on this platform.".format(
398-
wheel.filename,
399-
)
400-
raise UnsupportedWheel(msg)
432+
self._fail_if_link_is_unsupported_wheel(ireq.link)
401433
cand = self._make_candidate_from_link(
402434
ireq.link,
403435
extras=frozenset(ireq.extras),

0 commit comments

Comments
 (0)