Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
exceptions.py972 linesDownload Raw Back to _internal
1"""Exceptions used throughout package.2 3This module MUST NOT try to import from anything within `pip._internal` to4operate. This is expected to be importable from any/all files within the5subpackage and, thus, should not depend on them.6"""7 8from __future__ import annotations9 10import configparser11import contextlib12import locale13import logging14import pathlib15import re16import sys17import traceback18from collections.abc import Iterable, Iterator19from itertools import chain, groupby, repeat20from typing import TYPE_CHECKING, Literal21 22from pip._vendor.packaging.requirements import InvalidRequirement23from pip._vendor.packaging.version import InvalidVersion24from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult25from pip._vendor.rich.markup import escape26from pip._vendor.rich.text import Text27 28if TYPE_CHECKING:29    from hashlib import _Hash30 31    from pip._vendor.requests.models import PreparedRequest, Request, Response32 33    from pip._internal.metadata import BaseDistribution34    from pip._internal.models.link import Link35    from pip._internal.network.download import _FileDownload36    from pip._internal.req.req_install import InstallRequirement37 38logger = logging.getLogger(__name__)39 40 41#42# Scaffolding43#44def _is_kebab_case(s: str) -> bool:45    return re.match(r"^[a-z]+(-[a-z]+)*$", s) is not None46 47 48def _prefix_with_indent(49    s: Text | str,50    console: Console,51    *,52    prefix: str,53    indent: str,54) -> Text:55    if isinstance(s, Text):56        text = s57    else:58        text = console.render_str(s)59 60    return console.render_str(prefix, overflow="ignore") + console.render_str(61        f"\n{indent}", overflow="ignore"62    ).join(text.split(allow_blank=True))63 64 65class PipError(Exception):66    """The base pip error."""67 68 69class DiagnosticPipError(PipError):70    """An error, that presents diagnostic information to the user.71 72    This contains a bunch of logic, to enable pretty presentation of our error73    messages. Each error gets a unique reference. Each error can also include74    additional context, a hint and/or a note -- which are presented with the75    main error message in a consistent style.76 77    This is adapted from the error output styling in `sphinx-theme-builder`.78    """79 80    reference: str81 82    def __init__(83        self,84        *,85        kind: Literal["error", "warning"] = "error",86        reference: str | None = None,87        message: str | Text,88        context: str | Text | None,89        hint_stmt: str | Text | None,90        note_stmt: str | Text | None = None,91        link: str | None = None,92    ) -> None:93        # Ensure a proper reference is provided.94        if reference is None:95            assert hasattr(self, "reference"), "error reference not provided!"96            reference = self.reference97        assert _is_kebab_case(reference), "error reference must be kebab-case!"98 99        self.kind = kind100        self.reference = reference101 102        self.message = message103        self.context = context104 105        self.note_stmt = note_stmt106        self.hint_stmt = hint_stmt107 108        self.link = link109 110        super().__init__(f"<{self.__class__.__name__}: {self.reference}>")111 112    def __repr__(self) -> str:113        return (114            f"<{self.__class__.__name__}("115            f"reference={self.reference!r}, "116            f"message={self.message!r}, "117            f"context={self.context!r}, "118            f"note_stmt={self.note_stmt!r}, "119            f"hint_stmt={self.hint_stmt!r}"120            ")>"121        )122 123    def __rich_console__(124        self,125        console: Console,126        options: ConsoleOptions,127    ) -> RenderResult:128        colour = "red" if self.kind == "error" else "yellow"129 130        yield f"[{colour} bold]{self.kind}[/]: [bold]{self.reference}[/]"131        yield ""132 133        if not options.ascii_only:134            # Present the main message, with relevant context indented.135            if self.context is not None:136                yield _prefix_with_indent(137                    self.message,138                    console,139                    prefix=f"[{colour}]×[/] ",140                    indent=f"[{colour}]│[/] ",141                )142                yield _prefix_with_indent(143                    self.context,144                    console,145                    prefix=f"[{colour}]╰─>[/] ",146                    indent=f"[{colour}]   [/] ",147                )148            else:149                yield _prefix_with_indent(150                    self.message,151                    console,152                    prefix="[red]×[/] ",153                    indent="  ",154                )155        else:156            yield self.message157            if self.context is not None:158                yield ""159                yield self.context160 161        if self.note_stmt is not None or self.hint_stmt is not None:162            yield ""163 164        if self.note_stmt is not None:165            yield _prefix_with_indent(166                self.note_stmt,167                console,168                prefix="[magenta bold]note[/]: ",169                indent="      ",170            )171        if self.hint_stmt is not None:172            yield _prefix_with_indent(173                self.hint_stmt,174                console,175                prefix="[cyan bold]hint[/]: ",176                indent="      ",177            )178 179        if self.link is not None:180            yield ""181            yield f"Link: {self.link}"182 183 184#185# Actual Errors186#187class ConfigurationError(PipError):188    """General exception in configuration"""189 190 191class InstallationError(PipError):192    """General exception during installation"""193 194 195class FailedToPrepareCandidate(InstallationError):196    """Raised when we fail to prepare a candidate (i.e. fetch and generate metadata).197 198    This is intentionally not a diagnostic error, since the output will be presented199    above this error, when this occurs. This should instead present information to the200    user.201    """202 203    def __init__(204        self, *, package_name: str, requirement_chain: str, failed_step: str205    ) -> None:206        super().__init__(f"Failed to build '{package_name}' when {failed_step.lower()}")207        self.package_name = package_name208        self.requirement_chain = requirement_chain209        self.failed_step = failed_step210 211 212class MissingPyProjectBuildRequires(DiagnosticPipError):213    """Raised when pyproject.toml has `build-system`, but no `build-system.requires`."""214 215    reference = "missing-pyproject-build-system-requires"216 217    def __init__(self, *, package: str) -> None:218        super().__init__(219            message=f"Can not process {escape(package)}",220            context=Text(221                "This package has an invalid pyproject.toml file.\n"222                "The [build-system] table is missing the mandatory `requires` key."223            ),224            note_stmt="This is an issue with the package mentioned above, not pip.",225            hint_stmt=Text("See PEP 518 for the detailed specification."),226        )227 228 229class InvalidPyProjectBuildRequires(DiagnosticPipError):230    """Raised when pyproject.toml an invalid `build-system.requires`."""231 232    reference = "invalid-pyproject-build-system-requires"233 234    def __init__(self, *, package: str, reason: str) -> None:235        super().__init__(236            message=f"Can not process {escape(package)}",237            context=Text(238                "This package has an invalid `build-system.requires` key in "239                f"pyproject.toml.\n{reason}"240            ),241            note_stmt="This is an issue with the package mentioned above, not pip.",242            hint_stmt=Text("See PEP 518 for the detailed specification."),243        )244 245 246class NoneMetadataError(PipError):247    """Raised when accessing a Distribution's "METADATA" or "PKG-INFO".248 249    This signifies an inconsistency, when the Distribution claims to have250    the metadata file (if not, raise ``FileNotFoundError`` instead), but is251    not actually able to produce its content. This may be due to permission252    errors.253    """254 255    def __init__(256        self,257        dist: BaseDistribution,258        metadata_name: str,259    ) -> None:260        """261        :param dist: A Distribution object.262        :param metadata_name: The name of the metadata being accessed263            (can be "METADATA" or "PKG-INFO").264        """265        self.dist = dist266        self.metadata_name = metadata_name267 268    def __str__(self) -> str:269        # Use `dist` in the error message because its stringification270        # includes more information, like the version and location.271        return f"None {self.metadata_name} metadata found for distribution: {self.dist}"272 273 274class UserInstallationInvalid(InstallationError):275    """A --user install is requested on an environment without user site."""276 277    def __str__(self) -> str:278        return "User base directory is not specified"279 280 281class InvalidSchemeCombination(InstallationError):282    def __str__(self) -> str:283        before = ", ".join(str(a) for a in self.args[:-1])284        return f"Cannot set {before} and {self.args[-1]} together"285 286 287class DistributionNotFound(InstallationError):288    """Raised when a distribution cannot be found to satisfy a requirement"""289 290 291class RequirementsFileParseError(InstallationError):292    """Raised when a general error occurs parsing a requirements file line."""293 294 295class BestVersionAlreadyInstalled(PipError):296    """Raised when the most up-to-date version of a package is already297    installed."""298 299 300class BadCommand(PipError):301    """Raised when virtualenv or a command is not found"""302 303 304class CommandError(PipError):305    """Raised when there is an error in command-line arguments"""306 307 308class PreviousBuildDirError(PipError):309    """Raised when there's a previous conflicting build directory"""310 311 312class NetworkConnectionError(PipError):313    """HTTP connection error"""314 315    def __init__(316        self,317        error_msg: str,318        response: Response | None = None,319        request: Request | PreparedRequest | None = None,320    ) -> None:321        """322        Initialize NetworkConnectionError with  `request` and `response`323        objects.324        """325        self.response = response326        self.request = request327        self.error_msg = error_msg328        if (329            self.response is not None330            and not self.request331            and hasattr(response, "request")332        ):333            self.request = self.response.request334        super().__init__(error_msg, response, request)335 336    def __str__(self) -> str:337        return str(self.error_msg)338 339 340class InvalidWheelFilename(InstallationError):341    """Invalid wheel filename."""342 343 344class UnsupportedWheel(InstallationError):345    """Unsupported wheel."""346 347 348class InvalidWheel(InstallationError):349    """Invalid (e.g. corrupt) wheel."""350 351    def __init__(self, location: str, name: str):352        self.location = location353        self.name = name354 355    def __str__(self) -> str:356        return f"Wheel '{self.name}' located at {self.location} is invalid."357 358 359class MetadataInconsistent(InstallationError):360    """Built metadata contains inconsistent information.361 362    This is raised when the metadata contains values (e.g. name and version)363    that do not match the information previously obtained from sdist filename,364    user-supplied ``#egg=`` value, or an install requirement name.365    """366 367    def __init__(368        self, ireq: InstallRequirement, field: str, f_val: str, m_val: str369    ) -> None:370        self.ireq = ireq371        self.field = field372        self.f_val = f_val373        self.m_val = m_val374 375    def __str__(self) -> str:376        return (377            f"Requested {self.ireq} has inconsistent {self.field}: "378            f"expected {self.f_val!r}, but metadata has {self.m_val!r}"379        )380 381 382class MetadataInvalid(InstallationError):383    """Metadata is invalid."""384 385    def __init__(self, ireq: InstallRequirement, error: str) -> None:386        self.ireq = ireq387        self.error = error388 389    def __str__(self) -> str:390        return f"Requested {self.ireq} has invalid metadata: {self.error}"391 392 393class InstallationSubprocessError(DiagnosticPipError, InstallationError):394    """A subprocess call failed."""395 396    reference = "subprocess-exited-with-error"397 398    def __init__(399        self,400        *,401        command_description: str,402        exit_code: int,403        output_lines: list[str] | None,404    ) -> None:405        if output_lines is None:406            output_prompt = Text("No available output.")407        else:408            output_prompt = (409                Text.from_markup(f"[red][{len(output_lines)} lines of output][/]\n")410                + Text("".join(output_lines))411                + Text.from_markup(R"[red]\[end of output][/]")412            )413 414        super().__init__(415            message=(416                f"[green]{escape(command_description)}[/] did not run successfully.\n"417                f"exit code: {exit_code}"418            ),419            context=output_prompt,420            hint_stmt=None,421            note_stmt=(422                "This error originates from a subprocess, and is likely not a "423                "problem with pip."424            ),425        )426 427        self.command_description = command_description428        self.exit_code = exit_code429 430    def __str__(self) -> str:431        return f"{self.command_description} exited with {self.exit_code}"432 433 434class MetadataGenerationFailed(DiagnosticPipError, InstallationError):435    reference = "metadata-generation-failed"436 437    def __init__(438        self,439        *,440        package_details: str,441    ) -> None:442        super().__init__(443            message="Encountered error while generating package metadata.",444            context=escape(package_details),445            hint_stmt="See above for details.",446            note_stmt="This is an issue with the package mentioned above, not pip.",447        )448 449    def __str__(self) -> str:450        return "metadata generation failed"451 452 453class HashErrors(InstallationError):454    """Multiple HashError instances rolled into one for reporting"""455 456    def __init__(self) -> None:457        self.errors: list[HashError] = []458 459    def append(self, error: HashError) -> None:460        self.errors.append(error)461 462    def __str__(self) -> str:463        lines = []464        self.errors.sort(key=lambda e: e.order)465        for cls, errors_of_cls in groupby(self.errors, lambda e: e.__class__):466            lines.append(cls.head)467            lines.extend(e.body() for e in errors_of_cls)468        if lines:469            return "\n".join(lines)470        return ""471 472    def __bool__(self) -> bool:473        return bool(self.errors)474 475 476class HashError(InstallationError):477    """478    A failure to verify a package against known-good hashes479 480    :cvar order: An int sorting hash exception classes by difficulty of481        recovery (lower being harder), so the user doesn't bother fretting482        about unpinned packages when he has deeper issues, like VCS483        dependencies, to deal with. Also keeps error reports in a484        deterministic order.485    :cvar head: A section heading for display above potentially many486        exceptions of this kind487    :ivar req: The InstallRequirement that triggered this error. This is488        pasted on after the exception is instantiated, because it's not489        typically available earlier.490 491    """492 493    req: InstallRequirement | None = None494    head = ""495    order: int = -1496 497    def body(self) -> str:498        """Return a summary of me for display under the heading.499 500        This default implementation simply prints a description of the501        triggering requirement.502 503        :param req: The InstallRequirement that provoked this error, with504            its link already populated by the resolver's _populate_link().505 506        """507        return f"    {self._requirement_name()}"508 509    def __str__(self) -> str:510        return f"{self.head}\n{self.body()}"511 512    def _requirement_name(self) -> str:513        """Return a description of the requirement that triggered me.514 515        This default implementation returns long description of the req, with516        line numbers517 518        """519        return str(self.req) if self.req else "unknown package"520 521 522class VcsHashUnsupported(HashError):523    """A hash was provided for a version-control-system-based requirement, but524    we don't have a method for hashing those."""525 526    order = 0527    head = (528        "Can't verify hashes for these requirements because we don't "529        "have a way to hash version control repositories:"530    )531 532 533class DirectoryUrlHashUnsupported(HashError):534    """A hash was provided for a version-control-system-based requirement, but535    we don't have a method for hashing those."""536 537    order = 1538    head = (539        "Can't verify hashes for these file:// requirements because they "540        "point to directories:"541    )542 543 544class HashMissing(HashError):545    """A hash was needed for a requirement but is absent."""546 547    order = 2548    head = (549        "Hashes are required in --require-hashes mode, but they are "550        "missing from some requirements. Here is a list of those "551        "requirements along with the hashes their downloaded archives "552        "actually had. Add lines like these to your requirements files to "553        "prevent tampering. (If you did not enable --require-hashes "554        "manually, note that it turns on automatically when any package "555        "has a hash.)"556    )557 558    def __init__(self, gotten_hash: str) -> None:559        """560        :param gotten_hash: The hash of the (possibly malicious) archive we561            just downloaded562        """563        self.gotten_hash = gotten_hash564 565    def body(self) -> str:566        # Dodge circular import.567        from pip._internal.utils.hashes import FAVORITE_HASH568 569        package = None570        if self.req:571            # In the case of URL-based requirements, display the original URL572            # seen in the requirements file rather than the package name,573            # so the output can be directly copied into the requirements file.574            package = (575                self.req.original_link576                if self.req.is_direct577                # In case someone feeds something downright stupid578                # to InstallRequirement's constructor.579                else getattr(self.req, "req", None)580            )581        return "    {} --hash={}:{}".format(582            package or "unknown package", FAVORITE_HASH, self.gotten_hash583        )584 585 586class HashUnpinned(HashError):587    """A requirement had a hash specified but was not pinned to a specific588    version."""589 590    order = 3591    head = (592        "In --require-hashes mode, all requirements must have their "593        "versions pinned with ==. These do not:"594    )595 596 597class HashMismatch(HashError):598    """599    Distribution file hash values don't match.600 601    :ivar package_name: The name of the package that triggered the hash602        mismatch. Feel free to write to this after the exception is raise to603        improve its error message.604 605    """606 607    order = 4608    head = (609        "THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS "610        "FILE. If you have updated the package versions, please update "611        "the hashes. Otherwise, examine the package contents carefully; "612        "someone may have tampered with them."613    )614 615    def __init__(self, allowed: dict[str, list[str]], gots: dict[str, _Hash]) -> None:616        """617        :param allowed: A dict of algorithm names pointing to lists of allowed618            hex digests619        :param gots: A dict of algorithm names pointing to hashes we620            actually got from the files under suspicion621        """622        self.allowed = allowed623        self.gots = gots624 625    def body(self) -> str:626        return f"    {self._requirement_name()}:\n{self._hash_comparison()}"627 628    def _hash_comparison(self) -> str:629        """630        Return a comparison of actual and expected hash values.631 632        Example::633 634               Expected sha256 abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde635                            or 123451234512345123451234512345123451234512345636                    Got        bcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdefbcdef637 638        """639 640        def hash_then_or(hash_name: str) -> chain[str]:641            # For now, all the decent hashes have 6-char names, so we can get642            # away with hard-coding space literals.643            return chain([hash_name], repeat("    or"))644 645        lines: list[str] = []646        for hash_name, expecteds in self.allowed.items():647            prefix = hash_then_or(hash_name)648            lines.extend((f"        Expected {next(prefix)} {e}") for e in expecteds)649            lines.append(650                f"             Got        {self.gots[hash_name].hexdigest()}\n"651            )652        return "\n".join(lines)653 654 655class UnsupportedPythonVersion(InstallationError):656    """Unsupported python version according to Requires-Python package657    metadata."""658 659 660class ConfigurationFileCouldNotBeLoaded(ConfigurationError):661    """When there are errors while loading a configuration file"""662 663    def __init__(664        self,665        reason: str = "could not be loaded",666        fname: str | None = None,667        error: configparser.Error | None = None,668    ) -> None:669        super().__init__(error)670        self.reason = reason671        self.fname = fname672        self.error = error673 674    def __str__(self) -> str:675        if self.fname is not None:676            message_part = f" in {self.fname}."677        else:678            assert self.error is not None679            message_part = f".\n{self.error}\n"680        return f"Configuration file {self.reason}{message_part}"681 682 683_DEFAULT_EXTERNALLY_MANAGED_ERROR = f"""\684The Python environment under {sys.prefix} is managed externally, and may not be685manipulated by the user. Please use specific tooling from the distributor of686the Python installation to interact with this environment instead.687"""688 689 690class ExternallyManagedEnvironment(DiagnosticPipError):691    """The current environment is externally managed.692 693    This is raised when the current environment is externally managed, as694    defined by `PEP 668`_. The ``EXTERNALLY-MANAGED`` configuration is checked695    and displayed when the error is bubbled up to the user.696 697    :param error: The error message read from ``EXTERNALLY-MANAGED``.698    """699 700    reference = "externally-managed-environment"701 702    def __init__(self, error: str | None) -> None:703        if error is None:704            context = Text(_DEFAULT_EXTERNALLY_MANAGED_ERROR)705        else:706            context = Text(error)707        super().__init__(708            message="This environment is externally managed",709            context=context,710            note_stmt=(711                "If you believe this is a mistake, please contact your "712                "Python installation or OS distribution provider. "713                "You can override this, at the risk of breaking your Python "714                "installation or OS, by passing --break-system-packages."715            ),716            hint_stmt=Text("See PEP 668 for the detailed specification."),717        )718 719    @staticmethod720    def _iter_externally_managed_error_keys() -> Iterator[str]:721        # LC_MESSAGES is in POSIX, but not the C standard. The most common722        # platform that does not implement this category is Windows, where723        # using other categories for console message localization is equally724        # unreliable, so we fall back to the locale-less vendor message. This725        # can always be re-evaluated when a vendor proposes a new alternative.726        try:727            category = locale.LC_MESSAGES728        except AttributeError:729            lang: str | None = None730        else:731            lang, _ = locale.getlocale(category)732        if lang is not None:733            yield f"Error-{lang}"734            for sep in ("-", "_"):735                before, found, _ = lang.partition(sep)736                if not found:737                    continue738                yield f"Error-{before}"739        yield "Error"740 741    @classmethod742    def from_config(743        cls,744        config: pathlib.Path | str,745    ) -> ExternallyManagedEnvironment:746        parser = configparser.ConfigParser(interpolation=None)747        try:748            parser.read(config, encoding="utf-8")749            section = parser["externally-managed"]750            for key in cls._iter_externally_managed_error_keys():751                with contextlib.suppress(KeyError):752                    return cls(section[key])753        except KeyError:754            pass755        except (OSError, UnicodeDecodeError, configparser.ParsingError):756            from pip._internal.utils._log import VERBOSE757 758            exc_info = logger.isEnabledFor(VERBOSE)759            logger.warning("Failed to read %s", config, exc_info=exc_info)760        return cls(None)761 762 763class UninstallMissingRecord(DiagnosticPipError):764    reference = "uninstall-no-record-file"765 766    def __init__(self, *, distribution: BaseDistribution) -> None:767        installer = distribution.installer768        if not installer or installer == "pip":769            dep = f"{distribution.raw_name}=={distribution.version}"770            hint = Text.assemble(771                "You might be able to recover from this via: ",772                (f"pip install --ignore-installed --no-deps {dep}", "green"),773            )774        else:775            hint = Text(776                f"The package was installed by {installer}. "777                "You should check if it can uninstall the package."778            )779 780        super().__init__(781            message=Text(f"Cannot uninstall {distribution}"),782            context=(783                "The package's contents are unknown: "784                f"no RECORD file was found for {distribution.raw_name}."785            ),786            hint_stmt=hint,787        )788 789 790class LegacyDistutilsInstall(DiagnosticPipError):791    reference = "uninstall-distutils-installed-package"792 793    def __init__(self, *, distribution: BaseDistribution) -> None:794        super().__init__(795            message=Text(f"Cannot uninstall {distribution}"),796            context=(797                "It is a distutils installed project and thus we cannot accurately "798                "determine which files belong to it which would lead to only a partial "799                "uninstall."800            ),801            hint_stmt=None,802        )803 804 805class InvalidInstalledPackage(DiagnosticPipError):806    reference = "invalid-installed-package"807 808    def __init__(809        self,810        *,811        dist: BaseDistribution,812        invalid_exc: InvalidRequirement | InvalidVersion,813    ) -> None:814        installed_location = dist.installed_location815 816        if isinstance(invalid_exc, InvalidRequirement):817            invalid_type = "requirement"818        else:819            invalid_type = "version"820 821        super().__init__(822            message=Text(823                f"Cannot process installed package {dist} "824                + (f"in {installed_location!r} " if installed_location else "")825                + f"because it has an invalid {invalid_type}:\n{invalid_exc.args[0]}"826            ),827            context=(828                "Starting with pip 24.1, packages with invalid "829                f"{invalid_type}s can not be processed."830            ),831            hint_stmt="To proceed this package must be uninstalled.",832        )833 834 835class IncompleteDownloadError(DiagnosticPipError):836    """Raised when the downloader receives fewer bytes than advertised837    in the Content-Length header."""838 839    reference = "incomplete-download"840 841    def __init__(self, download: _FileDownload) -> None:842        # Dodge circular import.843        from pip._internal.utils.misc import format_size844 845        assert download.size is not None846        download_status = (847            f"{format_size(download.bytes_received)}/{format_size(download.size)}"848        )849        if download.reattempts:850            retry_status = f"after {download.reattempts + 1} attempts "851            hint = "Use --resume-retries to configure resume attempt limit."852        else:853            # Download retrying is not enabled.854            retry_status = ""855            hint = "Consider using --resume-retries to enable download resumption."856        message = Text(857            f"Download failed {retry_status}because not enough bytes "858            f"were received ({download_status})"859        )860 861        super().__init__(862            message=message,863            context=f"URL: {download.link.redacted_url}",864            hint_stmt=hint,865            note_stmt="This is an issue with network connectivity, not pip.",866        )867 868 869class ResolutionTooDeepError(DiagnosticPipError):870    """Raised when the dependency resolver exceeds the maximum recursion depth."""871 872    reference = "resolution-too-deep"873 874    def __init__(self) -> None:875        super().__init__(876            message="Dependency resolution exceeded maximum depth",877            context=(878                "Pip cannot resolve the current dependencies as the dependency graph "879                "is too complex for pip to solve efficiently."880            ),881            hint_stmt=(882                "Try adding lower bounds to constrain your dependencies, "883                "for example: 'package>=2.0.0' instead of just 'package'. "884            ),885            link="https://pip.pypa.io/en/stable/topics/dependency-resolution/#handling-resolution-too-deep-errors",886        )887 888 889class InstallWheelBuildError(DiagnosticPipError):890    reference = "failed-wheel-build-for-install"891 892    def __init__(self, failed: list[InstallRequirement]) -> None:893        super().__init__(894            message=(895                "Failed to build installable wheels for some "896                "pyproject.toml based projects"897            ),898            context=", ".join(r.name for r in failed),  # type: ignore899            hint_stmt=None,900        )901 902 903class InvalidEggFragment(DiagnosticPipError):904    reference = "invalid-egg-fragment"905 906    def __init__(self, link: Link, fragment: str) -> None:907        hint = ""908        if ">" in fragment or "=" in fragment or "<" in fragment:909            hint = (910                "Version specifiers are silently ignored for URL references. "911                "Remove them. "912            )913        if "[" in fragment and "]" in fragment:914            hint += "Try using the Direct URL requirement syntax: 'name[extra] @ URL'"915 916        if not hint:917            hint = "Egg fragments can only be a valid project name."918 919        super().__init__(920            message=f"The '{escape(fragment)}' egg fragment is invalid",921            context=f"from '{escape(str(link))}'",922            hint_stmt=escape(hint),923        )924 925 926class BuildDependencyInstallError(DiagnosticPipError):927    """Raised when build dependencies cannot be installed."""928 929    reference = "failed-build-dependency-install"930 931    def __init__(932        self,933        req: InstallRequirement | None,934        build_reqs: Iterable[str],935        *,936        cause: Exception,937        log_lines: list[str] | None,938    ) -> None:939        if isinstance(cause, PipError):940            note = "This is likely not a problem with pip."941        else:942            note = (943                "pip crashed unexpectedly. Please file an issue on pip's issue "944                "tracker: https://github.com/pypa/pip/issues/new"945            )946 947        if log_lines is None:948            # No logs are available, they must have been printed earlier.949            context = Text("See above for more details.")950        else:951            if isinstance(cause, PipError):952                log_lines.append(f"ERROR: {cause}")953            else:954                # Split rendered error into real lines without trailing newlines.955                log_lines.extend(956                    "".join(traceback.format_exception(cause)).splitlines()957                )958 959            context = Text.assemble(960                f"Installing {' '.join(build_reqs)}\n",961                (f"[{len(log_lines)} lines of output]\n", "red"),962                "\n".join(log_lines),963                ("\n[end of output]", "red"),964            )965 966        message = Text("Cannot install build dependencies", "green")967        if req:968            message += Text(f" for {req}")969        super().__init__(970            message=message, context=context, hint_stmt=None, note_stmt=note971        )972 
codekingpro/portable-devtools · Team Ai