From c5afbfc28e035b5b428e133bca071ed0d5a7911b Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Wed, 5 Mar 2025 19:32:22 +0000 Subject: [PATCH] MNT: expire legend-related deprecations --- .../next_api_changes/behavior/29832-REC.rst | 11 +++++++ lib/matplotlib/axes/_base.py | 7 ---- lib/matplotlib/legend.py | 8 ++--- lib/matplotlib/tests/test_legend.py | 32 ++++++------------- 4 files changed, 24 insertions(+), 34 deletions(-) create mode 100644 doc/api/next_api_changes/behavior/29832-REC.rst diff --git a/doc/api/next_api_changes/behavior/29832-REC.rst b/doc/api/next_api_changes/behavior/29832-REC.rst new file mode 100644 index 000000000000..a23b043a823b --- /dev/null +++ b/doc/api/next_api_changes/behavior/29832-REC.rst @@ -0,0 +1,11 @@ +Mixing positional and keyword arguments for ``legend`` handles and labels... +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +... is no longer valid. If passing *handles* and *labels* to ``legend``, they must +now be passed either both positionally or both as keywords. + +Legend labels for ``plot`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ +Previously if a sequence was passed to the *label* parameter of `~.Axes.plot` when +plotting a single dataset, the sequence was automatically cast to string for the legend +label. Now, if the sequence length is not one an error is raised. To keep the old +behavior, cast the sequence to string before passing. diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 4b41cf462f80..c5c525b29a06 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -523,13 +523,6 @@ def _plot_args(self, axes, tup, kwargs, *, labels = [label] * n_datasets elif len(label) == n_datasets: labels = label - elif n_datasets == 1: - msg = (f'Passing label as a length {len(label)} sequence when ' - 'plotting a single dataset is deprecated in Matplotlib 3.9 ' - 'and will error in 3.11. To keep the current behavior, ' - 'cast the sequence to string before passing.') - _api.warn_deprecated('3.9', message=msg) - labels = [label] else: raise ValueError( f"label must be scalar or have the same length as the input " diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index a6244b002006..d01a8dca0847 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -1286,7 +1286,7 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): legend(handles=handles, labels=labels) The behavior for a mixture of positional and keyword handles and labels - is undefined and issues a warning; it will be an error in the future. + is undefined and raises an error. Parameters ---------- @@ -1319,10 +1319,8 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs): handlers = kwargs.get('handler_map') if (handles is not None or labels is not None) and args: - _api.warn_deprecated("3.9", message=( - "You have mixed positional and keyword arguments, some input may " - "be discarded. This is deprecated since %(since)s and will " - "become an error in %(removal)s.")) + raise TypeError("When passing handles and labels, they must both be " + "passed positionally or both as keywords.") if (hasattr(handles, "__len__") and hasattr(labels, "__len__") and diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index e238e1f17124..577ceaf8fd0d 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -390,17 +390,14 @@ def test_legend_kwargs_handles_labels(self): ax.legend(labels=('a', 'b'), handles=(lnc, lns)) Legend.assert_called_with(ax, (lnc, lns), ('a', 'b')) - def test_warn_mixed_args_and_kwargs(self): + def test_error_mixed_args_and_kwargs(self): fig, ax = plt.subplots() th = np.linspace(0, 2*np.pi, 1024) lns, = ax.plot(th, np.sin(th), label='sin') lnc, = ax.plot(th, np.cos(th), label='cos') - with pytest.warns(DeprecationWarning) as record: + msg = 'must both be passed positionally or both as keywords' + with pytest.raises(TypeError, match=msg): ax.legend((lnc, lns), labels=('a', 'b')) - assert len(record) == 1 - assert str(record[0].message).startswith( - "You have mixed positional and keyword arguments, some input may " - "be discarded.") def test_parasite(self): from mpl_toolkits.axes_grid1 import host_subplot # type: ignore[import] @@ -460,16 +457,13 @@ def test_legend_kw_args(self): fig, (lines, lines2), ('a', 'b'), loc='right', bbox_transform=fig.transFigure) - def test_warn_args_kwargs(self): + def test_error_args_kwargs(self): fig, axs = plt.subplots(1, 2) lines = axs[0].plot(range(10)) lines2 = axs[1].plot(np.arange(10) * 2.) - with pytest.warns(DeprecationWarning) as record: + msg = 'must both be passed positionally or both as keywords' + with pytest.raises(TypeError, match=msg): fig.legend((lines, lines2), labels=('a', 'b')) - assert len(record) == 1 - assert str(record[0].message).startswith( - "You have mixed positional and keyword arguments, some input may " - "be discarded.") def test_figure_legend_outside(): @@ -1178,21 +1172,15 @@ def test_plot_multiple_input_single_label(label): assert legend_texts == [str(label)] * 2 -@pytest.mark.parametrize('label_array', [['low', 'high'], - ('low', 'high'), - np.array(['low', 'high'])]) -def test_plot_single_input_multiple_label(label_array): +def test_plot_single_input_multiple_label(): # test ax.plot() with 1D array like input # and iterable label x = [1, 2, 3] y = [2, 5, 6] fig, ax = plt.subplots() - with pytest.warns(mpl.MatplotlibDeprecationWarning, - match='Passing label as a length 2 sequence'): - ax.plot(x, y, label=label_array) - leg = ax.legend() - assert len(leg.get_texts()) == 1 - assert leg.get_texts()[0].get_text() == str(label_array) + with pytest.raises(ValueError, + match='label must be scalar or have the same length'): + ax.plot(x, y, label=['low', 'high']) def test_plot_single_input_list_label():
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: