Skip to content

Commit be46900

Browse files
authored
Merge pull request #12953 from notatallshaw/diagnostic-error-on-pip-uninstall
Diagnostic error when processing package already installed with invalid requirement
2 parents 53ba3fb + c558a49 commit be46900

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

news/12953.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Display disagnostic error message when already installed package has an invalid requirement.

src/pip/_internal/exceptions.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
from itertools import chain, groupby, repeat
1616
from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union
1717

18+
from pip._vendor.packaging.requirements import InvalidRequirement
19+
from pip._vendor.packaging.version import InvalidVersion
1820
from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult
1921
from pip._vendor.rich.markup import escape
2022
from pip._vendor.rich.text import Text
@@ -775,3 +777,33 @@ def __init__(self, *, distribution: "BaseDistribution") -> None:
775777
),
776778
hint_stmt=None,
777779
)
780+
781+
782+
class InvalidInstalledPackage(DiagnosticPipError):
783+
reference = "invalid-installed-package"
784+
785+
def __init__(
786+
self,
787+
*,
788+
dist: "BaseDistribution",
789+
invalid_exc: Union[InvalidRequirement, InvalidVersion],
790+
) -> None:
791+
installed_location = dist.installed_location
792+
793+
if isinstance(invalid_exc, InvalidRequirement):
794+
invalid_type = "requirement"
795+
else:
796+
invalid_type = "version"
797+
798+
super().__init__(
799+
message=Text(
800+
f"Cannot process installed package {dist} "
801+
+ (f"in {installed_location!r} " if installed_location else "")
802+
+ f"because it has an invalid {invalid_type}:\n{invalid_exc.args[0]}"
803+
),
804+
context=(
805+
"Starting with pip 24.1, packages with invalid "
806+
f"{invalid_type}s can not be processed."
807+
),
808+
hint_stmt="To proceed this package must be uninstalled.",
809+
)

src/pip/_internal/resolution/resolvelib/candidates.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pip._internal.exceptions import (
1010
HashError,
1111
InstallationSubprocessError,
12+
InvalidInstalledPackage,
1213
MetadataInconsistent,
1314
MetadataInvalid,
1415
)
@@ -398,8 +399,12 @@ def format_for_error(self) -> str:
398399
def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requirement]]:
399400
if not with_requires:
400401
return
401-
for r in self.dist.iter_dependencies():
402-
yield from self._factory.make_requirements_from_spec(str(r), self._ireq)
402+
403+
try:
404+
for r in self.dist.iter_dependencies():
405+
yield from self._factory.make_requirements_from_spec(str(r), self._ireq)
406+
except InvalidRequirement as exc:
407+
raise InvalidInstalledPackage(dist=self.dist, invalid_exc=exc) from None
403408

404409
def get_install_requirement(self) -> Optional[InstallRequirement]:
405410
return None

src/pip/_internal/resolution/resolvelib/factory.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@
2323
from pip._vendor.packaging.requirements import InvalidRequirement
2424
from pip._vendor.packaging.specifiers import SpecifierSet
2525
from pip._vendor.packaging.utils import NormalizedName, canonicalize_name
26-
from pip._vendor.packaging.version import Version
26+
from pip._vendor.packaging.version import InvalidVersion, Version
2727
from pip._vendor.resolvelib import ResolutionImpossible
2828

2929
from pip._internal.cache import CacheEntry, WheelCache
3030
from pip._internal.exceptions import (
3131
DistributionNotFound,
3232
InstallationError,
33+
InvalidInstalledPackage,
3334
MetadataInconsistent,
3435
MetadataInvalid,
3536
UnsupportedPythonVersion,
@@ -283,10 +284,15 @@ def _get_installed_candidate() -> Optional[Candidate]:
283284
installed_dist = self._installed_dists[name]
284285
except KeyError:
285286
return None
286-
# Don't use the installed distribution if its version does not fit
287-
# the current dependency graph.
288-
if not specifier.contains(installed_dist.version, prereleases=True):
289-
return None
287+
288+
try:
289+
# Don't use the installed distribution if its version
290+
# does not fit the current dependency graph.
291+
if not specifier.contains(installed_dist.version, prereleases=True):
292+
return None
293+
except InvalidVersion as e:
294+
raise InvalidInstalledPackage(dist=installed_dist, invalid_exc=e)
295+
290296
candidate = self._make_candidate_from_dist(
291297
dist=installed_dist,
292298
extras=extras,

0 commit comments

Comments
 (0)