requirements.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. from pip._vendor.packaging.specifiers import SpecifierSet
  2. from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
  3. from pip._internal.req.req_install import InstallRequirement
  4. from .base import Candidate, CandidateLookup, Requirement, format_name
  5. class ExplicitRequirement(Requirement):
  6. def __init__(self, candidate):
  7. # type: (Candidate) -> None
  8. self.candidate = candidate
  9. def __str__(self):
  10. # type: () -> str
  11. return str(self.candidate)
  12. def __repr__(self):
  13. # type: () -> str
  14. return "{class_name}({candidate!r})".format(
  15. class_name=self.__class__.__name__,
  16. candidate=self.candidate,
  17. )
  18. @property
  19. def project_name(self):
  20. # type: () -> NormalizedName
  21. # No need to canonicalise - the candidate did this
  22. return self.candidate.project_name
  23. @property
  24. def name(self):
  25. # type: () -> str
  26. # No need to canonicalise - the candidate did this
  27. return self.candidate.name
  28. def format_for_error(self):
  29. # type: () -> str
  30. return self.candidate.format_for_error()
  31. def get_candidate_lookup(self):
  32. # type: () -> CandidateLookup
  33. return self.candidate, None
  34. def is_satisfied_by(self, candidate):
  35. # type: (Candidate) -> bool
  36. return candidate == self.candidate
  37. class SpecifierRequirement(Requirement):
  38. def __init__(self, ireq):
  39. # type: (InstallRequirement) -> None
  40. assert ireq.link is None, "This is a link, not a specifier"
  41. self._ireq = ireq
  42. self._extras = frozenset(ireq.extras)
  43. def __str__(self):
  44. # type: () -> str
  45. return str(self._ireq.req)
  46. def __repr__(self):
  47. # type: () -> str
  48. return "{class_name}({requirement!r})".format(
  49. class_name=self.__class__.__name__,
  50. requirement=str(self._ireq.req),
  51. )
  52. @property
  53. def project_name(self):
  54. # type: () -> NormalizedName
  55. assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
  56. return canonicalize_name(self._ireq.req.name)
  57. @property
  58. def name(self):
  59. # type: () -> str
  60. return format_name(self.project_name, self._extras)
  61. def format_for_error(self):
  62. # type: () -> str
  63. # Convert comma-separated specifiers into "A, B, ..., F and G"
  64. # This makes the specifier a bit more "human readable", without
  65. # risking a change in meaning. (Hopefully! Not all edge cases have
  66. # been checked)
  67. parts = [s.strip() for s in str(self).split(",")]
  68. if len(parts) == 0:
  69. return ""
  70. elif len(parts) == 1:
  71. return parts[0]
  72. return ", ".join(parts[:-1]) + " and " + parts[-1]
  73. def get_candidate_lookup(self):
  74. # type: () -> CandidateLookup
  75. return None, self._ireq
  76. def is_satisfied_by(self, candidate):
  77. # type: (Candidate) -> bool
  78. assert candidate.name == self.name, (
  79. f"Internal issue: Candidate is not for this requirement "
  80. f"{candidate.name} vs {self.name}"
  81. )
  82. # We can safely always allow prereleases here since PackageFinder
  83. # already implements the prerelease logic, and would have filtered out
  84. # prerelease candidates if the user does not expect them.
  85. assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
  86. spec = self._ireq.req.specifier
  87. return spec.contains(candidate.version, prereleases=True)
  88. class RequiresPythonRequirement(Requirement):
  89. """A requirement representing Requires-Python metadata."""
  90. def __init__(self, specifier, match):
  91. # type: (SpecifierSet, Candidate) -> None
  92. self.specifier = specifier
  93. self._candidate = match
  94. def __str__(self):
  95. # type: () -> str
  96. return f"Python {self.specifier}"
  97. def __repr__(self):
  98. # type: () -> str
  99. return "{class_name}({specifier!r})".format(
  100. class_name=self.__class__.__name__,
  101. specifier=str(self.specifier),
  102. )
  103. @property
  104. def project_name(self):
  105. # type: () -> NormalizedName
  106. return self._candidate.project_name
  107. @property
  108. def name(self):
  109. # type: () -> str
  110. return self._candidate.name
  111. def format_for_error(self):
  112. # type: () -> str
  113. return str(self)
  114. def get_candidate_lookup(self):
  115. # type: () -> CandidateLookup
  116. if self.specifier.contains(self._candidate.version, prereleases=True):
  117. return self._candidate, None
  118. return None, None
  119. def is_satisfied_by(self, candidate):
  120. # type: (Candidate) -> bool
  121. assert candidate.name == self._candidate.name, "Not Python candidate"
  122. # We can safely always allow prereleases here since PackageFinder
  123. # already implements the prerelease logic, and would have filtered out
  124. # prerelease candidates if the user does not expect them.
  125. return self.specifier.contains(candidate.version, prereleases=True)
  126. class UnsatisfiableRequirement(Requirement):
  127. """A requirement that cannot be satisfied."""
  128. def __init__(self, name):
  129. # type: (NormalizedName) -> None
  130. self._name = name
  131. def __str__(self):
  132. # type: () -> str
  133. return f"{self._name} (unavailable)"
  134. def __repr__(self):
  135. # type: () -> str
  136. return "{class_name}({name!r})".format(
  137. class_name=self.__class__.__name__,
  138. name=str(self._name),
  139. )
  140. @property
  141. def project_name(self):
  142. # type: () -> NormalizedName
  143. return self._name
  144. @property
  145. def name(self):
  146. # type: () -> str
  147. return self._name
  148. def format_for_error(self):
  149. # type: () -> str
  150. return str(self)
  151. def get_candidate_lookup(self):
  152. # type: () -> CandidateLookup
  153. return None, None
  154. def is_satisfied_by(self, candidate):
  155. # type: (Candidate) -> bool
  156. return False