Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Diagnostic error when processing package already installed with invalid requirement #12953

Merged
merged 9 commits into from
Oct 21, 2024
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.
35 changes: 35 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,36 @@ 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 using 'pip<24.1', "
"some other Python package tool, or manually deleted."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"some other Python package tool, or manually deleted."
"some other Python packaging tool, or manually deleted."

Actually, I could uninstall celery 4.4.7 with pip main, so this hint might be overly cautious?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize that, going to test it and update the message here appropriately.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed and simplfied hint message to just say:

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
Loading