Skip to content

Commit

Permalink
Merge pull request #12953 from notatallshaw/diagnostic-error-on-pip-u…
Browse files Browse the repository at this point in the history
…ninstall

Diagnostic error when processing package already installed with invalid requirement
  • Loading branch information
sbidoul authored Oct 21, 2024
2 parents 53ba3fb + c558a49 commit be46900
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 7 deletions.
1 change: 1 addition & 0 deletions news/12953.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Display disagnostic error message when already installed package has an invalid requirement.
32 changes: 32 additions & 0 deletions src/pip/_internal/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from itertools import chain, groupby, repeat
from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union

from pip._vendor.packaging.requirements import InvalidRequirement
from pip._vendor.packaging.version import InvalidVersion
from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult
from pip._vendor.rich.markup import escape
from pip._vendor.rich.text import Text
Expand Down Expand Up @@ -775,3 +777,33 @@ def __init__(self, *, distribution: "BaseDistribution") -> None:
),
hint_stmt=None,
)


class InvalidInstalledPackage(DiagnosticPipError):
reference = "invalid-installed-package"

def __init__(
self,
*,
dist: "BaseDistribution",
invalid_exc: Union[InvalidRequirement, InvalidVersion],
) -> None:
installed_location = dist.installed_location

if isinstance(invalid_exc, InvalidRequirement):
invalid_type = "requirement"
else:
invalid_type = "version"

super().__init__(
message=Text(
f"Cannot process installed package {dist} "
+ (f"in {installed_location!r} " if installed_location else "")
+ f"because it has an invalid {invalid_type}:\n{invalid_exc.args[0]}"
),
context=(
"Starting with pip 24.1, packages with invalid "
f"{invalid_type}s can not be processed."
),
hint_stmt="To proceed this package must be uninstalled.",
)
9 changes: 7 additions & 2 deletions src/pip/_internal/resolution/resolvelib/candidates.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from pip._internal.exceptions import (
HashError,
InstallationSubprocessError,
InvalidInstalledPackage,
MetadataInconsistent,
MetadataInvalid,
)
Expand Down Expand Up @@ -398,8 +399,12 @@ def format_for_error(self) -> str:
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
if not with_requires:
return
for r in self.dist.iter_dependencies():
yield from self._factory.make_requirements_from_spec(str(r), self._ireq)

try:
for r in self.dist.iter_dependencies():
yield from self._factory.make_requirements_from_spec(str(r), self._ireq)
except InvalidRequirement as exc:
raise InvalidInstalledPackage(dist=self.dist, invalid_exc=exc) from None

def get_install_requirement(self) -> Optional[InstallRequirement]:
return None
Expand Down
16 changes: 11 additions & 5 deletions src/pip/_internal/resolution/resolvelib/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
from pip._vendor.packaging.requirements import InvalidRequirement
from pip._vendor.packaging.specifiers import SpecifierSet
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
from pip._vendor.packaging.version import Version
from pip._vendor.packaging.version import InvalidVersion, Version
from pip._vendor.resolvelib import ResolutionImpossible

from pip._internal.cache import CacheEntry, WheelCache
from pip._internal.exceptions import (
DistributionNotFound,
InstallationError,
InvalidInstalledPackage,
MetadataInconsistent,
MetadataInvalid,
UnsupportedPythonVersion,
Expand Down Expand Up @@ -283,10 +284,15 @@ def _get_installed_candidate() -> Optional[Candidate]:
installed_dist = self._installed_dists[name]
except KeyError:
return None
# Don't use the installed distribution if its version does not fit
# the current dependency graph.
if not specifier.contains(installed_dist.version, prereleases=True):
return None

try:
# Don't use the installed distribution if its version
# does not fit the current dependency graph.
if not specifier.contains(installed_dist.version, prereleases=True):
return None
except InvalidVersion as e:
raise InvalidInstalledPackage(dist=installed_dist, invalid_exc=e)

candidate = self._make_candidate_from_dist(
dist=installed_dist,
extras=extras,
Expand Down

0 comments on commit be46900

Please sign in to comment.