f561f1f1e270666ccd74c9d61f78c9c24f5c4c99
[SubU] /
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 ExplicitRequirement(Requirement):
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 SpecifierRequirement(Requirement):
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
68         # Convert comma-separated specifiers into "A, B, ..., F and G"
69         # This makes the specifier a bit more "human readable", without
70         # risking a change in meaning. (Hopefully! Not all edge cases have
71         # been checked)
72         parts = [s.strip() for s in str(self).split(",")]
73         if len(parts) == 0:
74             return ""
75         elif len(parts) == 1:
76             return parts[0]
77
78         return ", ".join(parts[:-1]) + " and " + parts[-1]
79
80     def get_candidate_lookup(self) -> CandidateLookup:
81         return None, self._ireq
82
83     def is_satisfied_by(self, candidate: Candidate) -> bool:
84         assert candidate.name == self.name, (
85             f"Internal issue: Candidate is not for this requirement "
86             f"{candidate.name} vs {self.name}"
87         )
88         # We can safely always allow prereleases here since PackageFinder
89         # already implements the prerelease logic, and would have filtered out
90         # prerelease candidates if the user does not expect them.
91         assert self._ireq.req, "Specifier-backed ireq is always PEP 508"
92         spec = self._ireq.req.specifier
93         return spec.contains(candidate.version, prereleases=True)
94
95
96 class RequiresPythonRequirement(Requirement):
97     """A requirement representing Requires-Python metadata."""
98
99     def __init__(self, specifier: SpecifierSet, match: Candidate) -> None:
100         self.specifier = specifier
101         self._candidate = match
102
103     def __str__(self) -> str:
104         return f"Python {self.specifier}"
105
106     def __repr__(self) -> str:
107         return "{class_name}({specifier!r})".format(
108             class_name=self.__class__.__name__,
109             specifier=str(self.specifier),
110         )
111
112     @property
113     def project_name(self) -> NormalizedName:
114         return self._candidate.project_name
115
116     @property
117     def name(self) -> str:
118         return self._candidate.name
119
120     def format_for_error(self) -> str:
121         return str(self)
122
123     def get_candidate_lookup(self) -> CandidateLookup:
124         if self.specifier.contains(self._candidate.version, prereleases=True):
125             return self._candidate, None
126         return None, None
127
128     def is_satisfied_by(self, candidate: Candidate) -> bool:
129         assert candidate.name == self._candidate.name, "Not Python candidate"
130         # We can safely always allow prereleases here since PackageFinder
131         # already implements the prerelease logic, and would have filtered out
132         # prerelease candidates if the user does not expect them.
133         return self.specifier.contains(candidate.version, prereleases=True)
134
135
136 class UnsatisfiableRequirement(Requirement):
137     """A requirement that cannot be satisfied."""
138
139     def __init__(self, name: NormalizedName) -> None:
140         self._name = name
141
142     def __str__(self) -> str:
143         return f"{self._name} (unavailable)"
144
145     def __repr__(self) -> str:
146         return "{class_name}({name!r})".format(
147             class_name=self.__class__.__name__,
148             name=str(self._name),
149         )
150
151     @property
152     def project_name(self) -> NormalizedName:
153         return self._name
154
155     @property
156     def name(self) -> str:
157         return self._name
158
159     def format_for_error(self) -> str:
160         return str(self)
161
162     def get_candidate_lookup(self) -> CandidateLookup:
163         return None, None
164
165     def is_satisfied_by(self, candidate: Candidate) -> bool:
166         return False