provider.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. from pip._vendor.packaging.specifiers import SpecifierSet
  2. from pip._vendor.resolvelib.providers import AbstractProvider
  3. from pip._internal.utils.typing import MYPY_CHECK_RUNNING
  4. if MYPY_CHECK_RUNNING:
  5. from typing import (
  6. Any,
  7. Dict,
  8. Iterable,
  9. Optional,
  10. Sequence,
  11. Set,
  12. Tuple,
  13. Union,
  14. )
  15. from .base import Requirement, Candidate
  16. from .factory import Factory
  17. # Notes on the relationship between the provider, the factory, and the
  18. # candidate and requirement classes.
  19. #
  20. # The provider is a direct implementation of the resolvelib class. Its role
  21. # is to deliver the API that resolvelib expects.
  22. #
  23. # Rather than work with completely abstract "requirement" and "candidate"
  24. # concepts as resolvelib does, pip has concrete classes implementing these two
  25. # ideas. The API of Requirement and Candidate objects are defined in the base
  26. # classes, but essentially map fairly directly to the equivalent provider
  27. # methods. In particular, `find_matches` and `is_satisfied_by` are
  28. # requirement methods, and `get_dependencies` is a candidate method.
  29. #
  30. # The factory is the interface to pip's internal mechanisms. It is stateless,
  31. # and is created by the resolver and held as a property of the provider. It is
  32. # responsible for creating Requirement and Candidate objects, and provides
  33. # services to those objects (access to pip's finder and preparer).
  34. class PipProvider(AbstractProvider):
  35. def __init__(
  36. self,
  37. factory, # type: Factory
  38. constraints, # type: Dict[str, SpecifierSet]
  39. ignore_dependencies, # type: bool
  40. upgrade_strategy, # type: str
  41. user_requested, # type: Set[str]
  42. ):
  43. # type: (...) -> None
  44. self._factory = factory
  45. self._constraints = constraints
  46. self._ignore_dependencies = ignore_dependencies
  47. self._upgrade_strategy = upgrade_strategy
  48. self.user_requested = user_requested
  49. def _sort_matches(self, matches):
  50. # type: (Iterable[Candidate]) -> Sequence[Candidate]
  51. # The requirement is responsible for returning a sequence of potential
  52. # candidates, one per version. The provider handles the logic of
  53. # deciding the order in which these candidates should be passed to
  54. # the resolver.
  55. # The `matches` argument is a sequence of candidates, one per version,
  56. # which are potential options to be installed. The requirement will
  57. # have already sorted out whether to give us an already-installed
  58. # candidate or a version from PyPI (i.e., it will deal with options
  59. # like --force-reinstall and --ignore-installed).
  60. # We now work out the correct order.
  61. #
  62. # 1. If no other considerations apply, later versions take priority.
  63. # 2. An already installed distribution is preferred over any other,
  64. # unless the user has requested an upgrade.
  65. # Upgrades are allowed when:
  66. # * The --upgrade flag is set, and
  67. # - The project was specified on the command line, or
  68. # - The project is a dependency and the "eager" upgrade strategy
  69. # was requested.
  70. def _eligible_for_upgrade(name):
  71. # type: (str) -> bool
  72. """Are upgrades allowed for this project?
  73. This checks the upgrade strategy, and whether the project was one
  74. that the user specified in the command line, in order to decide
  75. whether we should upgrade if there's a newer version available.
  76. (Note that we don't need access to the `--upgrade` flag, because
  77. an upgrade strategy of "to-satisfy-only" means that `--upgrade`
  78. was not specified).
  79. """
  80. if self._upgrade_strategy == "eager":
  81. return True
  82. elif self._upgrade_strategy == "only-if-needed":
  83. return (name in self.user_requested)
  84. return False
  85. def sort_key(c):
  86. # type: (Candidate) -> int
  87. """Return a sort key for the matches.
  88. The highest priority should be given to installed candidates that
  89. are not eligible for upgrade. We use the integer value in the first
  90. part of the key to sort these before other candidates.
  91. We only pull the installed candidate to the bottom (i.e. most
  92. preferred), but otherwise keep the ordering returned by the
  93. requirement. The requirement is responsible for returning a list
  94. otherwise sorted for the resolver, taking account for versions
  95. and binary preferences as specified by the user.
  96. """
  97. if c.is_installed and not _eligible_for_upgrade(c.name):
  98. return 1
  99. return 0
  100. return sorted(matches, key=sort_key)
  101. def identify(self, dependency):
  102. # type: (Union[Requirement, Candidate]) -> str
  103. return dependency.name
  104. def get_preference(
  105. self,
  106. resolution, # type: Optional[Candidate]
  107. candidates, # type: Sequence[Candidate]
  108. information # type: Sequence[Tuple[Requirement, Candidate]]
  109. ):
  110. # type: (...) -> Any
  111. # Use the "usual" value for now
  112. return len(candidates)
  113. def find_matches(self, requirements):
  114. # type: (Sequence[Requirement]) -> Iterable[Candidate]
  115. if not requirements:
  116. return []
  117. constraint = self._constraints.get(
  118. requirements[0].name, SpecifierSet(),
  119. )
  120. candidates = self._factory.find_candidates(requirements, constraint)
  121. return reversed(self._sort_matches(candidates))
  122. def is_satisfied_by(self, requirement, candidate):
  123. # type: (Requirement, Candidate) -> bool
  124. return requirement.is_satisfied_by(candidate)
  125. def get_dependencies(self, candidate):
  126. # type: (Candidate) -> Sequence[Requirement]
  127. with_requires = not self._ignore_dependencies
  128. return [
  129. r
  130. for r in candidate.iter_dependencies(with_requires)
  131. if r is not None
  132. ]