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