comparison planemo/lib/python3.7/site-packages/pip/_internal/exceptions.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 """Exceptions used throughout package"""
2 from __future__ import absolute_import
3
4 from itertools import chain, groupby, repeat
5
6 from pip._vendor.six import iteritems
7
8 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
9
10 if MYPY_CHECK_RUNNING:
11 from typing import Optional
12 from pip._vendor.pkg_resources import Distribution
13 from pip._internal.req.req_install import InstallRequirement
14
15
16 class PipError(Exception):
17 """Base pip exception"""
18
19
20 class ConfigurationError(PipError):
21 """General exception in configuration"""
22
23
24 class InstallationError(PipError):
25 """General exception during installation"""
26
27
28 class UninstallationError(PipError):
29 """General exception during uninstallation"""
30
31
32 class NoneMetadataError(PipError):
33 """
34 Raised when accessing "METADATA" or "PKG-INFO" metadata for a
35 pip._vendor.pkg_resources.Distribution object and
36 `dist.has_metadata('METADATA')` returns True but
37 `dist.get_metadata('METADATA')` returns None (and similarly for
38 "PKG-INFO").
39 """
40
41 def __init__(self, dist, metadata_name):
42 # type: (Distribution, str) -> None
43 """
44 :param dist: A Distribution object.
45 :param metadata_name: The name of the metadata being accessed
46 (can be "METADATA" or "PKG-INFO").
47 """
48 self.dist = dist
49 self.metadata_name = metadata_name
50
51 def __str__(self):
52 # type: () -> str
53 # Use `dist` in the error message because its stringification
54 # includes more information, like the version and location.
55 return (
56 'None {} metadata found for distribution: {}'.format(
57 self.metadata_name, self.dist,
58 )
59 )
60
61
62 class DistributionNotFound(InstallationError):
63 """Raised when a distribution cannot be found to satisfy a requirement"""
64
65
66 class RequirementsFileParseError(InstallationError):
67 """Raised when a general error occurs parsing a requirements file line."""
68
69
70 class BestVersionAlreadyInstalled(PipError):
71 """Raised when the most up-to-date version of a package is already
72 installed."""
73
74
75 class BadCommand(PipError):
76 """Raised when virtualenv or a command is not found"""
77
78
79 class CommandError(PipError):
80 """Raised when there is an error in command-line arguments"""
81
82
83 class PreviousBuildDirError(PipError):
84 """Raised when there's a previous conflicting build directory"""
85
86
87 class InvalidWheelFilename(InstallationError):
88 """Invalid wheel filename."""
89
90
91 class UnsupportedWheel(InstallationError):
92 """Unsupported wheel."""
93
94
95 class HashErrors(InstallationError):
96 """Multiple HashError instances rolled into one for reporting"""
97
98 def __init__(self):
99 self.errors = []
100
101 def append(self, error):
102 self.errors.append(error)
103
104 def __str__(self):
105 lines = []
106 self.errors.sort(key=lambda e: e.order)
107 for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__):
108 lines.append(cls.head)
109 lines.extend(e.body() for e in errors_of_cls)
110 if lines:
111 return '\n'.join(lines)
112
113 def __nonzero__(self):
114 return bool(self.errors)
115
116 def __bool__(self):
117 return self.__nonzero__()
118
119
120 class HashError(InstallationError):
121 """
122 A failure to verify a package against known-good hashes
123
124 :cvar order: An int sorting hash exception classes by difficulty of
125 recovery (lower being harder), so the user doesn't bother fretting
126 about unpinned packages when he has deeper issues, like VCS
127 dependencies, to deal with. Also keeps error reports in a
128 deterministic order.
129 :cvar head: A section heading for display above potentially many
130 exceptions of this kind
131 :ivar req: The InstallRequirement that triggered this error. This is
132 pasted on after the exception is instantiated, because it's not
133 typically available earlier.
134
135 """
136 req = None # type: Optional[InstallRequirement]
137 head = ''
138
139 def body(self):
140 """Return a summary of me for display under the heading.
141
142 This default implementation simply prints a description of the
143 triggering requirement.
144
145 :param req: The InstallRequirement that provoked this error, with
146 populate_link() having already been called
147
148 """
149 return ' %s' % self._requirement_name()
150
151 def __str__(self):
152 return '%s\n%s' % (self.head, self.body())
153
154 def _requirement_name(self):
155 """Return a description of the requirement that triggered me.
156
157 This default implementation returns long description of the req, with
158 line numbers
159
160 """
161 return str(self.req) if self.req else 'unknown package'
162
163
164 class VcsHashUnsupported(HashError):
165 """A hash was provided for a version-control-system-based requirement, but
166 we don't have a method for hashing those."""
167
168 order = 0
169 head = ("Can't verify hashes for these requirements because we don't "
170 "have a way to hash version control repositories:")
171
172
173 class DirectoryUrlHashUnsupported(HashError):
174 """A hash was provided for a version-control-system-based requirement, but
175 we don't have a method for hashing those."""
176
177 order = 1
178 head = ("Can't verify hashes for these file:// requirements because they "
179 "point to directories:")
180
181
182 class HashMissing(HashError):
183 """A hash was needed for a requirement but is absent."""
184
185 order = 2
186 head = ('Hashes are required in --require-hashes mode, but they are '
187 'missing from some requirements. Here is a list of those '
188 'requirements along with the hashes their downloaded archives '
189 'actually had. Add lines like these to your requirements files to '
190 'prevent tampering. (If you did not enable --require-hashes '
191 'manually, note that it turns on automatically when any package '
192 'has a hash.)')
193
194 def __init__(self, gotten_hash):
195 """
196 :param gotten_hash: The hash of the (possibly malicious) archive we
197 just downloaded
198 """
199 self.gotten_hash = gotten_hash
200
201 def body(self):
202 # Dodge circular import.
203 from pip._internal.utils.hashes import FAVORITE_HASH
204
205 package = None
206 if self.req:
207 # In the case of URL-based requirements, display the original URL
208 # seen in the requirements file rather than the package name,
209 # so the output can be directly copied into the requirements file.
210 package = (self.req.original_link if self.req.original_link
211 # In case someone feeds something downright stupid
212 # to InstallRequirement's constructor.
213 else getattr(self.req, 'req', None))
214 return ' %s --hash=%s:%s' % (package or 'unknown package',
215 FAVORITE_HASH,
216 self.gotten_hash)
217
218
219 class HashUnpinned(HashError):
220 """A requirement had a hash specified but was not pinned to a specific
221 version."""
222
223 order = 3
224 head = ('In --require-hashes mode, all requirements must have their '
225 'versions pinned with ==. These do not:')
226
227
228 class HashMismatch(HashError):
229 """
230 Distribution file hash values don't match.
231
232 :ivar package_name: The name of the package that triggered the hash
233 mismatch. Feel free to write to this after the exception is raise to
234 improve its error message.
235
236 """
237 order = 4
238 head = ('THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS '
239 'FILE. If you have updated the package versions, please update '
240 'the hashes. Otherwise, examine the package contents carefully; '
241 'someone may have tampered with them.')
242
243 def __init__(self, allowed, gots):
244 """
245 :param allowed: A dict of algorithm names pointing to lists of allowed
246 hex digests
247 :param gots: A dict of algorithm names pointing to hashes we
248 actually got from the files under suspicion
249 """
250 self.allowed = allowed
251 self.gots = gots
252
253 def body(self):
254 return ' %s:\n%s' % (self._requirement_name(),
255 self._hash_comparison())
256
257 def _hash_comparison(self):
258 """
259 Return a comparison of actual and expected hash values.
260
261 Example::
262
263 Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
264 or 123451234512345123451234512345123451234512345
265 Got bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef
266
267 """
268 def hash_then_or(hash_name):
269 # For now, all the decent hashes have 6-char names, so we can get
270 # away with hard-coding space literals.
271 return chain([hash_name], repeat(' or'))
272
273 lines = []
274 for hash_name, expecteds in iteritems(self.allowed):
275 prefix = hash_then_or(hash_name)
276 lines.extend((' Expected %s %s' % (next(prefix), e))
277 for e in expecteds)
278 lines.append(' Got %s\n' %
279 self.gots[hash_name].hexdigest())
280 prefix = ' or'
281 return '\n'.join(lines)
282
283
284 class UnsupportedPythonVersion(InstallationError):
285 """Unsupported python version according to Requires-Python package
286 metadata."""
287
288
289 class ConfigurationFileCouldNotBeLoaded(ConfigurationError):
290 """When there are errors while loading a configuration file
291 """
292
293 def __init__(self, reason="could not be loaded", fname=None, error=None):
294 super(ConfigurationFileCouldNotBeLoaded, self).__init__(error)
295 self.reason = reason
296 self.fname = fname
297 self.error = error
298
299 def __str__(self):
300 if self.fname is not None:
301 message_part = " in {}.".format(self.fname)
302 else:
303 assert self.error is not None
304 message_part = ".\n{}\n".format(self.error.message)
305 return "Configuration file {}{}".format(self.reason, message_part)