diff --git a/doc/api/next_api_changes/deprecations/20311-AL.rst b/doc/api/next_api_changes/deprecations/20311-AL.rst new file mode 100644 index 000000000000..a56941e5253a --- /dev/null +++ b/doc/api/next_api_changes/deprecations/20311-AL.rst @@ -0,0 +1,5 @@ +rcParams will no longer cast inputs to str +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +After a deprecation period, rcParams that expect a (non-pathlike) str will no +longer cast non-str inputs using `str`. This will avoid confusing errors in +subsequent code if e.g. a list input gets implicitly cast to a str. diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 07c9f175f373..b462bd58cb39 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -17,6 +17,7 @@ from functools import lru_cache, reduce from numbers import Number import operator +import os import re import numpy as np @@ -198,6 +199,11 @@ def validator(s): if (allow_none and (s is None or isinstance(s, str) and s.lower() == "none")): return None + if cls is str and not isinstance(s, str): + _api.warn_deprecated( + "3.5", message="Support for setting an rcParam that expects a " + "str value to a non-str value is deprecated since %(since)s " + "and support will be removed %(removal)s.") try: return cls(s) except (TypeError, ValueError) as e: @@ -224,6 +230,15 @@ def validator(s): validate_float, doc='return a list of floats') +def _validate_pathlike(s): + if isinstance(s, (str, os.PathLike)): + # Store value as str because savefig.directory needs to distinguish + # between "" (cwd) and "." (cwd, but gets updated by user selections). + return os.fsdecode(s) + else: + return validate_string(s) # Emit deprecation warning. + + def validate_fonttype(s): """ Confirm that this is a Postscript or PDF font type that we know how to @@ -1154,7 +1169,7 @@ def _convert_validator_spec(key, conv): "savefig.bbox": validate_bbox, # "tight", or "standard" (= None) "savefig.pad_inches": validate_float, # default directory in savefig dialog box - "savefig.directory": validate_string, + "savefig.directory": _validate_pathlike, "savefig.transparent": validate_bool, "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg @@ -1224,15 +1239,15 @@ def _convert_validator_spec(key, conv): # Additional arguments for HTML writer "animation.html_args": validate_stringlist, # Path to ffmpeg binary. If just binary name, subprocess uses $PATH. - "animation.ffmpeg_path": validate_string, + "animation.ffmpeg_path": _validate_pathlike, # Additional arguments for ffmpeg movie writer (using pipes) "animation.ffmpeg_args": validate_stringlist, # Path to AVConv binary. If just binary name, subprocess uses $PATH. - "animation.avconv_path": validate_string, + "animation.avconv_path": _validate_pathlike, # Additional arguments for avconv movie writer (using pipes) "animation.avconv_args": validate_stringlist, # Path to convert binary. If just binary name, subprocess uses $PATH. - "animation.convert_path": validate_string, + "animation.convert_path": _validate_pathlike, # Additional arguments for convert movie writer (using pipes) "animation.convert_args": validate_stringlist, diff --git a/lib/matplotlib/tests/test_rcparams.py b/lib/matplotlib/tests/test_rcparams.py index 8b80b155e5a9..17f63f737d84 100644 --- a/lib/matplotlib/tests/test_rcparams.py +++ b/lib/matplotlib/tests/test_rcparams.py @@ -225,11 +225,11 @@ def generate_validator_testcases(valid): (('a', 'b'), ['a', 'b']), (iter(['a', 'b']), ['a', 'b']), (np.array(['a', 'b']), ['a', 'b']), - ((1, 2), ['1', '2']), - (np.array([1, 2]), ['1', '2']), ), 'fail': ((set(), ValueError), (1, ValueError), + ((1, 2), _api.MatplotlibDeprecationWarning), + (np.array([1, 2]), _api.MatplotlibDeprecationWarning), ) }, {'validator': _listify_validator(validate_int, n=2),