From 85bb0be019aa630fab54fd07704a211a51b37b7c Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 5 Jan 2023 20:10:30 +0100 Subject: [PATCH] Harmonize exceptions for unknown keyword arguments. ... via a kwarg_error helper (see nargs_error for a similar design). In particular the type of the exception thrown by AxesImage.set_extent changed, and parentheses were added to the error thrown by _process_plot_var_args. --- doc/api/next_api_changes/behavior/24889-AL.rst | 3 +++ lib/matplotlib/_api/__init__.py | 17 +++++++++++++++++ lib/matplotlib/axes/_axes.py | 9 +++------ lib/matplotlib/axes/_base.py | 6 ++---- lib/matplotlib/figure.py | 3 +-- lib/matplotlib/image.py | 7 ++----- lib/matplotlib/tests/test_axes.py | 5 ++--- lib/matplotlib/ticker.py | 4 +--- 8 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/24889-AL.rst diff --git a/doc/api/next_api_changes/behavior/24889-AL.rst b/doc/api/next_api_changes/behavior/24889-AL.rst new file mode 100644 index 000000000000..95fb0c1cd5d8 --- /dev/null +++ b/doc/api/next_api_changes/behavior/24889-AL.rst @@ -0,0 +1,3 @@ +``AxesImage.set_extent`` now raises ``TypeError`` for unknown keyword arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It previously raised a `ValueError`. diff --git a/lib/matplotlib/_api/__init__.py b/lib/matplotlib/_api/__init__.py index ee2bf3450230..068abb945181 100644 --- a/lib/matplotlib/_api/__init__.py +++ b/lib/matplotlib/_api/__init__.py @@ -342,6 +342,23 @@ def nargs_error(name, takes, given): f"{given} were given") +def kwarg_error(name, kw): + """ + Generate a TypeError to be raised by function calls with wrong kwarg. + + Parameters + ---------- + name : str + The name of the calling function. + kw : str or Iterable[str] + Either the invalid keyword argument name, or an iterable yielding + invalid keyword arguments (e.g., a ``kwargs`` dict). + """ + if not isinstance(kw, str): + kw = next(iter(kw)) + return TypeError(f"{name}() got an unexpected keyword argument '{kw}'") + + def recursive_subclasses(cls): """Yield *cls* and direct and indirect subclasses of *cls*.""" yield cls diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 58d95912665b..a66cd775e230 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -7795,8 +7795,7 @@ def specgram(self, x, NFFT=None, Fs=None, Fc=None, detrend=None, extent = xmin, xmax, freqs[0], freqs[-1] if 'origin' in kwargs: - raise TypeError("specgram() got an unexpected keyword argument " - "'origin'") + raise _api.kwarg_error("specgram", "origin") im = self.imshow(Z, cmap, extent=extent, vmin=vmin, vmax=vmax, origin='upper', **kwargs) @@ -7894,8 +7893,7 @@ def spy(self, Z, precision=0, marker=None, markersize=None, kwargs['cmap'] = mcolors.ListedColormap(['w', 'k'], name='binary') if 'interpolation' in kwargs: - raise TypeError( - "spy() got an unexpected keyword argument 'interpolation'") + raise _api.kwarg_error("spy", "interpolation") if 'norm' not in kwargs: kwargs['norm'] = mcolors.NoNorm() ret = self.imshow(mask, interpolation='nearest', @@ -7920,8 +7918,7 @@ def spy(self, Z, precision=0, marker=None, markersize=None, if markersize is None: markersize = 10 if 'linestyle' in kwargs: - raise TypeError( - "spy() got an unexpected keyword argument 'linestyle'") + raise _api.kwarg_error("spy", "linestyle") ret = mlines.Line2D( x, y, linestyle='None', marker=marker, markersize=markersize, **kwargs) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index e843a6066b1b..53f5607f41d1 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -243,8 +243,7 @@ def __call__(self, *args, data=None, **kwargs): for pos_only in "xy": if pos_only in kwargs: - raise TypeError("{} got an unexpected keyword argument {!r}" - .format(self.command, pos_only)) + raise _api.kwarg_error(self.command, pos_only) if not args: return @@ -2188,8 +2187,7 @@ def axis(self, arg=None, /, *, emit=True, **kwargs): self.set_xlim(xmin, xmax, emit=emit, auto=xauto) self.set_ylim(ymin, ymax, emit=emit, auto=yauto) if kwargs: - raise TypeError(f"axis() got an unexpected keyword argument " - f"'{next(iter(kwargs))}'") + raise _api.kwarg_error("axis", kwargs) return (*self.get_xlim(), *self.get_ylim()) def get_legend(self): diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b006ca467be1..dc4f68073d70 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -716,8 +716,7 @@ def add_subplot(self, *args, **kwargs): if 'figure' in kwargs: # Axes itself allows for a 'figure' kwarg, but since we want to # bind the created Axes to self, it is not allowed here. - raise TypeError( - "add_subplot() got an unexpected keyword argument 'figure'") + raise _api.kwarg_error("add_subplot", "figure") if (len(args) == 1 and isinstance(args[0], mpl.axes._base._AxesBase) diff --git a/lib/matplotlib/image.py b/lib/matplotlib/image.py index d1ef076279c7..8b113cacdd0c 100644 --- a/lib/matplotlib/image.py +++ b/lib/matplotlib/image.py @@ -978,11 +978,8 @@ def set_extent(self, extent, **kwargs): [("x", [extent[0], extent[1]]), ("y", [extent[2], extent[3]])], kwargs) - if len(kwargs): - raise ValueError( - "set_extent did not consume all of the kwargs passed." + - f"{list(kwargs)!r} were unused" - ) + if kwargs: + raise _api.kwarg_error("set_extent", kwargs) xmin = self.axes._validate_converted_limits( xmin, self.convert_xunits) xmax = self.axes._validate_converted_limits( diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3c0a9712089c..ca176677745f 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -8244,7 +8244,7 @@ def test_automatic_legend(): def test_plot_errors(): - with pytest.raises(TypeError, match="plot got an unexpected keyword"): + with pytest.raises(TypeError, match=r"plot\(\) got an unexpected keyword"): plt.plot([1, 2, 3], x=1) with pytest.raises(ValueError, match=r"plot\(\) with multiple groups"): plt.plot([1, 2, 3], [1, 2, 3], [2, 3, 4], [2, 3, 4], label=['1', '2']) @@ -8419,8 +8419,7 @@ def test_extent_units(): axs[1, 1].xaxis.set_major_formatter(mdates.DateFormatter('%d')) axs[1, 1].set(xlabel='Day of Jan 2020') - with pytest.raises(ValueError, - match="set_extent did not consume all of the kwargs"): + with pytest.raises(TypeError, match=r"set_extent\(\) got an unexpected"): im.set_extent([2, 12, date_first, date_last], clip=False) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index ea7d2078f05b..db593838ea5f 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -2069,9 +2069,7 @@ def set_params(self, **kwargs): if 'integer' in kwargs: self._integer = kwargs.pop('integer') if kwargs: - key, _ = kwargs.popitem() - raise TypeError( - f"set_params() got an unexpected keyword argument '{key}'") + raise _api.kwarg_error("set_params", kwargs) def _raw_ticks(self, vmin, vmax): """