Skip to content

Commit 3f98b36

Browse files
committed
Include close matches when key not found
1 parent abda9dd commit 3f98b36

File tree

5 files changed

+38
-14
lines changed

5 files changed

+38
-14
lines changed

lib/matplotlib/__init__.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -740,12 +740,11 @@ def __setitem__(self, key, val):
740740
and val is rcsetup._auto_backend_sentinel
741741
and "backend" in self):
742742
return
743+
valid_key = _api.check_getitem(
744+
self.validate, rcParam=key, _suggest_close_matches=True, _error_cls=KeyError
745+
)
743746
try:
744-
cval = self.validate[key](val)
745-
except KeyError as err:
746-
raise KeyError(
747-
f"{key} is not a valid rc parameter (see rcParams.keys() for "
748-
f"a list of valid parameters)") from err
747+
cval = valid_key(val)
749748
except ValueError as ve:
750749
raise ValueError(f"Key {key}: {ve}") from None
751750
self._set(key, cval)

lib/matplotlib/_api/__init__.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
"""
1212

13+
import difflib
1314
import functools
1415
import itertools
1516
import pathlib
@@ -174,12 +175,19 @@ def check_shape(shape, /, **kwargs):
174175
)
175176

176177

177-
def check_getitem(mapping, /, **kwargs):
178+
def check_getitem(
179+
mapping, /, _suggest_close_matches=False, _error_cls=ValueError, **kwargs
180+
):
178181
"""
179182
*kwargs* must consist of a single *key, value* pair. If *key* is in
180183
*mapping*, return ``mapping[value]``; else, raise an appropriate
181184
ValueError.
182185
186+
Parameters
187+
----------
188+
_suggest_close_matches
189+
If True, suggest only close matches instead of all valid values.
190+
183191
Examples
184192
--------
185193
>>> _api.check_getitem({"foo": "bar"}, arg=arg)
@@ -190,9 +198,14 @@ def check_getitem(mapping, /, **kwargs):
190198
try:
191199
return mapping[v]
192200
except KeyError:
193-
raise ValueError(
194-
f"{v!r} is not a valid value for {k}; supported values are "
195-
f"{', '.join(map(repr, mapping))}") from None
201+
if _suggest_close_matches:
202+
if len(best := difflib.get_close_matches(v, mapping.keys(), cutoff=0.5)):
203+
suggestion = f"Did you mean one of {best}?"
204+
else:
205+
suggestion = ""
206+
else:
207+
suggestion = f"Supported values are {', '.join(map(repr, mapping))}"
208+
raise _error_cls(f"{v!r} is not a valid value for {k}. {suggestion}") from None
196209

197210

198211
def caching_module_getattr(cls):

lib/matplotlib/cm.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,10 @@ def __init__(self, cmaps):
9292
self._builtin_cmaps = tuple(cmaps)
9393

9494
def __getitem__(self, item):
95-
try:
96-
return self._cmaps[item].copy()
97-
except KeyError:
98-
raise KeyError(f"{item!r} is not a known colormap name") from None
95+
cmap = _api.check_getitem(
96+
self._cmaps, colormap=item, _suggest_close_matches=True, _error_cls=KeyError
97+
)
98+
return cmap.copy()
9999

100100
def __iter__(self):
101101
return iter(self._cmaps)

lib/matplotlib/tests/test_colors.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,3 +1828,13 @@ def test_LinearSegmentedColormap_from_list_value_color_tuple():
18281828
cmap([value for value, _ in value_color_tuples]),
18291829
to_rgba_array([color for _, color in value_color_tuples]),
18301830
)
1831+
1832+
1833+
def test_close_error_name():
1834+
with pytest.raises(
1835+
KeyError,
1836+
match=(
1837+
"'grays' is not a valid value for colormap. "
1838+
"Did you mean one of ['gray', 'Grays', 'gray_r']?"
1839+
)):
1840+
matplotlib.colormaps["grays"]

lib/matplotlib/tests/test_style.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ def test_context_with_badparam():
140140
with style.context({PARAM: other_value}):
141141
assert mpl.rcParams[PARAM] == other_value
142142
x = style.context({PARAM: original_value, 'badparam': None})
143-
with pytest.raises(KeyError):
143+
with pytest.raises(
144+
KeyError, match="\'badparam\' is not a valid value for rcParam. "
145+
):
144146
with x:
145147
pass
146148
assert mpl.rcParams[PARAM] == other_value

0 commit comments

Comments
 (0)
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