Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_internal/exceptions/__init__.py: 49%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

155 statements  

1"""Package of exceptions used in the pip codebase. 

2 

3This package used to be a single module, so many exceptions were 

4grandfathered into this module. In the future, more groups of exceptions 

5should be moved to their own submodules as needed. 

6 

7This package MUST NOT try to import from anything within `pip._internal` to 

8operate. This is expected to be importable from any/all files within the 

9subpackage and, thus, should not depend on them. 

10""" 

11 

12# NOTE: the pip._internal import restriction may change later if there is an 

13# exception group that does really need internal code to function (e.g. 

14# possibly network). In that case, said group should NOT be re-exported here. 

15 

16from __future__ import annotations 

17 

18import configparser 

19import contextlib 

20import locale 

21import logging 

22import pathlib 

23import sys 

24from collections.abc import Iterator 

25from typing import TYPE_CHECKING 

26 

27from pip._vendor.packaging.requirements import InvalidRequirement 

28from pip._vendor.packaging.version import InvalidVersion 

29from pip._vendor.rich.markup import escape 

30from pip._vendor.rich.text import Text 

31 

32from pip._internal.exceptions._base import ( 

33 DiagnosticPipError, 

34 InstallationError, 

35 PipError, 

36) 

37from pip._internal.exceptions.build import ( 

38 BackendUnavailableError, 

39 BuildDependencyInstallError, 

40 InstallationSubprocessError, 

41 VenvCreationError, 

42 VenvImportError, 

43) 

44from pip._internal.exceptions.hashes import ( 

45 DirectoryUrlHashUnsupported, 

46 HashError, 

47 HashErrors, 

48 HashMismatch, 

49 HashMissing, 

50 HashUnpinned, 

51 VcsHashUnsupported, 

52) 

53from pip._internal.exceptions.network import ( 

54 ConnectionFailedError, 

55 ConnectionTimeoutError, 

56 IncompleteDownloadError, 

57 NetworkConnectionError, 

58 ProxyConnectionError, 

59 SSLMissingError, 

60 SSLVerificationError, 

61) 

62from pip._internal.exceptions.pyproject import ( 

63 InvalidPyProjectBuildRequires, 

64 MissingPyProjectBuildRequires, 

65) 

66from pip._internal.exceptions.uninstall import ( 

67 LegacyDistutilsInstall, 

68 UninstallMissingRecord, 

69) 

70from pip._internal.exceptions.wheel import ( 

71 InvalidWheel, 

72 InvalidWheelFilename, 

73 UnsupportedWheel, 

74) 

75 

76if TYPE_CHECKING: 

77 from pip._internal.metadata import BaseDistribution 

78 from pip._internal.models.link import Link 

79 from pip._internal.req.req_install import InstallRequirement 

80 

81 

82__all__ = [ 

83 # base 

84 "DiagnosticPipError", 

85 "InstallationError", 

86 "PipError", 

87 # build 

88 "BackendUnavailableError", 

89 "BuildDependencyInstallError", 

90 "InstallationSubprocessError", 

91 "VenvCreationError", 

92 "VenvImportError", 

93 # hashes 

94 "DirectoryUrlHashUnsupported", 

95 "HashError", 

96 "HashErrors", 

97 "HashMismatch", 

98 "HashMissing", 

99 "HashUnpinned", 

100 "VcsHashUnsupported", 

101 # network 

102 "ConnectionFailedError", 

103 "ConnectionTimeoutError", 

104 "IncompleteDownloadError", 

105 "NetworkConnectionError", 

106 "ProxyConnectionError", 

107 "SSLMissingError", 

108 "SSLVerificationError", 

109 # pyproject 

110 "InvalidPyProjectBuildRequires", 

111 "MissingPyProjectBuildRequires", 

112 # uninstall 

113 "LegacyDistutilsInstall", 

114 "UninstallMissingRecord", 

115 # wheel 

116 "InvalidWheel", 

117 "InvalidWheelFilename", 

118 "UnsupportedWheel", 

119] 

120 

121logger = logging.getLogger(__name__) 

122 

123 

124class ConfigurationError(PipError): 

125 """General exception in configuration""" 

126 

127 

128class FailedToPrepareCandidate(InstallationError): 

129 """Raised when we fail to prepare a candidate (i.e. fetch and generate metadata). 

130 

131 This is intentionally not a diagnostic error, since the output will be presented 

132 above this error, when this occurs. This should instead present information to the 

133 user. 

134 """ 

135 

136 def __init__( 

137 self, *, package_name: str, requirement_chain: str, failed_step: str 

138 ) -> None: 

139 super().__init__(f"Failed to build '{package_name}' when {failed_step.lower()}") 

140 self.package_name = package_name 

141 self.requirement_chain = requirement_chain 

142 self.failed_step = failed_step 

143 

144 

145class NoneMetadataError(PipError): 

146 """Raised when accessing a Distribution's "METADATA" or "PKG-INFO". 

147 

148 This signifies an inconsistency, when the Distribution claims to have 

149 the metadata file (if not, raise ``FileNotFoundError`` instead), but is 

150 not actually able to produce its content. This may be due to permission 

151 errors. 

152 """ 

153 

154 def __init__( 

155 self, 

156 dist: BaseDistribution, 

157 metadata_name: str, 

158 ) -> None: 

159 """ 

160 :param dist: A Distribution object. 

161 :param metadata_name: The name of the metadata being accessed 

162 (can be "METADATA" or "PKG-INFO"). 

163 """ 

164 self.dist = dist 

165 self.metadata_name = metadata_name 

166 

167 def __str__(self) -> str: 

168 # Use `dist` in the error message because its stringification 

169 # includes more information, like the version and location. 

170 return f"None {self.metadata_name} metadata found for distribution: {self.dist}" 

171 

172 

173class UserInstallationInvalid(InstallationError): 

174 """A --user install is requested on an environment without user site.""" 

175 

176 def __str__(self) -> str: 

177 return "User base directory is not specified" 

178 

179 

180class InvalidSchemeCombination(InstallationError): 

181 def __str__(self) -> str: 

182 before = ", ".join(str(a) for a in self.args[:-1]) 

183 return f"Cannot set {before} and {self.args[-1]} together" 

184 

185 

186class DistributionNotFound(InstallationError): 

187 """Raised when a distribution cannot be found to satisfy a requirement""" 

188 

189 

190class RequirementsFileParseError(InstallationError): 

191 """Raised when a general error occurs parsing a requirements file line.""" 

192 

193 

194class BestVersionAlreadyInstalled(PipError): 

195 """Raised when the most up-to-date version of a package is already 

196 installed.""" 

197 

198 

199class BadCommand(PipError): 

200 """Raised when virtualenv or a command is not found""" 

201 

202 

203class CommandError(PipError): 

204 """Raised when there is an error in command-line arguments""" 

205 

206 

207class PreviousBuildDirError(PipError): 

208 """Raised when there's a previous conflicting build directory""" 

209 

210 

211class MetadataInconsistent(InstallationError): 

212 """Built metadata contains inconsistent information. 

213 

214 This is raised when the metadata contains values (e.g. name and version) 

215 that do not match the information previously obtained from sdist filename, 

216 user-supplied ``#egg=`` value, or an install requirement name. 

217 """ 

218 

219 def __init__( 

220 self, ireq: InstallRequirement, field: str, f_val: str, m_val: str 

221 ) -> None: 

222 self.ireq = ireq 

223 self.field = field 

224 self.f_val = f_val 

225 self.m_val = m_val 

226 

227 def __str__(self) -> str: 

228 return ( 

229 f"Requested {self.ireq} has inconsistent {self.field}: " 

230 f"expected {self.f_val!r}, but metadata has {self.m_val!r}" 

231 ) 

232 

233 

234class SidecarMetadataInconsistent(MetadataInconsistent): 

235 """The wheel's METADATA disagrees with its PEP 658 ``.metadata`` file. 

236 

237 Raised after the wheel has been downloaded and hash-verified, when a 

238 resolver-affecting field in the wheel's embedded ``METADATA`` does not 

239 match the value taken from the remote ``.metadata`` sidecar that drove 

240 resolution. ``f_val`` is the sidecar value, ``m_val`` is the wheel value. 

241 """ 

242 

243 def __str__(self) -> str: 

244 return ( 

245 f"Requested {self.ireq} has inconsistent {self.field} between " 

246 f"its PEP 658 .metadata file and the wheel's METADATA: " 

247 f"sidecar has {self.f_val!r}, wheel has {self.m_val!r}" 

248 ) 

249 

250 

251class MetadataInvalid(InstallationError): 

252 """Metadata is invalid.""" 

253 

254 def __init__(self, ireq: InstallRequirement, error: str) -> None: 

255 self.ireq = ireq 

256 self.error = error 

257 

258 def __str__(self) -> str: 

259 return f"Requested {self.ireq} has invalid metadata: {self.error}" 

260 

261 

262class MetadataGenerationFailed(DiagnosticPipError, InstallationError): 

263 reference = "metadata-generation-failed" 

264 

265 def __init__( 

266 self, 

267 *, 

268 package_details: str, 

269 ) -> None: 

270 super().__init__( 

271 message="Encountered error while generating package metadata.", 

272 context=escape(package_details), 

273 hint_stmt="See above for details.", 

274 note_stmt="This is an issue with the package mentioned above, not pip.", 

275 ) 

276 

277 def __str__(self) -> str: 

278 return "metadata generation failed" 

279 

280 

281class UnsupportedPythonVersion(InstallationError): 

282 """Unsupported python version according to Requires-Python package 

283 metadata.""" 

284 

285 

286class ConfigurationFileCouldNotBeLoaded(ConfigurationError): 

287 """When there are errors while loading a configuration file""" 

288 

289 def __init__( 

290 self, 

291 reason: str = "could not be loaded", 

292 fname: str | None = None, 

293 error: configparser.Error | None = None, 

294 ) -> None: 

295 super().__init__(error) 

296 self.reason = reason 

297 self.fname = fname 

298 self.error = error 

299 

300 def __str__(self) -> str: 

301 if self.fname is not None: 

302 message_part = f" in {self.fname}." 

303 else: 

304 assert self.error is not None 

305 message_part = f".\n{self.error}\n" 

306 return f"Configuration file {self.reason}{message_part}" 

307 

308 

309_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\ 

310The Python environment under {sys.prefix} is managed externally, and may not be 

311manipulated by the user. Please use specific tooling from the distributor of 

312the Python installation to interact with this environment instead. 

313""" 

314 

315 

316class ExternallyManagedEnvironment(DiagnosticPipError): 

317 """The current environment is externally managed. 

318 

319 This is raised when the current environment is externally managed, as 

320 defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked 

321 and displayed when the error is bubbled up to the user. 

322 

323 :param error: The error message read from ``EXTERNALLY-MANAGED``. 

324 """ 

325 

326 reference = "externally-managed-environment" 

327 

328 def __init__(self, error: str | None) -> None: 

329 if error is None: 

330 context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR) 

331 else: 

332 context = Text(error) 

333 super().__init__( 

334 message="This environment is externally managed", 

335 context=context, 

336 note_stmt=( 

337 "If you believe this is a mistake, please contact your " 

338 "Python installation or OS distribution provider. " 

339 "You can override this, at the risk of breaking your Python " 

340 "installation or OS, by passing --break-system-packages." 

341 ), 

342 hint_stmt=Text("See PEP 668 for the detailed specification."), 

343 ) 

344 

345 @staticmethod 

346 def _iter_externally_managed_error_keys() -> Iterator[str]: 

347 # LC_MESSAGES is in POSIX, but not the C standard. The most common 

348 # platform that does not implement this category is Windows, where 

349 # using other categories for console message localization is equally 

350 # unreliable, so we fall back to the locale-less vendor message. This 

351 # can always be re-evaluated when a vendor proposes a new alternative. 

352 try: 

353 category = locale.LC_MESSAGES 

354 except AttributeError: 

355 lang: str | None = None 

356 else: 

357 lang, _ = locale.getlocale(category) 

358 if lang is not None: 

359 yield f"Error-{lang}" 

360 for sep in ("-", "_"): 

361 before, found, _ = lang.partition(sep) 

362 if not found: 

363 continue 

364 yield f"Error-{before}" 

365 yield "Error" 

366 

367 @classmethod 

368 def from_config( 

369 cls, 

370 config: pathlib.Path | str, 

371 ) -> ExternallyManagedEnvironment: 

372 parser = configparser.ConfigParser(interpolation=None) 

373 try: 

374 parser.read(config, encoding="utf-8") 

375 section = parser["externally-managed"] 

376 for key in cls._iter_externally_managed_error_keys(): 

377 with contextlib.suppress(KeyError): 

378 return cls(section[key]) 

379 except KeyError: 

380 pass 

381 except (OSError, UnicodeDecodeError, configparser.ParsingError): 

382 from pip._internal.utils._log import VERBOSE 

383 

384 exc_info = logger.isEnabledFor(VERBOSE) 

385 logger.warning("Failed to read %s", config, exc_info=exc_info) 

386 return cls(None) 

387 

388 

389class InvalidInstalledPackage(DiagnosticPipError): 

390 reference = "invalid-installed-package" 

391 

392 def __init__( 

393 self, 

394 *, 

395 dist: BaseDistribution, 

396 invalid_exc: InvalidRequirement | InvalidVersion, 

397 ) -> None: 

398 installed_location = dist.installed_location 

399 

400 if isinstance(invalid_exc, InvalidRequirement): 

401 invalid_type = "requirement" 

402 else: 

403 invalid_type = "version" 

404 

405 super().__init__( 

406 message=Text( 

407 f"Cannot process installed package {dist} " 

408 + (f"in {installed_location!r} " if installed_location else "") 

409 + f"because it has an invalid {invalid_type}:\n{invalid_exc.args[0]}" 

410 ), 

411 context=( 

412 "Starting with pip 24.1, packages with invalid " 

413 f"{invalid_type}s can not be processed." 

414 ), 

415 hint_stmt="To proceed this package must be uninstalled.", 

416 ) 

417 

418 

419class ResolutionTooDeepError(DiagnosticPipError): 

420 """Raised when the dependency resolver exceeds the maximum recursion depth.""" 

421 

422 reference = "resolution-too-deep" 

423 

424 def __init__(self) -> None: 

425 super().__init__( 

426 message="Dependency resolution exceeded maximum depth", 

427 context=( 

428 "Pip cannot resolve the current dependencies as the dependency graph " 

429 "is too complex for pip to solve efficiently." 

430 ), 

431 hint_stmt=( 

432 "Try adding lower bounds to constrain your dependencies, " 

433 "for example: 'package>=2.0.0' instead of just 'package'. " 

434 ), 

435 link="https://pip.pypa.io/en/stable/topics/dependency-resolution/#handling-resolution-too-deep-errors", 

436 ) 

437 

438 

439class InstallWheelBuildError(DiagnosticPipError): 

440 reference = "failed-wheel-build-for-install" 

441 

442 def __init__(self, failed: list[InstallRequirement]) -> None: 

443 super().__init__( 

444 message=( 

445 "Failed to build installable wheels for some " 

446 "pyproject.toml based projects" 

447 ), 

448 context=", ".join(r.name for r in failed), # type: ignore 

449 hint_stmt=None, 

450 ) 

451 

452 

453class InvalidEggFragment(DiagnosticPipError): 

454 reference = "invalid-egg-fragment" 

455 

456 def __init__(self, link: Link, fragment: str) -> None: 

457 hint = "" 

458 if ">" in fragment or "=" in fragment or "<" in fragment: 

459 hint = ( 

460 "Version specifiers are silently ignored for URL references. " 

461 "Remove them. " 

462 ) 

463 if "[" in fragment and "]" in fragment: 

464 hint += "Try using the Direct URL requirement syntax: 'name[extra] @ URL'" 

465 

466 if not hint: 

467 hint = "Egg fragments can only be a valid project name." 

468 

469 super().__init__( 

470 message=f"The '{escape(fragment)}' egg fragment is invalid", 

471 context=f"from '{escape(str(link))}'", 

472 hint_stmt=escape(hint), 

473 )