Skip to content

Commit c5efaf8

Browse files
authored
Merge branch 'pypa:main' into patch-1
2 parents 13a1a62 + 6958e28 commit c5efaf8

File tree

13 files changed

+62
-23
lines changed

13 files changed

+62
-23
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ repos:
2828
args: [--fix, --exit-non-zero-on-fix]
2929

3030
- repo: https://github.com/pre-commit/mirrors-mypy
31-
rev: v1.10.0
31+
rev: v1.12.1
3232
hooks:
3333
- id: mypy
3434
exclude: tests/data

news/10EC0CC0-C535-4B04-8924-CBF3DAA3315D.trivial.rst

Whitespace-only changes.

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/cli/parser.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sys
77
import textwrap
88
from contextlib import suppress
9-
from typing import Any, Dict, Generator, List, Optional, Tuple
9+
from typing import Any, Dict, Generator, List, NoReturn, Optional, Tuple
1010

1111
from pip._internal.cli.status_codes import UNKNOWN_ERROR
1212
from pip._internal.configuration import Configuration, ConfigurationError
@@ -289,6 +289,6 @@ def get_default_values(self) -> optparse.Values:
289289
defaults[option.dest] = option.check_value(opt_str, default)
290290
return optparse.Values(defaults)
291291

292-
def error(self, msg: str) -> None:
292+
def error(self, msg: str) -> NoReturn:
293293
self.print_usage(sys.stderr)
294294
self.exit(UNKNOWN_ERROR, f"{msg}\n")

src/pip/_internal/cli/progress_bars.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def _rich_progress_bar(
2525
iterable: Iterable[bytes],
2626
*,
2727
bar_type: str,
28-
size: int,
28+
size: Optional[int],
2929
) -> Generator[bytes, None, None]:
3030
assert bar_type == "on", "This should only be used in the default mode."
3131

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/locations/_distutils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from distutils.command.install import SCHEME_KEYS
2222
from distutils.command.install import install as distutils_install_command
2323
from distutils.sysconfig import get_python_lib
24-
from typing import Dict, List, Optional, Union, cast
24+
from typing import Dict, List, Optional, Union
2525

2626
from pip._internal.models.scheme import Scheme
2727
from pip._internal.utils.compat import WINDOWS
@@ -64,7 +64,7 @@ def distutils_scheme(
6464
obj: Optional[DistutilsCommand] = None
6565
obj = d.get_command_obj("install", create=True)
6666
assert obj is not None
67-
i = cast(distutils_install_command, obj)
67+
i: distutils_install_command = obj
6868
# NOTE: setting user or home has the side-effect of creating the home dir
6969
# or user base for installations during finalize_options()
7070
# ideally, we'd prefer a scheme class that has no side-effects.
@@ -78,7 +78,7 @@ def distutils_scheme(
7878
i.root = root or i.root
7979
i.finalize_options()
8080

81-
scheme = {}
81+
scheme: Dict[str, str] = {}
8282
for key in SCHEME_KEYS:
8383
scheme[key] = getattr(i, "install_" + key)
8484

src/pip/_internal/network/lazy_wheel.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ def _check_zip(self) -> None:
159159
try:
160160
# For read-only ZIP files, ZipFile only needs
161161
# methods read, seek, seekable and tell.
162-
ZipFile(self) # type: ignore
162+
ZipFile(self)
163163
except BadZipFile:
164164
pass
165165
else:

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)