From 2d54a56d3474083856f082ff13261d9030cd67db Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 3 May 2025 18:33:59 +0100 Subject: [PATCH 1/5] Include close matches when key not found --- lib/matplotlib/__init__.py | 9 ++++----- lib/matplotlib/_api/__init__.py | 23 +++++++++++++++++++---- lib/matplotlib/_api/__init__.pyi | 6 ++++-- lib/matplotlib/cm.py | 8 ++++---- lib/matplotlib/tests/test_colors.py | 10 ++++++++++ lib/matplotlib/tests/test_style.py | 4 +++- 6 files changed, 44 insertions(+), 16 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index e98e8ea07502..ffd5960e4631 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -743,12 +743,11 @@ def __setitem__(self, key, val): and val is rcsetup._auto_backend_sentinel and "backend" in self): return + valid_key = _api.check_getitem( + self.validate, rcParam=key, _suggest_close_matches=True, _error_cls=KeyError + ) try: - cval = self.validate[key](val) - except KeyError as err: - raise KeyError( - f"{key} is not a valid rc parameter (see rcParams.keys() for " - f"a list of valid parameters)") from err + cval = valid_key(val) except ValueError as ve: raise ValueError(f"Key {key}: {ve}") from None self._set(key, cval) diff --git a/lib/matplotlib/_api/__init__.py b/lib/matplotlib/_api/__init__.py index 47c32f701729..19893310f502 100644 --- a/lib/matplotlib/_api/__init__.py +++ b/lib/matplotlib/_api/__init__.py @@ -10,6 +10,7 @@ """ +import difflib import functools import itertools import pathlib @@ -174,12 +175,21 @@ def check_shape(shape, /, **kwargs): ) -def check_getitem(mapping, /, **kwargs): +def check_getitem( + mapping, /, _suggest_close_matches=False, _error_cls=ValueError, **kwargs + ): """ *kwargs* must consist of a single *key, value* pair. If *key* is in *mapping*, return ``mapping[value]``; else, raise an appropriate ValueError. + Parameters + ---------- + _suggest_close_matches : + If True, suggest only close matches instead of all valid values. + _error_cls : + Class of error to raise. + Examples -------- >>> _api.check_getitem({"foo": "bar"}, arg=arg) @@ -190,9 +200,14 @@ def check_getitem(mapping, /, **kwargs): try: return mapping[v] except KeyError: - raise ValueError( - f"{v!r} is not a valid value for {k}; supported values are " - f"{', '.join(map(repr, mapping))}") from None + if _suggest_close_matches: + if len(best := difflib.get_close_matches(v, mapping.keys(), cutoff=0.5)): + suggestion = f"Did you mean one of {best}?" + else: + suggestion = "" + else: + suggestion = f"Supported values are {', '.join(map(repr, mapping))}" + raise _error_cls(f"{v!r} is not a valid value for {k}. {suggestion}") from None def caching_module_getattr(cls): diff --git a/lib/matplotlib/_api/__init__.pyi b/lib/matplotlib/_api/__init__.pyi index 9bf67110bb54..3a8aaa81b674 100644 --- a/lib/matplotlib/_api/__init__.pyi +++ b/lib/matplotlib/_api/__init__.pyi @@ -1,5 +1,5 @@ from collections.abc import Callable, Generator, Iterable, Mapping, Sequence -from typing import Any, TypeVar, overload +from typing import Any, Type, TypeVar, overload from typing_extensions import Self # < Py 3.11 from numpy.typing import NDArray @@ -42,7 +42,9 @@ def check_in_list( values: Sequence[Any], /, *, _print_supported_values: bool = ..., **kwargs: Any ) -> None: ... def check_shape(shape: tuple[int | None, ...], /, **kwargs: NDArray) -> None: ... -def check_getitem(mapping: Mapping[Any, Any], /, **kwargs: Any) -> Any: ... +def check_getitem( + mapping: Mapping[Any, Any], /, _suggest_close_matches: bool, _error_cls: Type[Exception], **kwargs: Any +) -> Any: ... def caching_module_getattr(cls: type) -> Callable[[str], Any]: ... @overload def define_aliases( diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index ef5bf0719d3b..f4deb3e82d0f 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -92,10 +92,10 @@ def __init__(self, cmaps): self._builtin_cmaps = tuple(cmaps) def __getitem__(self, item): - try: - return self._cmaps[item].copy() - except KeyError: - raise KeyError(f"{item!r} is not a known colormap name") from None + cmap = _api.check_getitem( + self._cmaps, colormap=item, _suggest_close_matches=True, _error_cls=KeyError + ) + return cmap.copy() def __iter__(self): return iter(self._cmaps) diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py index 73de50408401..f54ac46afea5 100644 --- a/lib/matplotlib/tests/test_colors.py +++ b/lib/matplotlib/tests/test_colors.py @@ -1876,3 +1876,13 @@ def scaled(self): axes[0,1].pcolor(r, colorizer=colorizer) axes[1,0].contour(r, colorizer=colorizer) axes[1,1].contourf(r, colorizer=colorizer) + + +def test_close_error_name(): + with pytest.raises( + KeyError, + match=( + "'grays' is not a valid value for colormap. " + "Did you mean one of ['gray', 'Grays', 'gray_r']?" + )): + matplotlib.colormaps["grays"] diff --git a/lib/matplotlib/tests/test_style.py b/lib/matplotlib/tests/test_style.py index 7b54f1141720..14110209fa15 100644 --- a/lib/matplotlib/tests/test_style.py +++ b/lib/matplotlib/tests/test_style.py @@ -150,7 +150,9 @@ def test_context_with_badparam(): with style.context({PARAM: other_value}): assert mpl.rcParams[PARAM] == other_value x = style.context({PARAM: original_value, 'badparam': None}) - with pytest.raises(KeyError): + with pytest.raises( + KeyError, match="\'badparam\' is not a valid value for rcParam. " + ): with x: pass assert mpl.rcParams[PARAM] == other_value From 8c4e5bdccfedf0614c1b6dc5534c92dc2ad17bba Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sat, 3 May 2025 18:37:24 +0100 Subject: [PATCH 2/5] Improve return type of check_getitem --- lib/matplotlib/_api/__init__.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/_api/__init__.pyi b/lib/matplotlib/_api/__init__.pyi index 3a8aaa81b674..e213dfd23e44 100644 --- a/lib/matplotlib/_api/__init__.pyi +++ b/lib/matplotlib/_api/__init__.pyi @@ -43,8 +43,8 @@ def check_in_list( ) -> None: ... def check_shape(shape: tuple[int | None, ...], /, **kwargs: NDArray) -> None: ... def check_getitem( - mapping: Mapping[Any, Any], /, _suggest_close_matches: bool, _error_cls: Type[Exception], **kwargs: Any -) -> Any: ... + mapping: Mapping[Any, _T], /, _suggest_close_matches: bool, _error_cls: Type[Exception], **kwargs: Any +) -> _T: ... def caching_module_getattr(cls: type) -> Callable[[str], Any]: ... @overload def define_aliases( From e3152e2bf8f3d83cdfc14b889f5138134c9bd11c Mon Sep 17 00:00:00 2001 From: David Stansby Date: Sun, 1 Jun 2025 10:14:05 +0100 Subject: [PATCH 3/5] Automatically determine whether to suggest options --- lib/matplotlib/__init__.py | 2 +- lib/matplotlib/_api/__init__.py | 6 ++---- lib/matplotlib/_api/__init__.pyi | 2 +- lib/matplotlib/cm.py | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index ffd5960e4631..03d288efe342 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -744,7 +744,7 @@ def __setitem__(self, key, val): and "backend" in self): return valid_key = _api.check_getitem( - self.validate, rcParam=key, _suggest_close_matches=True, _error_cls=KeyError + self.validate, rcParam=key, _error_cls=KeyError ) try: cval = valid_key(val) diff --git a/lib/matplotlib/_api/__init__.py b/lib/matplotlib/_api/__init__.py index 19893310f502..13f291e1eb31 100644 --- a/lib/matplotlib/_api/__init__.py +++ b/lib/matplotlib/_api/__init__.py @@ -176,7 +176,7 @@ def check_shape(shape, /, **kwargs): def check_getitem( - mapping, /, _suggest_close_matches=False, _error_cls=ValueError, **kwargs + mapping, /, _error_cls=ValueError, **kwargs ): """ *kwargs* must consist of a single *key, value* pair. If *key* is in @@ -185,8 +185,6 @@ def check_getitem( Parameters ---------- - _suggest_close_matches : - If True, suggest only close matches instead of all valid values. _error_cls : Class of error to raise. @@ -200,7 +198,7 @@ def check_getitem( try: return mapping[v] except KeyError: - if _suggest_close_matches: + if len(mapping) > 5: if len(best := difflib.get_close_matches(v, mapping.keys(), cutoff=0.5)): suggestion = f"Did you mean one of {best}?" else: diff --git a/lib/matplotlib/_api/__init__.pyi b/lib/matplotlib/_api/__init__.pyi index e213dfd23e44..f6a3d49e60a3 100644 --- a/lib/matplotlib/_api/__init__.pyi +++ b/lib/matplotlib/_api/__init__.pyi @@ -43,7 +43,7 @@ def check_in_list( ) -> None: ... def check_shape(shape: tuple[int | None, ...], /, **kwargs: NDArray) -> None: ... def check_getitem( - mapping: Mapping[Any, _T], /, _suggest_close_matches: bool, _error_cls: Type[Exception], **kwargs: Any + mapping: Mapping[Any, _T], /, _error_cls: Type[Exception], **kwargs: Any ) -> _T: ... def caching_module_getattr(cls: type) -> Callable[[str], Any]: ... @overload diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index f4deb3e82d0f..f38baae3b80e 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -93,7 +93,7 @@ def __init__(self, cmaps): def __getitem__(self, item): cmap = _api.check_getitem( - self._cmaps, colormap=item, _suggest_close_matches=True, _error_cls=KeyError + self._cmaps, colormap=item, _error_cls=KeyError ) return cmap.copy() From ced9dd476734e6320fae33bb0605f2745a996ad5 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Tue, 15 Jul 2025 09:44:20 +0100 Subject: [PATCH 4/5] Remove deprecated import --- lib/matplotlib/_api/__init__.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/_api/__init__.pyi b/lib/matplotlib/_api/__init__.pyi index f6a3d49e60a3..34212098badc 100644 --- a/lib/matplotlib/_api/__init__.pyi +++ b/lib/matplotlib/_api/__init__.pyi @@ -1,5 +1,5 @@ from collections.abc import Callable, Generator, Iterable, Mapping, Sequence -from typing import Any, Type, TypeVar, overload +from typing import Any, TypeVar, overload from typing_extensions import Self # < Py 3.11 from numpy.typing import NDArray @@ -43,7 +43,7 @@ def check_in_list( ) -> None: ... def check_shape(shape: tuple[int | None, ...], /, **kwargs: NDArray) -> None: ... def check_getitem( - mapping: Mapping[Any, _T], /, _error_cls: Type[Exception], **kwargs: Any + mapping: Mapping[Any, _T], /, _error_cls: type[Exception], **kwargs: Any ) -> _T: ... def caching_module_getattr(cls: type) -> Callable[[str], Any]: ... @overload From e02e0f78ab547fcacecb23128ff28d3129edda84 Mon Sep 17 00:00:00 2001 From: David Stansby Date: Wed, 16 Jul 2025 09:04:49 +0100 Subject: [PATCH 5/5] Style fixes Co-authored-by: Elliott Sales de Andrade --- lib/matplotlib/_api/__init__.py | 4 +--- lib/matplotlib/cm.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/matplotlib/_api/__init__.py b/lib/matplotlib/_api/__init__.py index 13f291e1eb31..39496cfb0e82 100644 --- a/lib/matplotlib/_api/__init__.py +++ b/lib/matplotlib/_api/__init__.py @@ -175,9 +175,7 @@ def check_shape(shape, /, **kwargs): ) -def check_getitem( - mapping, /, _error_cls=ValueError, **kwargs - ): +def check_getitem(mapping, /, _error_cls=ValueError, **kwargs): """ *kwargs* must consist of a single *key, value* pair. If *key* is in *mapping*, return ``mapping[value]``; else, raise an appropriate diff --git a/lib/matplotlib/cm.py b/lib/matplotlib/cm.py index f38baae3b80e..299059177a20 100644 --- a/lib/matplotlib/cm.py +++ b/lib/matplotlib/cm.py @@ -92,9 +92,7 @@ def __init__(self, cmaps): self._builtin_cmaps = tuple(cmaps) def __getitem__(self, item): - cmap = _api.check_getitem( - self._cmaps, colormap=item, _error_cls=KeyError - ) + cmap = _api.check_getitem(self._cmaps, colormap=item, _error_cls=KeyError) return cmap.copy() def __iter__(self): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy