provider.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from pip._vendor.resolvelib.providers import AbstractProvider
  2. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  3. from .base import Constraint
  4. if MYPY_CHECK_RUNNING:
  5. from typing import Any, Dict, Iterable, Optional, Sequence, Set, Tuple, Union
  6. from .base import Candidate, Requirement
  7. from .factory import Factory
  8. # Notes on the relationship between the provider, the factory, and the
  9. # candidate and requirement classes.
  10. #
  11. # The provider is a direct implementation of the resolvelib class. Its role
  12. # is to deliver the API that resolvelib expects.
  13. #
  14. # Rather than work with completely abstract "requirement" and "candidate"
  15. # concepts as resolvelib does, pip has concrete classes implementing these two
  16. # ideas. The API of Requirement and Candidate objects are defined in the base
  17. # classes, but essentially map fairly directly to the equivalent provider
  18. # methods. In particular, `find_matches` and `is_satisfied_by` are
  19. # requirement methods, and `get_dependencies` is a candidate method.
  20. #
  21. # The factory is the interface to pip's internal mechanisms. It is stateless,
  22. # and is created by the resolver and held as a property of the provider. It is
  23. # responsible for creating Requirement and Candidate objects, and provides
  24. # services to those objects (access to pip's finder and preparer).
  25. class PipProvider(AbstractProvider):
  26. """Pip's provider implementation for resolvelib.
  27. :params constraints: A mapping of constraints specified by the user. Keys
  28. are canonicalized project names.
  29. :params ignore_dependencies: Whether the user specified ``--no-deps``.
  30. :params upgrade_strategy: The user-specified upgrade strategy.
  31. :params user_requested: A set of canonicalized package names that the user
  32. supplied for pip to install/upgrade.
  33. """
  34. def __init__(
  35. self,
  36. factory, # type: Factory
  37. constraints, # type: Dict[str, Constraint]
  38. ignore_dependencies, # type: bool
  39. upgrade_strategy, # type: str
  40. user_requested, # type: Set[str]
  41. ):
  42. # type: (...) -> None
  43. self._factory = factory
  44. self._constraints = constraints
  45. self._ignore_dependencies = ignore_dependencies
  46. self._upgrade_strategy = upgrade_strategy
  47. self._user_requested = user_requested
  48. def identify(self, dependency):
  49. # type: (Union[Requirement, Candidate]) -> str
  50. return dependency.name
  51. def get_preference(
  52. self,
  53. resolution, # type: Optional[Candidate]
  54. candidates, # type: Sequence[Candidate]
  55. information # type: Sequence[Tuple[Requirement, Candidate]]
  56. ):
  57. # type: (...) -> Any
  58. """Produce a sort key for given requirement based on preference.
  59. The lower the return value is, the more preferred this group of
  60. arguments is.
  61. Currently pip considers the followings in order:
  62. * Prefer if any of the known requirements points to an explicit URL.
  63. * If equal, prefer if any requirements contain ``===`` and ``==``.
  64. * If equal, prefer if requirements include version constraints, e.g.
  65. ``>=`` and ``<``.
  66. * If equal, prefer user-specified (non-transitive) requirements.
  67. * If equal, order alphabetically for consistency (helps debuggability).
  68. """
  69. def _get_restrictive_rating(requirements):
  70. # type: (Iterable[Requirement]) -> int
  71. """Rate how restrictive a set of requirements are.
  72. ``Requirement.get_candidate_lookup()`` returns a 2-tuple for
  73. lookup. The first element is ``Optional[Candidate]`` and the
  74. second ``Optional[InstallRequirement]``.
  75. * If the requirement is an explicit one, the explicitly-required
  76. candidate is returned as the first element.
  77. * If the requirement is based on a PEP 508 specifier, the backing
  78. ``InstallRequirement`` is returned as the second element.
  79. We use the first element to check whether there is an explicit
  80. requirement, and the second for equality operator.
  81. """
  82. lookups = (r.get_candidate_lookup() for r in requirements)
  83. cands, ireqs = zip(*lookups)
  84. if any(cand is not None for cand in cands):
  85. return 0
  86. spec_sets = (ireq.specifier for ireq in ireqs if ireq)
  87. operators = [
  88. specifier.operator
  89. for spec_set in spec_sets
  90. for specifier in spec_set
  91. ]
  92. if any(op in ("==", "===") for op in operators):
  93. return 1
  94. if operators:
  95. return 2
  96. # A "bare" requirement without any version requirements.
  97. return 3
  98. restrictive = _get_restrictive_rating(req for req, _ in information)
  99. transitive = all(parent is not None for _, parent in information)
  100. key = next(iter(candidates)).name if candidates else ""
  101. # HACK: Setuptools have a very long and solid backward compatibility
  102. # track record, and extremely few projects would request a narrow,
  103. # non-recent version range of it since that would break a lot things.
  104. # (Most projects specify it only to request for an installer feature,
  105. # which does not work, but that's another topic.) Intentionally
  106. # delaying Setuptools helps reduce branches the resolver has to check.
  107. # This serves as a temporary fix for issues like "apache-airlfow[all]"
  108. # while we work on "proper" branch pruning techniques.
  109. delay_this = (key == "setuptools")
  110. return (delay_this, restrictive, transitive, key)
  111. def find_matches(self, requirements):
  112. # type: (Sequence[Requirement]) -> Iterable[Candidate]
  113. if not requirements:
  114. return []
  115. name = requirements[0].project_name
  116. def _eligible_for_upgrade(name):
  117. # type: (str) -> bool
  118. """Are upgrades allowed for this project?
  119. This checks the upgrade strategy, and whether the project was one
  120. that the user specified in the command line, in order to decide
  121. whether we should upgrade if there's a newer version available.
  122. (Note that we don't need access to the `--upgrade` flag, because
  123. an upgrade strategy of "to-satisfy-only" means that `--upgrade`
  124. was not specified).
  125. """
  126. if self._upgrade_strategy == "eager":
  127. return True
  128. elif self._upgrade_strategy == "only-if-needed":
  129. return (name in self._user_requested)
  130. return False
  131. return self._factory.find_candidates(
  132. requirements,
  133. constraint=self._constraints.get(name, Constraint.empty()),
  134. prefers_installed=(not _eligible_for_upgrade(name)),
  135. )
  136. def is_satisfied_by(self, requirement, candidate):
  137. # type: (Requirement, Candidate) -> bool
  138. return requirement.is_satisfied_by(candidate)
  139. def get_dependencies(self, candidate):
  140. # type: (Candidate) -> Sequence[Requirement]
  141. with_requires = not self._ignore_dependencies
  142. return [
  143. r
  144. for r in candidate.iter_dependencies(with_requires)
  145. if r is not None
  146. ]