Skip to content

[ENH]: int / float-tuple like kwarg legend(loc) for rcParams['legend.loc'] #25839

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 15, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``rcParams['legend.loc']`` now accepts float-tuple inputs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :rc:`legend.loc` rcParams now accepts float-tuple inputs, same as the *loc* keyword argument to `.Legend`.
This allows users to set the location of the legend in a more flexible and consistent way.
51 changes: 46 additions & 5 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,51 @@ def visit_Attribute(self, node):
self.generic_visit(node)


# A validator dedicated to the named legend loc
_validate_named_legend_loc = ValidateInStrings(
'legend.loc',
[
"best",
"upper right", "upper left", "lower left", "lower right", "right",
"center left", "center right", "lower center", "upper center",
"center"],
ignorecase=True)


def _validate_legend_loc(loc):
"""
Confirm that loc is a type which rc.Params["legend.loc"] supports.

.. versionadded:: 3.8

Parameters
----------
loc : str | int | (float, float) | str((float, float))
The location of the legend.

Returns
-------
loc : str | int | (float, float) or raise ValueError exception
The location of the legend.
"""
if isinstance(loc, str):
try:
return _validate_named_legend_loc(loc)
except ValueError:
pass
try:
loc = ast.literal_eval(loc)
except (SyntaxError, ValueError):
pass
if isinstance(loc, int):
if 0 <= loc <= 10:
return loc
if isinstance(loc, tuple):
if len(loc) == 2 and all(isinstance(e, Real) for e in loc):
return loc
raise ValueError(f"{loc} is not a valid legend location.")


def validate_cycler(s):
"""Return a Cycler object from a string repr or the object itself."""
if isinstance(s, str):
Expand Down Expand Up @@ -1042,11 +1087,7 @@ def _convert_validator_spec(key, conv):

# legend properties
"legend.fancybox": validate_bool,
"legend.loc": _ignorecase([
"best",
"upper right", "upper left", "lower left", "lower right", "right",
"center left", "center right", "lower center", "upper center",
"center"]),
"legend.loc": _validate_legend_loc,

# the number of points in the legend line
"legend.numpoints": validate_int,
Expand Down
36 changes: 30 additions & 6 deletions lib/matplotlib/tests/test_rcparams.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import copy
import os
from pathlib import Path
import re
import subprocess
import sys
from unittest import mock
Expand Down Expand Up @@ -592,8 +591,33 @@ def test_deprecation(monkeypatch):
# suppress_matplotlib_deprecation_warning, rather than any explicit check.


def test_rcparams_legend_loc():
value = (0.9, .7)
match_str = f"{value} is not a valid value for legend.loc;"
with pytest.raises(ValueError, match=re.escape(match_str)):
mpl.RcParams({'legend.loc': value})
@pytest.mark.parametrize("value", [
"best",
1,
"1",
(0.9, .7),
(-0.9, .7),
"(0.9, .7)"
])
def test_rcparams_legend_loc(value):
# rcParams['legend.loc'] should allow any of the following formats.
# if any of these are not allowed, an exception will be raised
# test for gh issue #22338
mpl.rcParams["legend.loc"] = value


@pytest.mark.parametrize("value", [
"best",
1,
(0.9, .7),
(-0.9, .7),
])
def test_rcparams_legend_loc_from_file(tmpdir, value):
# rcParams['legend.loc'] should be settable from matplotlibrc.
# if any of these are not allowed, an exception will be raised.
# test for gh issue #22338
rc_path = tmpdir.join("matplotlibrc")
rc_path.write(f"legend.loc: {value}")

with mpl.rc_context(fname=rc_path):
assert mpl.rcParams["legend.loc"] == value