comparison lib/python3.8/site-packages/pip/_internal/exceptions.py @ 0:9e54283cc701 draft

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