1"""Routines related to PyPI, indexes"""
8from typing
import TYPE_CHECKING, FrozenSet, Iterable, List, Optional, Set, Tuple, Union
17 BestVersionAlreadyInstalled,
42__all__ = [
"FormatControl",
"BestCandidateResult",
"PackageFinder"]
45logger = getLogger(__name__)
47BuildTag = Union[Tuple[()], Tuple[int, str]]
48CandidateSortingKey = Tuple[int, int, int, _BaseVersion, Optional[int], BuildTag]
53 version_info: Tuple[int, int, int],
54 ignore_requires_python: bool =
False,
57 Return whether the given Python version is compatible with a link's
58 "Requires-Python" value.
60 :param version_info: A 3-tuple of ints representing the Python
61 major-minor-micro version to check.
62 :param ignore_requires_python: Whether to ignore the "Requires-Python"
63 value if the given Python version isn't compatible.
66 is_compatible = check_requires_python(
68 version_info=version_info,
72 "Ignoring invalid Requires-Python (%r) for link: %s",
78 version =
".".join(map(str, version_info))
79 if not ignore_requires_python:
81 "Link requires a different Python (%s not in: %r): %s",
89 "Ignoring failed Requires-Python check (%s not in: %r) for link: %s",
111 Responsible for evaluating links for a particular project.
124 formats: FrozenSet[str],
125 target_python: TargetPython,
127 ignore_requires_python: Optional[bool] =
None,
130 :param project_name: The user supplied package name.
131 :param canonical_name: The canonical package name.
132 :param formats: The formats allowed for this package. Should be a set
133 with 'binary' or 'source' or both in it.
134 :param target_python: The target Python interpreter to use when
135 evaluating link compatibility. This is used, for example, to
136 check wheel compatibility, as well as when checking the Python
137 version, e.g. the Python version embedded in a link filename
138 (or egg fragment) and against an HTML link's optional PEP 503
139 "data-requires-python" attribute.
140 :param allow_yanked: Whether files marked as yanked (in the sense
141 of PEP 592) are permitted to be candidates for install.
142 :param ignore_requires_python: Whether to ignore incompatible
143 PEP 503 "data-requires-python" values in HTML links. Defaults
146 if ignore_requires_python
is None:
147 ignore_requires_python =
False
159 Determine whether a link is a candidate for installation.
161 :return: A tuple (result, detail), where *result* is an enum
162 representing whether the evaluation found a candidate, or the reason
163 why one is not found. If a candidate is found, *detail* will be the
164 candidate's version string; if one is not found, it contains the
165 reason the link fails to qualify.
179 if ext
not in SUPPORTED_EXTENSIONS:
182 f
"unsupported archive format: {ext}",
184 if "binary" not in self.
_formats and ext == WHEEL_EXTENSION:
185 reason = f
"No binaries permitted for {self.project_name}"
187 if "macosx10" in link.path and ext ==
".zip":
189 if ext == WHEEL_EXTENSION:
192 except InvalidWheelFilename:
195 "invalid wheel filename",
198 reason = f
"wrong project name (not {self.project_name})"
207 f
"none of the wheel's tags ({file_tags}) are compatible "
208 f
"(run pip debug --verbose to show compatible tags)"
215 if "source" not in self.
_formats and ext != WHEEL_EXTENSION:
216 reason = f
"No sources permitted for {self.project_name}"
225 reason = f
"Missing project version for {self.project_name}"
235 "Python version is incorrect",
243 if not supports_python:
244 reason = f
"{version} Requires-Python {link.requires_python}"
247 logger.debug(
"Found link %s, version: %s", link, version)
253 candidates: List[InstallationCandidate],
254 hashes: Optional[Hashes],
256) -> List[InstallationCandidate]:
258 Filter out candidates whose hashes aren't allowed, and return a new
261 If at least one candidate has an allowed hash, then all candidates with
262 either an allowed hash or no hash specified are returned. Otherwise,
263 the given candidates are returned.
265 Including the candidates with no hash specified when there is a match
266 allows a warning to be logged if there is a more preferred candidate
267 with no hash specified. Returning all candidates in the case of no
268 matches lets pip report the hash of the candidate that would otherwise
269 have been installed (e.g. permitting the user to more easily update
270 their requirements file with the desired hash).
274 "Given no hashes to check %s links for project %r: "
275 "discarding no candidates",
280 return list(candidates)
282 matches_or_no_digest = []
286 for candidate
in candidates:
299 filtered = matches_or_no_digest
302 filtered = list(candidates)
304 if len(filtered) ==
len(candidates):
305 discard_message =
"discarding no candidates"
307 discard_message =
"discarding {} non-matches:\n {}".format(
313 "Checked %s links for project %r against %s hashes "
314 "(%s matches, %s no digest): %s",
319 len(matches_or_no_digest) - match_count,
329 Encapsulates some of the preferences for filtering and sorting
330 InstallationCandidate objects.
335 prefer_binary: bool =
False,
336 allow_all_prereleases: bool =
False,
339 :param allow_all_prereleases: Whether to allow all pre-releases.
346 """A collection of candidates, returned by `PackageFinder.find_best_candidate`.
348 This class is only intended to be instantiated by CandidateEvaluator's
349 `compute_best_candidate()` method.
354 candidates: List[InstallationCandidate],
355 applicable_candidates: List[InstallationCandidate],
356 best_candidate: Optional[InstallationCandidate],
359 :param candidates: A sequence of all available candidates found.
360 :param applicable_candidates: The applicable candidates.
361 :param best_candidate: The most preferred candidate found, or None
362 if no applicable candidates were found.
364 assert set(applicable_candidates) <= set(candidates)
366 if best_candidate
is None:
367 assert not applicable_candidates
369 assert best_candidate
in applicable_candidates
376 def iter_all(self) -> Iterable[InstallationCandidate]:
377 """Iterate through all candidates."""
381 """Iterate through the applicable candidates."""
388 Responsible for filtering and sorting candidates for installation based
389 on what tags are valid.
396 target_python: Optional[TargetPython] =
None,
397 prefer_binary: bool =
False,
398 allow_all_prereleases: bool =
False,
400 hashes: Optional[Hashes] =
None,
401 ) ->
"CandidateEvaluator":
402 """Create a CandidateEvaluator object.
404 :param target_python: The target Python interpreter to use when
405 checking compatibility. If None (the default), a TargetPython
406 object will be constructed from the running Python.
407 :param specifier: An optional object implementing `filter`
408 (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable
410 :param hashes: An optional collection of allowed hashes.
412 if target_python
is None:
414 if specifier
is None:
420 project_name=project_name,
421 supported_tags=supported_tags,
423 prefer_binary=prefer_binary,
424 allow_all_prereleases=allow_all_prereleases,
431 supported_tags: List[Tag],
433 prefer_binary: bool =
False,
434 allow_all_prereleases: bool =
False,
435 hashes: Optional[Hashes] =
None,
438 :param supported_tags: The PEP 425 tags supported by the target
439 Python in order of preference (most preferred first).
451 tag: idx
for idx, tag
in enumerate(supported_tags)
456 candidates: List[InstallationCandidate],
457 ) -> List[InstallationCandidate]:
459 Return the applicable candidates from a list of candidates.
475 prereleases=allow_prereleases,
480 applicable_candidates = [c
for c
in candidates
if str(
c.version)
in versions]
483 candidates=applicable_candidates,
488 return sorted(filtered_applicable_candidates, key=self.
_sort_key)
490 def _sort_key(self, candidate: InstallationCandidate) -> CandidateSortingKey:
492 Function to pass as the `key` argument to a call to sorted() to sort
493 InstallationCandidates by preference.
495 Returns a tuple such that tuples sorting as greater using Python's
496 default comparison operator are more preferred.
498 The preference is as follows:
500 First and foremost, candidates with allowed (matching) hashes are
501 always preferred over candidates without matching hashes. This is
502 because e.g. if the only candidate with an allowed hash is yanked,
503 we still want to use that candidate.
505 Second, excepting hash considerations, candidates that have been
506 yanked (in the sense of PEP 592) are always less preferred than
507 candidates that haven't been yanked. Then:
509 If not finding wheels, they are sorted by version only.
510 If finding wheels, then the sort order is by version, then:
512 2. wheels ordered via Wheel.support_index_min(self._supported_tags)
514 If prefer_binary was set, then all wheels are sorted above sources.
516 Note: it was considered to embed this logic into the Link
517 comparison operators, but then different sdist links
518 with the same version, would have to be considered equal
521 support_num =
len(valid_tags)
522 build_tag: BuildTag = ()
523 binary_preference = 0
536 "{} is not a supported wheel for this platform. It "
540 binary_preference = 1
543 assert match
is not None,
"guaranteed by filename validation"
545 build_tag = (int(build_tag_groups[0]), build_tag_groups[1])
561 candidates: List[InstallationCandidate],
562 ) -> Optional[InstallationCandidate]:
564 Return the best candidate per the instance's sort order, or None if
565 no candidate is acceptable.
569 best_candidate = max(candidates, key=self.
_sort_key)
570 return best_candidate
574 candidates: List[InstallationCandidate],
575 ) -> BestCandidateResult:
577 Compute and return a `BestCandidateResult` instance.
585 applicable_candidates=applicable_candidates,
586 best_candidate=best_candidate,
591 """This finds packages.
593 This is meant to match easy_install's technique for looking for
594 packages, by reading pages and looking for appropriate links.
599 link_collector: LinkCollector,
600 target_python: TargetPython,
602 format_control: Optional[FormatControl] =
None,
603 candidate_prefs: Optional[CandidatePreferences] =
None,
604 ignore_requires_python: Optional[bool] =
None,
607 This constructor is primarily meant to be used by the create() class
608 method and from tests.
610 :param format_control: A FormatControl object, used to control
611 the selection of source packages / binary packages when consulting
613 :param candidate_prefs: Options to use when creating a
614 CandidateEvaluator object.
616 if candidate_prefs
is None:
619 format_control = format_control
or FormatControl(set(), set())
630 self._logged_links: Set[Tuple[Link, LinkType, str]] = set()
639 link_collector: LinkCollector,
640 selection_prefs: SelectionPreferences,
641 target_python: Optional[TargetPython] =
None,
642 ) ->
"PackageFinder":
643 """Create a PackageFinder.
645 :param selection_prefs: The candidate selection preferences, as a
646 SelectionPreferences object.
647 :param target_python: The target Python interpreter to use when
648 checking compatibility. If None (the default), a TargetPython
649 object will be constructed from the running Python.
651 if target_python
is None:
660 candidate_prefs=candidate_prefs,
661 link_collector=link_collector,
662 target_python=target_python,
691 yield build_netloc(*host_port)
710 for _, result, detail
in self._logged_links
713 return sorted(reasons)
716 canonical_name = canonicalize_name(project_name)
720 project_name=project_name,
721 canonical_name=canonical_name,
730 Returns elements of links in order, non-egg links first, egg links
731 second, while eliminating duplicates
733 eggs, no_eggs = [], []
734 seen: Set[Link] = set()
742 return no_eggs + eggs
745 entry = (link, result, detail)
746 if entry
not in self._logged_links:
750 self._logged_links.add(entry)
753 self, link_evaluator: LinkEvaluator, link: Link
754 ) -> Optional[InstallationCandidate]:
756 If the link is a candidate for install, convert it to an
757 InstallationCandidate and return it. Otherwise, return None.
771 self, link_evaluator: LinkEvaluator, links: Iterable[Link]
772 ) -> List[InstallationCandidate]:
774 Convert links that are candidates to InstallationCandidate objects.
779 if candidate
is not None:
785 self, project_url: Link, link_evaluator: LinkEvaluator
786 ) -> List[InstallationCandidate]:
788 "Fetching project page and analyzing links: %s",
792 if index_response
is None:
795 page_links = list(parse_links(index_response))
805 @functools.lru_cache(maxsize=None)
807 """Find all available InstallationCandidate for project_name
809 This checks index_urls and find_links.
810 All versions found are returned as an InstallationCandidate list.
812 See LinkEvaluator.evaluate_link() for details on which files
818 project_name=project_name,
821 link_evaluator=link_evaluator,
827 for sources
in collected_sources
828 for source
in sources
829 if source
is not None
831 page_candidates = list(page_candidates_it)
835 for sources
in collected_sources
836 for source
in sources
837 if source
is not None
841 sorted(file_links_it, reverse=
True),
846 for candidate
in file_candidates:
856 return file_candidates + page_candidates
862 hashes: Optional[Hashes] =
None,
863 ) -> CandidateEvaluator:
864 """Create a CandidateEvaluator object to use."""
867 project_name=project_name,
875 @functools.lru_cache(maxsize=None)
880 hashes: Optional[Hashes] =
None,
881 ) -> BestCandidateResult:
882 """Find matches for the given project and specifier.
884 :param specifier: An optional object implementing `filter`
885 (e.g. `packaging.specifiers.SpecifierSet`) to filter applicable
888 :return: A `BestCandidateResult` instance.
892 project_name=project_name,
899 self, req: InstallRequirement, upgrade: bool
900 ) -> Optional[InstallationCandidate]:
901 """Try to find a Link matching req
903 Expects req, an InstallRequirement and upgrade, a boolean
904 Returns a InstallationCandidate if found,
905 Raises DistributionNotFound or BestVersionAlreadyInstalled otherwise
915 installed_version: Optional[_BaseVersion] =
None
934 if installed_version
is None and best_candidate
is None:
936 "Could not find a version that satisfies the requirement %s "
937 "(from versions: %s)",
943 "No matching distribution found for {}".format(req)
947 candidate: Optional[InstallationCandidate],
948 ) ->
"TypeGuard[InstallationCandidate]":
949 if installed_version
is None:
951 if best_candidate
is None:
955 if not upgrade
and installed_version
is not None:
958 "Existing installed version (%s) satisfies requirement "
959 "(most up-to-date version is %s)",
965 "Existing installed version (%s) is most up-to-date and "
966 "satisfies requirement",
973 "Using version %s (newest of versions: %s)",
977 return best_candidate
981 "Installed version (%s) is most up-to-date (past versions: %s)",
985 raise BestVersionAlreadyInstalled
989 """Find the separator's index based on the package's canonical name.
991 :param fragment: A <package>+<version> filename "fragment" (stem) or
993 :param canonical_name: The package's canonical name.
995 This function is needed since the canonicalized name does not necessarily
996 have the same length as the egg info's name part. An example::
998 >>> fragment = 'foo__bar-1.0'
999 >>> canonical_name = 'foo-bar'
1000 >>> _find_name_version_sep(fragment, canonical_name)
1009 if canonicalize_name(fragment[:i]) == canonical_name:
1011 raise ValueError(f
"{fragment} does not match {canonical_name}")
1015 """Parse the version string from a <package>+<version> filename
1016 "fragment" (stem) or egg fragment.
1018 :param fragment: The string to parse. E.g. foo-2.1
1019 :param canonical_name: The canonicalized name of the package this
1026 version = fragment[version_start:]
Iterable[InstallationCandidate] iter_applicable(self)
None __init__(self, List[InstallationCandidate] candidates, List[InstallationCandidate] applicable_candidates, Optional[InstallationCandidate] best_candidate)
Iterable[InstallationCandidate] iter_all(self)
"CandidateEvaluator" create(cls, str project_name, Optional[TargetPython] target_python=None, bool prefer_binary=False, bool allow_all_prereleases=False, Optional[specifiers.BaseSpecifier] specifier=None, Optional[Hashes] hashes=None)
List[InstallationCandidate] get_applicable_candidates(self, List[InstallationCandidate] candidates)
BestCandidateResult compute_best_candidate(self, List[InstallationCandidate] candidates)
CandidateSortingKey _sort_key(self, InstallationCandidate candidate)
None __init__(self, str project_name, List[Tag] supported_tags, specifiers.BaseSpecifier specifier, bool prefer_binary=False, bool allow_all_prereleases=False, Optional[Hashes] hashes=None)
Optional[InstallationCandidate] sort_best_candidate(self, List[InstallationCandidate] candidates)
None __init__(self, bool prefer_binary=False, bool allow_all_prereleases=False)
Tuple[LinkType, str] evaluate_link(self, Link link)
None __init__(self, str project_name, str canonical_name, FrozenSet[str] formats, TargetPython target_python, bool allow_yanked, Optional[bool] ignore_requires_python=None)
CandidateEvaluator make_candidate_evaluator(self, str project_name, Optional[specifiers.BaseSpecifier] specifier=None, Optional[Hashes] hashes=None)
SearchScope search_scope(self)
BestCandidateResult find_best_candidate(self, str project_name, Optional[specifiers.BaseSpecifier] specifier=None, Optional[Hashes] hashes=None)
List[str] index_urls(self)
TargetPython target_python(self)
List[Link] _sort_links(self, Iterable[Link] links)
Optional[InstallationCandidate] find_requirement(self, InstallRequirement req, bool upgrade)
List[str] requires_python_skipped_reasons(self)
LinkEvaluator make_link_evaluator(self, str project_name)
None _log_skipped_link(self, Link link, LinkType result, str detail)
bool allow_all_prereleases(self)
None __init__(self, LinkCollector link_collector, TargetPython target_python, bool allow_yanked, Optional[FormatControl] format_control=None, Optional[CandidatePreferences] candidate_prefs=None, Optional[bool] ignore_requires_python=None)
None set_prefer_binary(self)
List[str] find_links(self)
List[InstallationCandidate] process_project_url(self, Link project_url, LinkEvaluator link_evaluator)
Iterable[str] trusted_hosts(self)
List[InstallationCandidate] evaluate_links(self, LinkEvaluator link_evaluator, Iterable[Link] links)
Optional[InstallationCandidate] get_install_candidate(self, LinkEvaluator link_evaluator, Link link)
None search_scope(self, SearchScope search_scope)
List[InstallationCandidate] find_all_candidates(self, str project_name)
None set_allow_all_prereleases(self)
"PackageFinder" create(cls, LinkCollector link_collector, SelectionPreferences selection_prefs, Optional[TargetPython] target_python=None)
Optional[str] _extract_version_from_fragment(str fragment, str canonical_name)
int _find_name_version_sep(str fragment, str canonical_name)
List[InstallationCandidate] filter_unallowed_hashes(List[InstallationCandidate] candidates, Optional[Hashes] hashes, str project_name)
bool _check_link_requires_python(Link link, Tuple[int, int, int] version_info, bool ignore_requires_python=False)