From d50ea9ae10a0408698f4a384b40bd42d8dbb4095 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 15:36:59 -0600 Subject: [PATCH 1/8] error (instead of warning) on unavailable extra --- src/pip/_internal/exceptions.py | 23 +++++++++++++++++++ .../resolution/resolvelib/candidates.py | 11 +++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 8ceb818a35d..d5b1523a82e 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -775,3 +775,26 @@ def __init__(self, *, distribution: "BaseDistribution") -> None: ), hint_stmt=None, ) + + +class UnavailableExtra(InstallationError): + """A requested extra is not available.""" + + def __init__( + self, + base: str, + version: str, + extra: str, + available_extras: List[str], + ): + self.base = base + self.version = version + self.extra = extra + self.available_extras = available_extras + + def __str__(self) -> str: + nice_available = " ".join( + '"{}", '.format(e) + for e in self.available_extras + )[:-2] + return f"{self.base} {self.version} does not provide the extra '{self.extra}'\n{self.base} provides extras: {nice_available}" diff --git a/src/pip/_internal/resolution/resolvelib/candidates.py b/src/pip/_internal/resolution/resolvelib/candidates.py index d30d477be68..142290cd651 100644 --- a/src/pip/_internal/resolution/resolvelib/candidates.py +++ b/src/pip/_internal/resolution/resolvelib/candidates.py @@ -11,6 +11,7 @@ InstallationSubprocessError, MetadataInconsistent, MetadataInvalid, + UnavailableExtra, ) from pip._internal.metadata import BaseDistribution from pip._internal.models.link import Link, links_equivalent @@ -508,11 +509,11 @@ def iter_dependencies(self, with_requires: bool) -> Iterable[Optional[Requiremen valid_extras = self.extras.intersection(self.base.dist.iter_provided_extras()) invalid_extras = self.extras.difference(self.base.dist.iter_provided_extras()) for extra in sorted(invalid_extras): - logger.warning( - "%s %s does not provide the extra '%s'", - self.base.name, - self.version, - extra, + raise UnavailableExtra( + base=self.base.name, + version=self.version, + extra=extra, + available_extras=self.base.dist.iter_provided_extras(), ) for r in self.base.dist.iter_dependencies(valid_extras): From 24253bc085f36e2465552a1a207f18e8f7b1d6c3 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 15:39:04 -0600 Subject: [PATCH 2/8] news --- news/7122.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/7122.feature.rst diff --git a/news/7122.feature.rst b/news/7122.feature.rst new file mode 100644 index 00000000000..1faf15c21f8 --- /dev/null +++ b/news/7122.feature.rst @@ -0,0 +1 @@ +Print an error message (and exit with a non-zero exit code) when an invalid extra is requested From 653a78c35824fda3d48d1c958f81bb751641cf50 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 15:44:37 -0600 Subject: [PATCH 3/8] blacken --- src/pip/_internal/exceptions.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index d5b1523a82e..af6a1c71125 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -781,11 +781,11 @@ class UnavailableExtra(InstallationError): """A requested extra is not available.""" def __init__( - self, - base: str, - version: str, - extra: str, - available_extras: List[str], + self, + base: str, + version: str, + extra: str, + available_extras: List[str], ): self.base = base self.version = version @@ -793,8 +793,7 @@ def __init__( self.available_extras = available_extras def __str__(self) -> str: - nice_available = " ".join( - '"{}", '.format(e) - for e in self.available_extras - )[:-2] + nice_available = " ".join('"{}", '.format(e) for e in self.available_extras)[ + :-2 + ] return f"{self.base} {self.version} does not provide the extra '{self.extra}'\n{self.base} provides extras: {nice_available}" From cae7861217632932ccaad21c12807e7d39b2bfba Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 16:01:30 -0600 Subject: [PATCH 4/8] type-checking / ruff --- src/pip/_internal/exceptions.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index af6a1c71125..b3c6facf663 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -22,6 +22,7 @@ if TYPE_CHECKING: from hashlib import _Hash + from pip._vendor.packaging.utils import NormalizedName from pip._vendor.requests.models import Request, Response from pip._internal.metadata import BaseDistribution @@ -785,7 +786,7 @@ def __init__( base: str, version: str, extra: str, - available_extras: List[str], + available_extras: Iterator["NormalizedName"], ): self.base = base self.version = version @@ -793,7 +794,5 @@ def __init__( self.available_extras = available_extras def __str__(self) -> str: - nice_available = " ".join('"{}", '.format(e) for e in self.available_extras)[ - :-2 - ] + nice_available = " ".join(f'"{e}", ' for e in self.available_extras)[:-2] return f"{self.base} {self.version} does not provide the extra '{self.extra}'\n{self.base} provides extras: {nice_available}" From 7ff986834280d8c58d6fe7760747f7c6e884c780 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 17:41:02 -0600 Subject: [PATCH 5/8] ruff --- src/pip/_internal/exceptions.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index b3c6facf663..46f8b9e9728 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -13,7 +13,16 @@ import re import sys from itertools import chain, groupby, repeat -from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union +from typing import ( + TYPE_CHECKING, + Dict, + Iterator, + List, + Literal, + Optional, + Union, + Iterable, +) from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult from pip._vendor.rich.markup import escape @@ -786,7 +795,7 @@ def __init__( base: str, version: str, extra: str, - available_extras: Iterator["NormalizedName"], + available_extras: Iterable["NormalizedName"], ): self.base = base self.version = version From e6ba1be53c6385722659c03a777ac17b6fd68504 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 17:44:20 -0600 Subject: [PATCH 6/8] ruff --- src/pip/_internal/exceptions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 46f8b9e9728..9d97ea87c01 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -16,12 +16,12 @@ from typing import ( TYPE_CHECKING, Dict, + Iterable, Iterator, List, Literal, Optional, Union, - Iterable, ) from pip._vendor.rich.console import Console, ConsoleOptions, RenderResult @@ -32,6 +32,7 @@ from hashlib import _Hash from pip._vendor.packaging.utils import NormalizedName + from pip._vendor.packaging.version import Version from pip._vendor.requests.models import Request, Response from pip._internal.metadata import BaseDistribution @@ -793,7 +794,7 @@ class UnavailableExtra(InstallationError): def __init__( self, base: str, - version: str, + version: Version, extra: str, available_extras: Iterable["NormalizedName"], ): From 9565210580cf648898fbd7bf068de1dc515e391a Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 17:45:23 -0600 Subject: [PATCH 7/8] split line --- src/pip/_internal/exceptions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index 9d97ea87c01..a3bb0eb1546 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -805,4 +805,7 @@ def __init__( def __str__(self) -> str: nice_available = " ".join(f'"{e}", ' for e in self.available_extras)[:-2] - return f"{self.base} {self.version} does not provide the extra '{self.extra}'\n{self.base} provides extras: {nice_available}" + return ( + f"{self.base} {self.version} does not provide the extra '{self.extra}'" + f"\n{self.base} provides extras: {nice_available}" + ) From e2e35e878e30e43605dd9584d113057274c404ff Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Oct 2024 17:48:47 -0600 Subject: [PATCH 8/8] types are fun --- src/pip/_internal/exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pip/_internal/exceptions.py b/src/pip/_internal/exceptions.py index a3bb0eb1546..559e2586333 100644 --- a/src/pip/_internal/exceptions.py +++ b/src/pip/_internal/exceptions.py @@ -794,7 +794,7 @@ class UnavailableExtra(InstallationError): def __init__( self, base: str, - version: Version, + version: "Version", extra: str, available_extras: Iterable["NormalizedName"], ):