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