8000 Document and test legend argument parsing by timhoffm · Pull Request #18122 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Document and test legend argument parsing #18122

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion lib/matplotlib/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1162,7 +1162,45 @@ def _parse_legend_args(axs, *args, handles=None, labels=None, **kwargs):
Get the handles and labels from the calls to either ``figure.legend``
or ``axes.legend``.

``axs`` is a list of axes (to get legend artists from)
The parser is a bit involved because we support::

legend()
legend(labels)
legend(handles, labels)
legend(labels=labels)
legend(handles=handles)
legend(handles=handles, labels=labels)

The behavior for a mixture of positional and keyword handles and labels
is undefined and issues a warning.

Parameters
----------
axs : list of `.Axes`
If handles are not given explicitly, the artists in these Axes are
used as handles.
*args : tuple
Positional parameters passed to ``legend()``.
handles
The value of the keyword argument ``legend(handles=...)``, or *None*
if that keyword argument was not used.
labels
The value of the keyword argument ``legend(labels=...)``, or *None*
if that keyword argument was not used.
**kwargs
All other keyword arguments passed to ``legend()``.

Returns
-------
handles : list of `.Artist`
The legend handles.
labels : list of str
The legend labels.
extra_args : tuple
*args* with positional handles and labels removed.
kwargs : dict
*kwargs* with keywords handles and labels removed.

"""
log = logging.getLogger(__name__)

Expand Down
58 changes: 39 additions & 19 deletions lib/matplotlib/tests/test_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def test_multiple_keys():
@image_comparison(['rgba_alpha.png'], remove_text=True,
tol=0 if platform.machine() == 'x86_64' else 0.01)
def test_alpha_rgba():
fig, ax = plt.subplots(1, 1)
fig, ax = plt.subplots()
ax.plot(range(10), lw=5)
leg = plt.legend(['Longlabel that will go away'], loc='center')
leg.legendPatch.set_facecolor([1, 0, 0, 0.5])
Expand All @@ -116,7 +116,7 @@ def test_alpha_rgba():
@image_comparison(['rcparam_alpha.png'], remove_text=True,
tol=0 if platform.machine() == 'x86_64' else 0.01)
def test_alpha_rcparam():
fig, ax = plt.subplots(1, 1)
fig, ax = plt.subplots()
ax.plot(range(10), lw=5)
with mpl.rc_context(rc={'legend.framealpha': .75}):
leg = plt.legend(['Longlabel that will go away'], loc='center')
Expand Down Expand Up @@ -228,26 +228,26 @@ def test_legend_remove():

class TestLegendFunction:
# Tests the legend function on the Axes and pyplot.
def test_legend_handle_label(self):
def test_legend_no_args(self):
lines = plt.plot(range(10), label='hello world')
with mock.patch('matplotlib.legend.Legend') as Legend:
plt.legend()
Legend.assert_called_with(plt.gca(), lines, ['hello world'])

def test_legend_positional_handles_labels(self):
lines = plt.plot(range(10))
with mock.patch('matplotlib.legend.Legend') as Legend:
plt.legend(lines, ['hello world'])
Legend.assert_called_with(plt.gca(), lines, ['hello world'])

def test_legend_handles_only(self):
def test_legend_positional_handles_only(self):
lines = plt.plot(range(10))
with pytest.raises(TypeError, match='but found an Artist'):
# a single arg is interpreted as labels
# it's a common error to just pass handles
plt.legend(lines)

def test_legend_no_args(self):
lines = plt.plot(range(10), label='hello world')
with mock.patch('matplotlib.legend.Legend') as Legend:
plt.legend()
Legend.assert_called_with(plt.gca(), lines, ['hello world'])

def test_legend_label_args(self):
def test_legend_positional_labels_only(self):
lines = plt.plot(range(10), label='hello world')
with mock.patch('matplotlib.legend.Legend') as Legend:
plt.legend(['foobar'])
Expand All @@ -267,20 +267,40 @@ def test_legend_handler_map(self):
plt.legend(handler_map={'1': 2})
handles_labels.assert_called_with([plt.gca()], {'1': 2})

def test_kwargs(self):
fig, ax = plt.subplots(1, 1)
def test_legend_kwargs_handles_only(self):
fig, ax = plt.subplots()
x = np.linspace(0, 1, 11)
ln1, = ax.plot(x, x, label='x')
ln2, = ax.plot(x, 2*x, label='2x')
ln3, = ax.plot(x, 3*x, label='3x')
with mock.patch('matplotlib.legend.Legend') as Legend:
ax.legend(handles=[ln3, ln2]) # reversed and not ln1
Legend.assert_called_with(ax, [ln3, ln2], ['3x', '2x'])

def test_legend_kwargs_labels_only(self):
fig, ax = plt.subplots()
x = np.linspace(0, 1, 11)
ln1, = ax.plot(x, x)
ln2, = ax.plot(x, 2*x)
with mock.patch('matplotlib.legend.Legend') as Legend:
ax.legend(labels=['x', '2x'])
Legend.assert_called_with(ax, [ln1, ln2], ['x', '2x'])

def test_legend_kwargs_handles_labels(self):
fig, ax = plt.subplots()
th = np.linspace(0, 2*np.pi, 1024)
lns, = ax.plot(th, np.sin(th), label='sin', lw=5)
lnc, = ax.plot(th, np.cos(th), label='cos', lw=5)
lns, = ax.plot(th, np.sin(th), label='sin')
lnc, = ax.plot(th, np.cos(th), label='cos')
with mock.patch('matplotlib.legend.Legend') as Legend:
# labels of lns, lnc are overwritten with explict ('a', 'b')
ax.legend(labels=('a', 'b'), handles=(lnc, lns))
Legend.assert_called_with(ax, (lnc, lns), ('a', 'b'))

def test_warn_args_kwargs(self):
fig, ax = plt.subplots(1, 1)
def test_warn_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', lw=5)
lnc, = ax.plot(th, np.cos(th), label='cos', lw=5)
lns, = ax.plot(th, np.sin(th), label='sin')
lnc, = ax.plot(th, np.cos(th), label='cos')
with pytest.warns(UserWarning) as record:
ax.legend((lnc, lns), labels=('a', 'b'))
assert len(record) == 1
Expand Down
0