Skip to content

Commit febe770

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

File tree

1 file changed

+86
-68
lines changed

1 file changed

+86
-68
lines changed

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

Lines changed: 86 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import functools
23
import logging
34
from typing import (
@@ -126,6 +127,15 @@ def force_reinstall(self):
126127
# type: () -> bool
127128
return self._force_reinstall
128129

130+
def _fail_if_link_is_unsupported_wheel(self, link: Link) -> None:
131+
if not link.is_wheel:
132+
return
133+
wheel = Wheel(link.filename)
134+
if wheel.supported(self._finder.target_python.get_tags()):
135+
return
136+
msg = f"{link.filename} is not a supported wheel on this platform."
137+
raise UnsupportedWheel(msg)
138+
129139
def _make_extras_candidate(self, base, extras):
130140
# type: (BaseCandidate, FrozenSet[str]) -> ExtrasCandidate
131141
cache_key = (id(base), extras)
@@ -278,6 +288,51 @@ def iter_index_candidate_infos():
278288
incompatible_ids,
279289
)
280290

291+
def _iter_explicit_candidates_from_base(
292+
self,
293+
base_requirements: Iterable[Requirement],
294+
extras: FrozenSet[str],
295+
) -> Iterator[Candidate]:
296+
"""Produce explicit candidates from the base given an extra-ed package.
297+
298+
:param base_requirements: Requirements known to the resolver. The
299+
requirements are guaranteed to not have extras.
300+
:param extras: The extras to inject into the explicit requirements'
301+
candidates.
302+
"""
303+
for req in base_requirements:
304+
lookup_cand, _ = req.get_candidate_lookup()
305+
if lookup_cand is None: # Not explicit.
306+
continue
307+
# We've stripped extras from the identifier, and should always
308+
# get a BaseCandidate here, unless there's a bug elsewhere.
309+
base_cand = as_base_candidate(lookup_cand)
310+
assert base_cand is not None, "no extras here"
311+
yield self._make_extras_candidate(base_cand, extras)
312+
313+
def _iter_candidates_from_constraints(
314+
self,
315+
identifier: str,
316+
constraint: Constraint,
317+
template: InstallRequirement,
318+
) -> Iterator[Candidate]:
319+
"""Produce explicit candidates from constraints.
320+
321+
This creates "fake" InstallRequirement objects that are basically clones
322+
of what "should" be the template, but with original_link set to link.
323+
"""
324+
for link in constraint.links:
325+
self._fail_if_link_is_unsupported_wheel(link)
326+
candidate = self._make_candidate_from_link(
327+
link,
328+
extras=frozenset(),
329+
template=install_req_from_link_and_ireq(link, template),
330+
name=canonicalize_name(identifier),
331+
version=None,
332+
)
333+
if candidate:
334+
yield candidate
335+
281336
def find_candidates(
282337
self,
283338
identifier: str,
@@ -291,76 +346,44 @@ def find_candidates(
291346
# can be made quicker by comparing only the id() values.
292347
incompat_ids = {id(c) for c in incompatibilities.get(identifier, ())}
293348

349+
# Collect basic lookup information from the requirements.
294350
explicit_candidates = set() # type: Set[Candidate]
295351
ireqs = [] # type: List[InstallRequirement]
296352
for req in requirements[identifier]:
297353
cand, ireq = req.get_candidate_lookup()
298-
if cand is not None and id(cand) not in incompat_ids:
354+
if cand is not None and id(cand):
299355
explicit_candidates.add(cand)
300356
if ireq is not None:
301357
ireqs.append(ireq)
302358

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,
359+
# If the current identifier contains extras, add explicit candidates
360+
# from entries from extra-less identifier.
361+
with contextlib.suppress(InvalidRequirement):
362+
parsed_requirement = PackagingRequirement(identifier)
363+
explicit_candidates.update(
364+
self._iter_explicit_candidates_from_base(
365+
requirements.get(parsed_requirement.name, ()),
366+
frozenset(parsed_requirement.extras),
367+
),
333368
)
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)
342369

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()
350-
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)
370+
# Add explicit candidates from constraints. We only do this if there are
371+
# kown ireqs, which represent requirements not already explicit. If
372+
# there are no ireqs, we're constraining already-explicit requirements,
373+
# which is handled later when we return the explicit candidates.
374+
if ireqs:
375+
try:
376+
constraint_cands = self._iter_candidates_from_constraints(
377+
identifier,
378+
constraint,
379+
template=ireqs[0],
380+
)
381+
except UnsupportedWheel:
382+
# If we're constrained to install a wheel incompatible with the
383+
# target architecture, no candidates will ever be valid.
384+
return ()
385+
else:
386+
explicit_candidates.update(constraint_cands)
364387

365388
# If none of the requirements want an explicit candidate, we can ask
366389
# the finder for candidates.
@@ -376,7 +399,8 @@ def find_candidates(
376399
return (
377400
c
378401
for c in explicit_candidates
379-
if constraint.is_satisfied_by(c)
402+
if id(c) not in incompat_ids
403+
and constraint.is_satisfied_by(c)
380404
and all(req.is_satisfied_by(c) for req in requirements[identifier])
381405
)
382406

@@ -391,13 +415,7 @@ def make_requirement_from_install_req(self, ireq, requested_extras):
391415
return None
392416
if not ireq.link:
393417
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)
418+
self._fail_if_link_is_unsupported_wheel(ireq.link)
401419
cand = self._make_candidate_from_link(
402420
ireq.link,
403421
extras=frozenset(ireq.extras),

0 commit comments

Comments
 (0)