1506 lines
56 KiB
Python
1506 lines
56 KiB
Python
|
"""
|
||
|
The rcsetup module contains the validation code for customization using
|
||
|
Matplotlib's rc settings.
|
||
|
|
||
|
Each rc setting is assigned a function used to validate any attempted changes
|
||
|
to that setting. The validation functions are defined in the rcsetup module,
|
||
|
and are used to construct the rcParams global object which stores the settings
|
||
|
and is referenced throughout Matplotlib.
|
||
|
|
||
|
The default values of the rc settings are set in the default matplotlibrc file.
|
||
|
Any additions or deletions to the parameter set listed here should also be
|
||
|
propagated to the :file:`matplotlibrc.template` in Matplotlib's root source
|
||
|
directory.
|
||
|
"""
|
||
|
|
||
|
import ast
|
||
|
from functools import lru_cache, reduce
|
||
|
import logging
|
||
|
from numbers import Number
|
||
|
import operator
|
||
|
import os
|
||
|
import re
|
||
|
|
||
|
import numpy as np
|
||
|
|
||
|
from matplotlib import animation, cbook
|
||
|
from matplotlib.cbook import ls_mapper
|
||
|
from matplotlib.fontconfig_pattern import parse_fontconfig_pattern
|
||
|
from matplotlib.colors import is_color_like
|
||
|
|
||
|
# Don't let the original cycler collide with our validating cycler
|
||
|
from cycler import Cycler, cycler as ccycler
|
||
|
|
||
|
|
||
|
_log = logging.getLogger(__name__)
|
||
|
# The capitalized forms are needed for ipython at present; this may
|
||
|
# change for later versions.
|
||
|
interactive_bk = ['GTK3Agg', 'GTK3Cairo',
|
||
|
'MacOSX',
|
||
|
'nbAgg',
|
||
|
'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo',
|
||
|
'TkAgg', 'TkCairo',
|
||
|
'WebAgg',
|
||
|
'WX', 'WXAgg', 'WXCairo']
|
||
|
non_interactive_bk = ['agg', 'cairo',
|
||
|
'pdf', 'pgf', 'ps', 'svg', 'template']
|
||
|
all_backends = interactive_bk + non_interactive_bk
|
||
|
|
||
|
|
||
|
class ValidateInStrings:
|
||
|
def __init__(self, key, valid, ignorecase=False, *,
|
||
|
_deprecated_since=None):
|
||
|
"""*valid* is a list of legal strings."""
|
||
|
self.key = key
|
||
|
self.ignorecase = ignorecase
|
||
|
self._deprecated_since = _deprecated_since
|
||
|
|
||
|
def func(s):
|
||
|
if ignorecase:
|
||
|
return s.lower()
|
||
|
else:
|
||
|
return s
|
||
|
self.valid = {func(k): k for k in valid}
|
||
|
|
||
|
def __call__(self, s):
|
||
|
if self._deprecated_since:
|
||
|
name, = (k for k, v in globals().items() if v is self)
|
||
|
cbook.warn_deprecated(
|
||
|
self._deprecated_since, name=name, obj_type="function")
|
||
|
if self.ignorecase:
|
||
|
s = s.lower()
|
||
|
if s in self.valid:
|
||
|
return self.valid[s]
|
||
|
msg = (f"{s!r} is not a valid value for {self.key}; supported values "
|
||
|
f"are {[*self.valid.values()]}")
|
||
|
if (isinstance(s, str)
|
||
|
and (s.startswith('"') and s.endswith('"')
|
||
|
or s.startswith("'") and s.endswith("'"))
|
||
|
and s[1:-1] in self.valid):
|
||
|
msg += "; remove quotes surrounding your string"
|
||
|
raise ValueError(msg)
|
||
|
|
||
|
|
||
|
@lru_cache()
|
||
|
def _listify_validator(scalar_validator, allow_stringlist=False, *,
|
||
|
n=None, doc=None):
|
||
|
def f(s):
|
||
|
if isinstance(s, str):
|
||
|
try:
|
||
|
val = [scalar_validator(v.strip()) for v in s.split(',')
|
||
|
if v.strip()]
|
||
|
except Exception:
|
||
|
if allow_stringlist:
|
||
|
# Sometimes, a list of colors might be a single string
|
||
|
# of single-letter colornames. So give that a shot.
|
||
|
val = [scalar_validator(v.strip()) for v in s if v.strip()]
|
||
|
else:
|
||
|
raise
|
||
|
# Allow any ordered sequence type -- generators, np.ndarray, pd.Series
|
||
|
# -- but not sets, whose iteration order is non-deterministic.
|
||
|
elif np.iterable(s) and not isinstance(s, (set, frozenset)):
|
||
|
# The condition on this list comprehension will preserve the
|
||
|
# behavior of filtering out any empty strings (behavior was
|
||
|
# from the original validate_stringlist()), while allowing
|
||
|
# any non-string/text scalar values such as numbers and arrays.
|
||
|
val = [scalar_validator(v) for v in s
|
||
|
if not isinstance(v, str) or v]
|
||
|
else:
|
||
|
raise ValueError(
|
||
|
f"Expected str or other non-set iterable, but got {s}")
|
||
|
if n is not None and len(val) != n:
|
||
|
raise ValueError(
|
||
|
f"Expected {n} values, but there are {len(val)} values in {s}")
|
||
|
return val
|
||
|
|
||
|
try:
|
||
|
f.__name__ = "{}list".format(scalar_validator.__name__)
|
||
|
except AttributeError: # class instance.
|
||
|
f.__name__ = "{}List".format(type(scalar_validator).__name__)
|
||
|
f.__qualname__ = f.__qualname__.rsplit(".", 1)[0] + "." + f.__name__
|
||
|
f.__doc__ = doc if doc is not None else scalar_validator.__doc__
|
||
|
return f
|
||
|
|
||
|
|
||
|
def validate_any(s):
|
||
|
return s
|
||
|
validate_anylist = _listify_validator(validate_any)
|
||
|
|
||
|
|
||
|
def _validate_date(s):
|
||
|
try:
|
||
|
np.datetime64(s)
|
||
|
return s
|
||
|
except ValueError:
|
||
|
raise ValueError(
|
||
|
f'{s!r} should be a string that can be parsed by numpy.datetime64')
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.2", alternative="os.path.exists")
|
||
|
def validate_path_exists(s):
|
||
|
"""If s is a path, return s, else False"""
|
||
|
if s is None:
|
||
|
return None
|
||
|
if os.path.exists(s):
|
||
|
return s
|
||
|
else:
|
||
|
raise RuntimeError('"%s" should be a path but it does not exist' % s)
|
||
|
|
||
|
|
||
|
def validate_bool(b):
|
||
|
"""Convert b to ``bool`` or raise."""
|
||
|
if isinstance(b, str):
|
||
|
b = b.lower()
|
||
|
if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True):
|
||
|
return True
|
||
|
elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False):
|
||
|
return False
|
||
|
else:
|
||
|
raise ValueError('Could not convert "%s" to bool' % b)
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_bool_maybe_none(b):
|
||
|
"""Convert b to ``bool`` or raise, passing through *None*."""
|
||
|
if isinstance(b, str):
|
||
|
b = b.lower()
|
||
|
if b is None or b == 'none':
|
||
|
return None
|
||
|
if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True):
|
||
|
return True
|
||
|
elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False):
|
||
|
return False
|
||
|
else:
|
||
|
raise ValueError('Could not convert "%s" to bool' % b)
|
||
|
|
||
|
|
||
|
def _validate_tex_preamble(s):
|
||
|
if s is None or s == 'None':
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Support for setting the 'text.latex.preamble' or "
|
||
|
"'pgf.preamble' rcParam to None is deprecated since %(since)s and "
|
||
|
"will be removed %(removal)s; set it to an empty string instead.")
|
||
|
return ""
|
||
|
try:
|
||
|
if isinstance(s, str):
|
||
|
return s
|
||
|
elif np.iterable(s):
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Support for setting the 'text.latex.preamble' "
|
||
|
"or 'pgf.preamble' rcParam to a list of strings is deprecated "
|
||
|
"since %(since)s and will be removed %(removal)s; set it to a "
|
||
|
"single string instead.")
|
||
|
return '\n'.join(s)
|
||
|
else:
|
||
|
raise TypeError
|
||
|
except TypeError as e:
|
||
|
raise ValueError('Could not convert "%s" to string' % s) from e
|
||
|
|
||
|
|
||
|
def validate_axisbelow(s):
|
||
|
try:
|
||
|
return validate_bool(s)
|
||
|
except ValueError:
|
||
|
if isinstance(s, str):
|
||
|
if s == 'line':
|
||
|
return 'line'
|
||
|
if s.lower().startswith('line'):
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message=f"Support for setting axes.axisbelow to "
|
||
|
f"{s!r} to mean 'line' is deprecated since %(since)s and "
|
||
|
f"will be removed %(removal)s; set it to 'line' instead.")
|
||
|
return 'line'
|
||
|
raise ValueError('%s cannot be interpreted as'
|
||
|
' True, False, or "line"' % s)
|
||
|
|
||
|
|
||
|
def validate_dpi(s):
|
||
|
"""Confirm s is string 'figure' or convert s to float or raise."""
|
||
|
if s == 'figure':
|
||
|
return s
|
||
|
try:
|
||
|
return float(s)
|
||
|
except ValueError as e:
|
||
|
raise ValueError(f'{s!r} is not string "figure" and '
|
||
|
f'could not convert {s!r} to float') from e
|
||
|
|
||
|
|
||
|
def _make_type_validator(cls, *, allow_none=False):
|
||
|
"""
|
||
|
Return a validator that converts inputs to *cls* or raises (and possibly
|
||
|
allows ``None`` as well).
|
||
|
"""
|
||
|
|
||
|
def validator(s):
|
||
|
if (allow_none and
|
||
|
(s is None or isinstance(s, str) and s.lower() == "none")):
|
||
|
return None
|
||
|
try:
|
||
|
return cls(s)
|
||
|
except ValueError as e:
|
||
|
raise ValueError(
|
||
|
f'Could not convert {s!r} to {cls.__name__}') from e
|
||
|
|
||
|
validator.__name__ = f"validate_{cls.__name__}"
|
||
|
if allow_none:
|
||
|
validator.__name__ += "_or_None"
|
||
|
validator.__qualname__ = (
|
||
|
validator.__qualname__.rsplit(".", 1)[0] + "." + validator.__name__)
|
||
|
return validator
|
||
|
|
||
|
|
||
|
validate_string = _make_type_validator(str)
|
||
|
validate_string_or_None = _make_type_validator(str, allow_none=True)
|
||
|
validate_stringlist = _listify_validator(
|
||
|
validate_string, doc='return a list of strings')
|
||
|
validate_int = _make_type_validator(int)
|
||
|
validate_int_or_None = _make_type_validator(int, allow_none=True)
|
||
|
validate_float = _make_type_validator(float)
|
||
|
validate_float_or_None = _make_type_validator(float, allow_none=True)
|
||
|
validate_floatlist = _listify_validator(
|
||
|
validate_float, doc='return a list of floats')
|
||
|
|
||
|
|
||
|
def validate_fonttype(s):
|
||
|
"""
|
||
|
Confirm that this is a Postscript or PDF font type that we know how to
|
||
|
convert to.
|
||
|
"""
|
||
|
fonttypes = {'type3': 3,
|
||
|
'truetype': 42}
|
||
|
try:
|
||
|
fonttype = validate_int(s)
|
||
|
except ValueError:
|
||
|
try:
|
||
|
return fonttypes[s.lower()]
|
||
|
except KeyError as e:
|
||
|
raise ValueError('Supported Postscript/PDF font types are %s'
|
||
|
% list(fonttypes)) from e
|
||
|
else:
|
||
|
if fonttype not in fonttypes.values():
|
||
|
raise ValueError(
|
||
|
'Supported Postscript/PDF font types are %s' %
|
||
|
list(fonttypes.values()))
|
||
|
return fonttype
|
||
|
|
||
|
|
||
|
_validate_standard_backends = ValidateInStrings(
|
||
|
'backend', all_backends, ignorecase=True)
|
||
|
_auto_backend_sentinel = object()
|
||
|
|
||
|
|
||
|
def validate_backend(s):
|
||
|
backend = (
|
||
|
s if s is _auto_backend_sentinel or s.startswith("module://")
|
||
|
else _validate_standard_backends(s))
|
||
|
return backend
|
||
|
|
||
|
|
||
|
validate_toolbar = ValidateInStrings(
|
||
|
'toolbar', ['None', 'toolbar2', 'toolmanager'], ignorecase=True,
|
||
|
_deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def _make_nseq_validator(cls, n=None, allow_none=False):
|
||
|
|
||
|
def validator(s):
|
||
|
"""Convert *n* objects using ``cls``, or raise."""
|
||
|
if isinstance(s, str):
|
||
|
s = [x.strip() for x in s.split(',')]
|
||
|
if n is not None and len(s) != n:
|
||
|
raise ValueError(
|
||
|
f'Expected exactly {n} comma-separated values, '
|
||
|
f'but got {len(s)} comma-separated values: {s}')
|
||
|
else:
|
||
|
if n is not None and len(s) != n:
|
||
|
raise ValueError(
|
||
|
f'Expected exactly {n} values, '
|
||
|
f'but got {len(s)} values: {s}')
|
||
|
try:
|
||
|
return [cls(val) if not allow_none or val is not None else val
|
||
|
for val in s]
|
||
|
except ValueError as e:
|
||
|
raise ValueError(
|
||
|
f'Could not convert all entries to {cls.__name__}s') from e
|
||
|
|
||
|
return validator
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_nseq_float(n):
|
||
|
return _make_nseq_validator(float, n)
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_nseq_int(n):
|
||
|
return _make_nseq_validator(int, n)
|
||
|
|
||
|
|
||
|
def validate_color_or_inherit(s):
|
||
|
"""Return a valid color arg."""
|
||
|
if cbook._str_equal(s, 'inherit'):
|
||
|
return s
|
||
|
return validate_color(s)
|
||
|
|
||
|
|
||
|
def validate_color_or_auto(s):
|
||
|
if cbook._str_equal(s, 'auto'):
|
||
|
return s
|
||
|
return validate_color(s)
|
||
|
|
||
|
|
||
|
def validate_color_for_prop_cycle(s):
|
||
|
# N-th color cycle syntax can't go into the color cycle.
|
||
|
if isinstance(s, str) and re.match("^C[0-9]$", s):
|
||
|
raise ValueError(f"Cannot put cycle reference ({s!r}) in prop_cycler")
|
||
|
return validate_color(s)
|
||
|
|
||
|
|
||
|
def validate_color(s):
|
||
|
"""Return a valid color arg."""
|
||
|
if isinstance(s, str):
|
||
|
if s.lower() == 'none':
|
||
|
return 'none'
|
||
|
if len(s) == 6 or len(s) == 8:
|
||
|
stmp = '#' + s
|
||
|
if is_color_like(stmp):
|
||
|
return stmp
|
||
|
|
||
|
if is_color_like(s):
|
||
|
return s
|
||
|
|
||
|
# If it is still valid, it must be a tuple (as a string from matplotlibrc).
|
||
|
try:
|
||
|
color = ast.literal_eval(s)
|
||
|
except (SyntaxError, ValueError):
|
||
|
pass
|
||
|
else:
|
||
|
if is_color_like(color):
|
||
|
return color
|
||
|
|
||
|
raise ValueError(f'{s!r} does not look like a color arg')
|
||
|
|
||
|
|
||
|
validate_colorlist = _listify_validator(
|
||
|
validate_color, allow_stringlist=True, doc='return a list of colorspecs')
|
||
|
validate_orientation = ValidateInStrings(
|
||
|
'orientation', ['landscape', 'portrait'], _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
def validate_aspect(s):
|
||
|
if s in ('auto', 'equal'):
|
||
|
return s
|
||
|
try:
|
||
|
return float(s)
|
||
|
except ValueError as e:
|
||
|
raise ValueError('not a valid aspect specification') from e
|
||
|
|
||
|
|
||
|
def validate_fontsize_None(s):
|
||
|
if s is None or s == 'None':
|
||
|
return None
|
||
|
else:
|
||
|
return validate_fontsize(s)
|
||
|
|
||
|
|
||
|
def validate_fontsize(s):
|
||
|
fontsizes = ['xx-small', 'x-small', 'small', 'medium', 'large',
|
||
|
'x-large', 'xx-large', 'smaller', 'larger']
|
||
|
if isinstance(s, str):
|
||
|
s = s.lower()
|
||
|
if s in fontsizes:
|
||
|
return s
|
||
|
try:
|
||
|
return float(s)
|
||
|
except ValueError as e:
|
||
|
raise ValueError("%s is not a valid font size. Valid font sizes "
|
||
|
"are %s." % (s, ", ".join(fontsizes))) from e
|
||
|
|
||
|
|
||
|
validate_fontsizelist = _listify_validator(validate_fontsize)
|
||
|
|
||
|
|
||
|
def validate_fontweight(s):
|
||
|
weights = [
|
||
|
'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman',
|
||
|
'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black']
|
||
|
# Note: Historically, weights have been case-sensitive in Matplotlib
|
||
|
if s in weights:
|
||
|
return s
|
||
|
try:
|
||
|
return int(s)
|
||
|
except (ValueError, TypeError) as e:
|
||
|
raise ValueError(f'{s} is not a valid font weight.') from e
|
||
|
|
||
|
|
||
|
def validate_font_properties(s):
|
||
|
parse_fontconfig_pattern(s)
|
||
|
return s
|
||
|
|
||
|
|
||
|
def _validate_mathtext_fallback_to_cm(b):
|
||
|
"""
|
||
|
Temporary validate for fallback_to_cm, while deprecated
|
||
|
|
||
|
"""
|
||
|
if isinstance(b, str):
|
||
|
b = b.lower()
|
||
|
if b is None or b == 'none':
|
||
|
return None
|
||
|
else:
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Support for setting the 'mathtext.fallback_to_cm' "
|
||
|
"rcParam is deprecated since %(since)s and will be removed "
|
||
|
"%(removal)s; use 'mathtext.fallback : 'cm' instead.")
|
||
|
return validate_bool_maybe_none(b)
|
||
|
|
||
|
|
||
|
def _validate_mathtext_fallback(s):
|
||
|
_fallback_fonts = ['cm', 'stix', 'stixsans']
|
||
|
if isinstance(s, str):
|
||
|
s = s.lower()
|
||
|
if s is None or s == 'none':
|
||
|
return None
|
||
|
elif s.lower() in _fallback_fonts:
|
||
|
return s
|
||
|
else:
|
||
|
raise ValueError(
|
||
|
f"{s} is not a valid fallback font name. Valid fallback font "
|
||
|
f"names are {','.join(_fallback_fonts)}. Passing 'None' will turn "
|
||
|
"fallback off.")
|
||
|
|
||
|
|
||
|
validate_fontset = ValidateInStrings(
|
||
|
'fontset',
|
||
|
['dejavusans', 'dejavuserif', 'cm', 'stix', 'stixsans', 'custom'],
|
||
|
_deprecated_since="3.3")
|
||
|
validate_mathtext_default = ValidateInStrings(
|
||
|
'default', "rm cal it tt sf bf default bb frak scr regular".split(),
|
||
|
_deprecated_since="3.3")
|
||
|
_validate_alignment = ValidateInStrings(
|
||
|
'alignment',
|
||
|
['center', 'top', 'bottom', 'baseline', 'center_baseline'],
|
||
|
_deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
def validate_whiskers(s):
|
||
|
if s == 'range':
|
||
|
cbook.warn_deprecated(
|
||
|
"3.2", message="Support for setting the boxplot.whiskers rcParam "
|
||
|
"to 'range' is deprecated since %(since)s and will be removed "
|
||
|
"%(removal)s; set it to 0, 100 instead.")
|
||
|
return 'range'
|
||
|
else:
|
||
|
try:
|
||
|
return _listify_validator(validate_float, n=2)(s)
|
||
|
except (TypeError, ValueError):
|
||
|
try:
|
||
|
return float(s)
|
||
|
except ValueError as e:
|
||
|
raise ValueError("Not a valid whisker value ['range', float, "
|
||
|
"(float, float)]") from e
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.2")
|
||
|
def update_savefig_format(value):
|
||
|
# The old savefig.extension could also have a value of "auto", but
|
||
|
# the new savefig.format does not. We need to fix this here.
|
||
|
value = validate_string(value)
|
||
|
if value == 'auto':
|
||
|
cbook.warn_deprecated(
|
||
|
"3.2", message="Support for setting the 'savefig.format' rcParam "
|
||
|
"to 'auto' is deprecated since %(since)s and will be removed "
|
||
|
"%(removal)s; set it to 'png' instead.")
|
||
|
value = 'png'
|
||
|
return value
|
||
|
|
||
|
|
||
|
# Replace by validate_string once deprecation period passes.
|
||
|
def _update_savefig_format(value):
|
||
|
# The old savefig.extension could also have a value of "auto", but
|
||
|
# the new savefig.format does not. We need to fix this here.
|
||
|
value = validate_string(value)
|
||
|
if value == 'auto':
|
||
|
cbook.warn_deprecated(
|
||
|
"3.2", message="Support for setting the 'savefig.format' rcParam "
|
||
|
"to 'auto' is deprecated since %(since)s and will be removed "
|
||
|
"%(removal)s; set it to 'png' instead.")
|
||
|
value = 'png'
|
||
|
return value
|
||
|
|
||
|
|
||
|
validate_ps_papersize = ValidateInStrings(
|
||
|
'ps_papersize',
|
||
|
['auto', 'letter', 'legal', 'ledger',
|
||
|
'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'a10',
|
||
|
'b0', 'b1', 'b2', 'b3', 'b4', 'b5', 'b6', 'b7', 'b8', 'b9', 'b10',
|
||
|
], ignorecase=True, _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
def validate_ps_distiller(s):
|
||
|
if isinstance(s, str):
|
||
|
s = s.lower()
|
||
|
if s in ('none', None, 'false', False):
|
||
|
return None
|
||
|
else:
|
||
|
return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s)
|
||
|
|
||
|
|
||
|
# A validator dedicated to the named line styles, based on the items in
|
||
|
# ls_mapper, and a list of possible strings read from Line2D.set_linestyle
|
||
|
_validate_named_linestyle = ValidateInStrings(
|
||
|
'linestyle',
|
||
|
[*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''],
|
||
|
ignorecase=True)
|
||
|
|
||
|
|
||
|
def _validate_linestyle(ls):
|
||
|
"""
|
||
|
A validator for all possible line styles, the named ones *and*
|
||
|
the on-off ink sequences.
|
||
|
"""
|
||
|
if isinstance(ls, str):
|
||
|
try: # Look first for a valid named line style, like '--' or 'solid'.
|
||
|
return _validate_named_linestyle(ls)
|
||
|
except ValueError:
|
||
|
pass
|
||
|
try:
|
||
|
ls = ast.literal_eval(ls) # Parsing matplotlibrc.
|
||
|
except (SyntaxError, ValueError):
|
||
|
pass # Will error with the ValueError at the end.
|
||
|
|
||
|
def _is_iterable_not_string_like(x):
|
||
|
# Explicitly exclude bytes/bytearrays so that they are not
|
||
|
# nonsensically interpreted as sequences of numbers (codepoints).
|
||
|
return np.iterable(x) and not isinstance(x, (str, bytes, bytearray))
|
||
|
|
||
|
# (offset, (on, off, on, off, ...))
|
||
|
if (_is_iterable_not_string_like(ls)
|
||
|
and len(ls) == 2
|
||
|
and isinstance(ls[0], (type(None), Number))
|
||
|
and _is_iterable_not_string_like(ls[1])
|
||
|
and len(ls[1]) % 2 == 0
|
||
|
and all(isinstance(elem, Number) for elem in ls[1])):
|
||
|
if ls[0] is None:
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Passing the dash offset as None is deprecated "
|
||
|
"since %(since)s and support for it will be removed "
|
||
|
"%(removal)s; pass it as zero instead.")
|
||
|
ls = (0, ls[1])
|
||
|
return ls
|
||
|
# For backcompat: (on, off, on, off, ...); the offset is implicitly None.
|
||
|
if (_is_iterable_not_string_like(ls)
|
||
|
and len(ls) % 2 == 0
|
||
|
and all(isinstance(elem, Number) for elem in ls)):
|
||
|
return (0, ls)
|
||
|
raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.")
|
||
|
|
||
|
|
||
|
def _deprecate_case_insensitive_join_cap(s):
|
||
|
s_low = s.lower()
|
||
|
if s != s_low:
|
||
|
if s_low in ['miter', 'round', 'bevel']:
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Case-insensitive capstyles are deprecated "
|
||
|
"since %(since)s and support for them will be removed "
|
||
|
"%(removal)s; please pass them in lowercase.")
|
||
|
elif s_low in ['butt', 'round', 'projecting']:
|
||
|
cbook.warn_deprecated(
|
||
|
"3.3", message="Case-insensitive joinstyles are deprecated "
|
||
|
"since %(since)s and support for them will be removed "
|
||
|
"%(removal)s; please pass them in lowercase.")
|
||
|
# Else, error out at the check_in_list stage.
|
||
|
return s_low
|
||
|
|
||
|
|
||
|
def validate_joinstyle(s):
|
||
|
s = _deprecate_case_insensitive_join_cap(s)
|
||
|
cbook._check_in_list(['miter', 'round', 'bevel'], joinstyle=s)
|
||
|
return s
|
||
|
|
||
|
|
||
|
def validate_capstyle(s):
|
||
|
s = _deprecate_case_insensitive_join_cap(s)
|
||
|
cbook._check_in_list(['butt', 'round', 'projecting'], capstyle=s)
|
||
|
return s
|
||
|
|
||
|
|
||
|
validate_fillstyle = ValidateInStrings(
|
||
|
'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none'])
|
||
|
|
||
|
|
||
|
validate_joinstylelist = _listify_validator(validate_joinstyle)
|
||
|
validate_capstylelist = _listify_validator(validate_capstyle)
|
||
|
validate_fillstylelist = _listify_validator(validate_fillstyle)
|
||
|
|
||
|
|
||
|
def validate_markevery(s):
|
||
|
"""
|
||
|
Validate the markevery property of a Line2D object.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
s : None, int, float, slice, length-2 tuple of ints,
|
||
|
length-2 tuple of floats, list of ints
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
None, int, float, slice, length-2 tuple of ints,
|
||
|
length-2 tuple of floats, list of ints
|
||
|
|
||
|
"""
|
||
|
# Validate s against type slice float int and None
|
||
|
if isinstance(s, (slice, float, int, type(None))):
|
||
|
return s
|
||
|
# Validate s against type tuple
|
||
|
if isinstance(s, tuple):
|
||
|
if (len(s) == 2
|
||
|
and (all(isinstance(e, int) for e in s)
|
||
|
or all(isinstance(e, float) for e in s))):
|
||
|
return s
|
||
|
else:
|
||
|
raise TypeError(
|
||
|
"'markevery' tuple must be pair of ints or of floats")
|
||
|
# Validate s against type list
|
||
|
if isinstance(s, list):
|
||
|
if all(isinstance(e, int) for e in s):
|
||
|
return s
|
||
|
else:
|
||
|
raise TypeError(
|
||
|
"'markevery' list must have all elements of type int")
|
||
|
raise TypeError("'markevery' is of an invalid type")
|
||
|
|
||
|
|
||
|
validate_markeverylist = _listify_validator(validate_markevery)
|
||
|
|
||
|
validate_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, _deprecated_since="3.3")
|
||
|
|
||
|
validate_svg_fonttype = ValidateInStrings(
|
||
|
'svg.fonttype', ['none', 'path'], _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_hinting(s):
|
||
|
return _validate_hinting(s)
|
||
|
|
||
|
|
||
|
# Replace by plain list in _prop_validators after deprecation period.
|
||
|
def _validate_hinting(s):
|
||
|
if s in (True, False):
|
||
|
cbook.warn_deprecated(
|
||
|
"3.2", message="Support for setting the text.hinting rcParam to "
|
||
|
"True or False is deprecated since %(since)s and will be removed "
|
||
|
"%(removal)s; set it to its synonyms 'auto' or 'none' instead.")
|
||
|
return s
|
||
|
return ValidateInStrings(
|
||
|
'text.hinting',
|
||
|
['default', 'no_autohint', 'force_autohint', 'no_hinting',
|
||
|
'auto', 'native', 'either', 'none'],
|
||
|
ignorecase=True)(s)
|
||
|
|
||
|
|
||
|
validate_pgf_texsystem = ValidateInStrings(
|
||
|
'pgf.texsystem', ['xelatex', 'lualatex', 'pdflatex'],
|
||
|
_deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_movie_writer(s):
|
||
|
# writers.list() would only list actually available writers, but
|
||
|
# FFMpeg.isAvailable is slow and not worth paying for at every import.
|
||
|
if s in animation.writers._registered:
|
||
|
return s
|
||
|
else:
|
||
|
raise ValueError(f"Supported animation writers are "
|
||
|
f"{sorted(animation.writers._registered)}")
|
||
|
|
||
|
|
||
|
validate_movie_frame_fmt = ValidateInStrings(
|
||
|
'animation.frame_format', ['png', 'jpeg', 'tiff', 'raw', 'rgba'],
|
||
|
_deprecated_since="3.3")
|
||
|
validate_axis_locator = ValidateInStrings(
|
||
|
'major', ['minor', 'both', 'major'], _deprecated_since="3.3")
|
||
|
validate_movie_html_fmt = ValidateInStrings(
|
||
|
'animation.html', ['html5', 'jshtml', 'none'], _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
def validate_bbox(s):
|
||
|
if isinstance(s, str):
|
||
|
s = s.lower()
|
||
|
if s == 'tight':
|
||
|
return s
|
||
|
if s == 'standard':
|
||
|
return None
|
||
|
raise ValueError("bbox should be 'tight' or 'standard'")
|
||
|
elif s is not None:
|
||
|
# Backwards compatibility. None is equivalent to 'standard'.
|
||
|
raise ValueError("bbox should be 'tight' or 'standard'")
|
||
|
return s
|
||
|
|
||
|
|
||
|
def validate_sketch(s):
|
||
|
if isinstance(s, str):
|
||
|
s = s.lower()
|
||
|
if s == 'none' or s is None:
|
||
|
return None
|
||
|
try:
|
||
|
return tuple(_listify_validator(validate_float, n=3)(s))
|
||
|
except ValueError:
|
||
|
raise ValueError("Expected a (scale, length, randomness) triplet")
|
||
|
|
||
|
|
||
|
def _validate_greaterequal0_lessthan1(s):
|
||
|
s = validate_float(s)
|
||
|
if 0 <= s < 1:
|
||
|
return s
|
||
|
else:
|
||
|
raise RuntimeError(f'Value must be >=0 and <1; got {s}')
|
||
|
|
||
|
|
||
|
def _validate_greaterequal0_lessequal1(s):
|
||
|
s = validate_float(s)
|
||
|
if 0 <= s <= 1:
|
||
|
return s
|
||
|
else:
|
||
|
raise RuntimeError(f'Value must be >=0 and <=1; got {s}')
|
||
|
|
||
|
|
||
|
_range_validators = { # Slightly nicer (internal) API.
|
||
|
"0 <= x < 1": _validate_greaterequal0_lessthan1,
|
||
|
"0 <= x <= 1": _validate_greaterequal0_lessequal1,
|
||
|
}
|
||
|
|
||
|
|
||
|
validate_grid_axis = ValidateInStrings(
|
||
|
'axes.grid.axis', ['x', 'y', 'both'], _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
def validate_hatch(s):
|
||
|
r"""
|
||
|
Validate a hatch pattern.
|
||
|
A hatch pattern string can have any sequence of the following
|
||
|
characters: ``\ / | - + * . x o O``.
|
||
|
"""
|
||
|
if not isinstance(s, str):
|
||
|
raise ValueError("Hatch pattern must be a string")
|
||
|
cbook._check_isinstance(str, hatch_pattern=s)
|
||
|
unknown = set(s) - {'\\', '/', '|', '-', '+', '*', '.', 'x', 'o', 'O'}
|
||
|
if unknown:
|
||
|
raise ValueError("Unknown hatch symbol(s): %s" % list(unknown))
|
||
|
return s
|
||
|
|
||
|
|
||
|
validate_hatchlist = _listify_validator(validate_hatch)
|
||
|
validate_dashlist = _listify_validator(validate_floatlist)
|
||
|
|
||
|
|
||
|
_prop_validators = {
|
||
|
'color': _listify_validator(validate_color_for_prop_cycle,
|
||
|
allow_stringlist=True),
|
||
|
'linewidth': validate_floatlist,
|
||
|
'linestyle': _listify_validator(_validate_linestyle),
|
||
|
'facecolor': validate_colorlist,
|
||
|
'edgecolor': validate_colorlist,
|
||
|
'joinstyle': validate_joinstylelist,
|
||
|
'capstyle': validate_capstylelist,
|
||
|
'fillstyle': validate_fillstylelist,
|
||
|
'markerfacecolor': validate_colorlist,
|
||
|
'markersize': validate_floatlist,
|
||
|
'markeredgewidth': validate_floatlist,
|
||
|
'markeredgecolor': validate_colorlist,
|
||
|
'markevery': validate_markeverylist,
|
||
|
'alpha': validate_floatlist,
|
||
|
'marker': validate_stringlist,
|
||
|
'hatch': validate_hatchlist,
|
||
|
'dashes': validate_dashlist,
|
||
|
}
|
||
|
_prop_aliases = {
|
||
|
'c': 'color',
|
||
|
'lw': 'linewidth',
|
||
|
'ls': 'linestyle',
|
||
|
'fc': 'facecolor',
|
||
|
'ec': 'edgecolor',
|
||
|
'mfc': 'markerfacecolor',
|
||
|
'mec': 'markeredgecolor',
|
||
|
'mew': 'markeredgewidth',
|
||
|
'ms': 'markersize',
|
||
|
}
|
||
|
|
||
|
|
||
|
def cycler(*args, **kwargs):
|
||
|
"""
|
||
|
Create a `~cycler.Cycler` object much like :func:`cycler.cycler`,
|
||
|
but includes input validation.
|
||
|
|
||
|
Call signatures::
|
||
|
|
||
|
cycler(cycler)
|
||
|
cycler(label=values[, label2=values2[, ...]])
|
||
|
cycler(label, values)
|
||
|
|
||
|
Form 1 copies a given `~cycler.Cycler` object.
|
||
|
|
||
|
Form 2 creates a `~cycler.Cycler` which cycles over one or more
|
||
|
properties simultaneously. If multiple properties are given, their
|
||
|
value lists must have the same length.
|
||
|
|
||
|
Form 3 creates a `~cycler.Cycler` for a single property. This form
|
||
|
exists for compatibility with the original cycler. Its use is
|
||
|
discouraged in favor of the kwarg form, i.e. ``cycler(label=values)``.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
cycler : Cycler
|
||
|
Copy constructor for Cycler.
|
||
|
|
||
|
label : str
|
||
|
The property key. Must be a valid `.Artist` property.
|
||
|
For example, 'color' or 'linestyle'. Aliases are allowed,
|
||
|
such as 'c' for 'color' and 'lw' for 'linewidth'.
|
||
|
|
||
|
values : iterable
|
||
|
Finite-length iterable of the property values. These values
|
||
|
are validated and will raise a ValueError if invalid.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
Cycler
|
||
|
A new :class:`~cycler.Cycler` for the given properties.
|
||
|
|
||
|
Examples
|
||
|
--------
|
||
|
Creating a cycler for a single property:
|
||
|
|
||
|
>>> c = cycler(color=['red', 'green', 'blue'])
|
||
|
|
||
|
Creating a cycler for simultaneously cycling over multiple properties
|
||
|
(e.g. red circle, green plus, blue cross):
|
||
|
|
||
|
>>> c = cycler(color=['red', 'green', 'blue'],
|
||
|
... marker=['o', '+', 'x'])
|
||
|
|
||
|
"""
|
||
|
if args and kwargs:
|
||
|
raise TypeError("cycler() can only accept positional OR keyword "
|
||
|
"arguments -- not both.")
|
||
|
elif not args and not kwargs:
|
||
|
raise TypeError("cycler() must have positional OR keyword arguments")
|
||
|
|
||
|
if len(args) == 1:
|
||
|
if not isinstance(args[0], Cycler):
|
||
|
raise TypeError("If only one positional argument given, it must "
|
||
|
" be a Cycler instance.")
|
||
|
return validate_cycler(args[0])
|
||
|
elif len(args) == 2:
|
||
|
pairs = [(args[0], args[1])]
|
||
|
elif len(args) > 2:
|
||
|
raise TypeError("No more than 2 positional arguments allowed")
|
||
|
else:
|
||
|
pairs = kwargs.items()
|
||
|
|
||
|
validated = []
|
||
|
for prop, vals in pairs:
|
||
|
norm_prop = _prop_aliases.get(prop, prop)
|
||
|
validator = _prop_validators.get(norm_prop, None)
|
||
|
if validator is None:
|
||
|
raise TypeError("Unknown artist property: %s" % prop)
|
||
|
vals = validator(vals)
|
||
|
# We will normalize the property names as well to reduce
|
||
|
# the amount of alias handling code elsewhere.
|
||
|
validated.append((norm_prop, vals))
|
||
|
|
||
|
return reduce(operator.add, (ccycler(k, v) for k, v in validated))
|
||
|
|
||
|
|
||
|
def validate_cycler(s):
|
||
|
"""Return a Cycler object from a string repr or the object itself."""
|
||
|
if isinstance(s, str):
|
||
|
# TODO: We might want to rethink this...
|
||
|
# While I think I have it quite locked down, it is execution of
|
||
|
# arbitrary code without sanitation.
|
||
|
# Combine this with the possibility that rcparams might come from the
|
||
|
# internet (future plans), this could be downright dangerous.
|
||
|
# I locked it down by only having the 'cycler()' function available.
|
||
|
# UPDATE: Partly plugging a security hole.
|
||
|
# I really should have read this:
|
||
|
# http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
|
||
|
# We should replace this eval with a combo of PyParsing and
|
||
|
# ast.literal_eval()
|
||
|
try:
|
||
|
if '.__' in s.replace(' ', ''):
|
||
|
raise ValueError("'%s' seems to have dunder methods. Raising"
|
||
|
" an exception for your safety")
|
||
|
s = eval(s, {'cycler': cycler, '__builtins__': {}})
|
||
|
except BaseException as e:
|
||
|
raise ValueError("'%s' is not a valid cycler construction: %s" %
|
||
|
(s, e)) from e
|
||
|
# Should make sure what comes from the above eval()
|
||
|
# is a Cycler object.
|
||
|
if isinstance(s, Cycler):
|
||
|
cycler_inst = s
|
||
|
else:
|
||
|
raise ValueError("object was not a string or Cycler instance: %s" % s)
|
||
|
|
||
|
unknowns = cycler_inst.keys - (set(_prop_validators) | set(_prop_aliases))
|
||
|
if unknowns:
|
||
|
raise ValueError("Unknown artist properties: %s" % unknowns)
|
||
|
|
||
|
# Not a full validation, but it'll at least normalize property names
|
||
|
# A fuller validation would require v0.10 of cycler.
|
||
|
checker = set()
|
||
|
for prop in cycler_inst.keys:
|
||
|
norm_prop = _prop_aliases.get(prop, prop)
|
||
|
if norm_prop != prop and norm_prop in cycler_inst.keys:
|
||
|
raise ValueError("Cannot specify both '{0}' and alias '{1}'"
|
||
|
" in the same prop_cycle".format(norm_prop, prop))
|
||
|
if norm_prop in checker:
|
||
|
raise ValueError("Another property was already aliased to '{0}'."
|
||
|
" Collision normalizing '{1}'.".format(norm_prop,
|
||
|
prop))
|
||
|
checker.update([norm_prop])
|
||
|
|
||
|
# This is just an extra-careful check, just in case there is some
|
||
|
# edge-case I haven't thought of.
|
||
|
assert len(checker) == len(cycler_inst.keys)
|
||
|
|
||
|
# Now, it should be safe to mutate this cycler
|
||
|
for prop in cycler_inst.keys:
|
||
|
norm_prop = _prop_aliases.get(prop, prop)
|
||
|
cycler_inst.change_key(prop, norm_prop)
|
||
|
|
||
|
for key, vals in cycler_inst.by_key().items():
|
||
|
_prop_validators[key](vals)
|
||
|
|
||
|
return cycler_inst
|
||
|
|
||
|
|
||
|
def validate_hist_bins(s):
|
||
|
valid_strs = ["auto", "sturges", "fd", "doane", "scott", "rice", "sqrt"]
|
||
|
if isinstance(s, str) and s in valid_strs:
|
||
|
return s
|
||
|
try:
|
||
|
return int(s)
|
||
|
except (TypeError, ValueError):
|
||
|
pass
|
||
|
try:
|
||
|
return validate_floatlist(s)
|
||
|
except ValueError:
|
||
|
pass
|
||
|
raise ValueError("'hist.bins' must be one of {}, an int or"
|
||
|
" a sequence of floats".format(valid_strs))
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.2")
|
||
|
def validate_animation_writer_path(p):
|
||
|
# Make sure it's a string and then figure out if the animations
|
||
|
# are already loaded and reset the writers (which will validate
|
||
|
# the path on next call)
|
||
|
cbook._check_isinstance(str, path=p)
|
||
|
from sys import modules
|
||
|
# set dirty, so that the next call to the registry will re-evaluate
|
||
|
# the state.
|
||
|
# only set dirty if already loaded. If not loaded, the load will
|
||
|
# trigger the checks.
|
||
|
if "matplotlib.animation" in modules:
|
||
|
modules["matplotlib.animation"].writers.set_dirty()
|
||
|
return p
|
||
|
|
||
|
|
||
|
@cbook.deprecated("3.3")
|
||
|
def validate_webagg_address(s):
|
||
|
if s is not None:
|
||
|
import socket
|
||
|
try:
|
||
|
socket.inet_aton(s)
|
||
|
except socket.error as e:
|
||
|
raise ValueError(
|
||
|
"'webagg.address' is not a valid IP address") from e
|
||
|
return s
|
||
|
raise ValueError("'webagg.address' is not a valid IP address")
|
||
|
|
||
|
|
||
|
validate_axes_titlelocation = ValidateInStrings(
|
||
|
'axes.titlelocation', ['left', 'center', 'right'], _deprecated_since="3.3")
|
||
|
|
||
|
|
||
|
class _ignorecase(list):
|
||
|
"""A marker class indicating that a list-of-str is case-insensitive."""
|
||
|
|
||
|
|
||
|
def _convert_validator_spec(key, conv):
|
||
|
if isinstance(conv, list):
|
||
|
ignorecase = isinstance(conv, _ignorecase)
|
||
|
return ValidateInStrings(key, conv, ignorecase=ignorecase)
|
||
|
else:
|
||
|
return conv
|
||
|
|
||
|
|
||
|
# Mapping of rcParams to validators.
|
||
|
# Converters given as lists or _ignorecase are converted to ValidateInStrings
|
||
|
# immediately below.
|
||
|
# The rcParams defaults are defined in matplotlibrc.template, which gets copied
|
||
|
# to matplotlib/mpl-data/matplotlibrc by the setup script.
|
||
|
_validators = {
|
||
|
"backend": validate_backend,
|
||
|
"backend_fallback": validate_bool,
|
||
|
"toolbar": _ignorecase(["none", "toolbar2", "toolmanager"]),
|
||
|
"datapath": validate_any, # see _get_data_path_cached
|
||
|
"interactive": validate_bool,
|
||
|
"timezone": validate_string,
|
||
|
|
||
|
"webagg.port": validate_int,
|
||
|
"webagg.address": validate_string,
|
||
|
"webagg.open_in_browser": validate_bool,
|
||
|
"webagg.port_retries": validate_int,
|
||
|
|
||
|
# line props
|
||
|
"lines.linewidth": validate_float, # line width in points
|
||
|
"lines.linestyle": _validate_linestyle, # solid line
|
||
|
"lines.color": validate_color, # first color in color cycle
|
||
|
"lines.marker": validate_string, # marker name
|
||
|
"lines.markerfacecolor": validate_color_or_auto, # default color
|
||
|
"lines.markeredgecolor": validate_color_or_auto, # default color
|
||
|
"lines.markeredgewidth": validate_float,
|
||
|
"lines.markersize": validate_float, # markersize, in points
|
||
|
"lines.antialiased": validate_bool, # antialiased (no jaggies)
|
||
|
"lines.dash_joinstyle": validate_joinstyle,
|
||
|
"lines.solid_joinstyle": validate_joinstyle,
|
||
|
"lines.dash_capstyle": validate_capstyle,
|
||
|
"lines.solid_capstyle": validate_capstyle,
|
||
|
"lines.dashed_pattern": validate_floatlist,
|
||
|
"lines.dashdot_pattern": validate_floatlist,
|
||
|
"lines.dotted_pattern": validate_floatlist,
|
||
|
"lines.scale_dashes": validate_bool,
|
||
|
|
||
|
# marker props
|
||
|
"markers.fillstyle": validate_fillstyle,
|
||
|
|
||
|
## pcolor(mesh) props:
|
||
|
"pcolor.shading": ["auto", "flat", "nearest", "gouraud"],
|
||
|
|
||
|
## patch props
|
||
|
"patch.linewidth": validate_float, # line width in points
|
||
|
"patch.edgecolor": validate_color,
|
||
|
"patch.force_edgecolor": validate_bool,
|
||
|
"patch.facecolor": validate_color, # first color in cycle
|
||
|
"patch.antialiased": validate_bool, # antialiased (no jaggies)
|
||
|
|
||
|
## hatch props
|
||
|
"hatch.color": validate_color,
|
||
|
"hatch.linewidth": validate_float,
|
||
|
|
||
|
## Histogram properties
|
||
|
"hist.bins": validate_hist_bins,
|
||
|
|
||
|
## Boxplot properties
|
||
|
"boxplot.notch": validate_bool,
|
||
|
"boxplot.vertical": validate_bool,
|
||
|
"boxplot.whiskers": validate_whiskers,
|
||
|
"boxplot.bootstrap": validate_int_or_None,
|
||
|
"boxplot.patchartist": validate_bool,
|
||
|
"boxplot.showmeans": validate_bool,
|
||
|
"boxplot.showcaps": validate_bool,
|
||
|
"boxplot.showbox": validate_bool,
|
||
|
"boxplot.showfliers": validate_bool,
|
||
|
"boxplot.meanline": validate_bool,
|
||
|
|
||
|
"boxplot.flierprops.color": validate_color,
|
||
|
"boxplot.flierprops.marker": validate_string,
|
||
|
"boxplot.flierprops.markerfacecolor": validate_color_or_auto,
|
||
|
"boxplot.flierprops.markeredgecolor": validate_color,
|
||
|
"boxplot.flierprops.markeredgewidth": validate_float,
|
||
|
"boxplot.flierprops.markersize": validate_float,
|
||
|
"boxplot.flierprops.linestyle": _validate_linestyle,
|
||
|
"boxplot.flierprops.linewidth": validate_float,
|
||
|
|
||
|
"boxplot.boxprops.color": validate_color,
|
||
|
"boxplot.boxprops.linewidth": validate_float,
|
||
|
"boxplot.boxprops.linestyle": _validate_linestyle,
|
||
|
|
||
|
"boxplot.whiskerprops.color": validate_color,
|
||
|
"boxplot.whiskerprops.linewidth": validate_float,
|
||
|
"boxplot.whiskerprops.linestyle": _validate_linestyle,
|
||
|
|
||
|
"boxplot.capprops.color": validate_color,
|
||
|
"boxplot.capprops.linewidth": validate_float,
|
||
|
"boxplot.capprops.linestyle": _validate_linestyle,
|
||
|
|
||
|
"boxplot.medianprops.color": validate_color,
|
||
|
"boxplot.medianprops.linewidth": validate_float,
|
||
|
"boxplot.medianprops.linestyle": _validate_linestyle,
|
||
|
|
||
|
"boxplot.meanprops.color": validate_color,
|
||
|
"boxplot.meanprops.marker": validate_string,
|
||
|
"boxplot.meanprops.markerfacecolor": validate_color,
|
||
|
"boxplot.meanprops.markeredgecolor": validate_color,
|
||
|
"boxplot.meanprops.markersize": validate_float,
|
||
|
"boxplot.meanprops.linestyle": _validate_linestyle,
|
||
|
"boxplot.meanprops.linewidth": validate_float,
|
||
|
|
||
|
## font props
|
||
|
"font.family": validate_stringlist, # used by text object
|
||
|
"font.style": validate_string,
|
||
|
"font.variant": validate_string,
|
||
|
"font.stretch": validate_string,
|
||
|
"font.weight": validate_fontweight,
|
||
|
"font.size": validate_float, # Base font size in points
|
||
|
"font.serif": validate_stringlist,
|
||
|
"font.sans-serif": validate_stringlist,
|
||
|
"font.cursive": validate_stringlist,
|
||
|
"font.fantasy": validate_stringlist,
|
||
|
"font.monospace": validate_stringlist,
|
||
|
|
||
|
# text props
|
||
|
"text.color": validate_color,
|
||
|
"text.usetex": validate_bool,
|
||
|
"text.latex.preamble": _validate_tex_preamble,
|
||
|
"text.latex.preview": validate_bool,
|
||
|
"text.hinting": _validate_hinting,
|
||
|
"text.hinting_factor": validate_int,
|
||
|
"text.kerning_factor": validate_int,
|
||
|
"text.antialiased": validate_bool,
|
||
|
|
||
|
"mathtext.cal": validate_font_properties,
|
||
|
"mathtext.rm": validate_font_properties,
|
||
|
"mathtext.tt": validate_font_properties,
|
||
|
"mathtext.it": validate_font_properties,
|
||
|
"mathtext.bf": validate_font_properties,
|
||
|
"mathtext.sf": validate_font_properties,
|
||
|
"mathtext.fontset": ["dejavusans", "dejavuserif", "cm", "stix",
|
||
|
"stixsans", "custom"],
|
||
|
"mathtext.default": ["rm", "cal", "it", "tt", "sf", "bf", "default",
|
||
|
"bb", "frak", "scr", "regular"],
|
||
|
"mathtext.fallback_to_cm": _validate_mathtext_fallback_to_cm,
|
||
|
"mathtext.fallback": _validate_mathtext_fallback,
|
||
|
|
||
|
"image.aspect": validate_aspect, # equal, auto, a number
|
||
|
"image.interpolation": validate_string,
|
||
|
"image.cmap": validate_string, # gray, jet, etc.
|
||
|
"image.lut": validate_int, # lookup table
|
||
|
"image.origin": ["upper", "lower"],
|
||
|
"image.resample": validate_bool,
|
||
|
# Specify whether vector graphics backends will combine all images on a
|
||
|
# set of axes into a single composite image
|
||
|
"image.composite_image": validate_bool,
|
||
|
|
||
|
# contour props
|
||
|
"contour.negative_linestyle": _validate_linestyle,
|
||
|
"contour.corner_mask": validate_bool,
|
||
|
"contour.linewidth": validate_float_or_None,
|
||
|
|
||
|
# errorbar props
|
||
|
"errorbar.capsize": validate_float,
|
||
|
|
||
|
# axis props
|
||
|
# alignment of x/y axis title
|
||
|
"xaxis.labellocation": ["left", "center", "right"],
|
||
|
"yaxis.labellocation": ["bottom", "center", "top"],
|
||
|
|
||
|
# axes props
|
||
|
"axes.axisbelow": validate_axisbelow,
|
||
|
"axes.facecolor": validate_color, # background color
|
||
|
"axes.edgecolor": validate_color, # edge color
|
||
|
"axes.linewidth": validate_float, # edge linewidth
|
||
|
|
||
|
"axes.spines.left": validate_bool, # Set visibility of axes spines,
|
||
|
"axes.spines.right": validate_bool, # i.e., the lines around the chart
|
||
|
"axes.spines.bottom": validate_bool, # denoting data boundary.
|
||
|
"axes.spines.top": validate_bool,
|
||
|
|
||
|
"axes.titlesize": validate_fontsize, # axes title fontsize
|
||
|
"axes.titlelocation": ["left", "center", "right"], # axes title alignment
|
||
|
"axes.titleweight": validate_fontweight, # axes title font weight
|
||
|
"axes.titlecolor": validate_color_or_auto, # axes title font color
|
||
|
# title location, axes units, None means auto
|
||
|
"axes.titley": validate_float_or_None,
|
||
|
# pad from axes top decoration to title in points
|
||
|
"axes.titlepad": validate_float,
|
||
|
"axes.grid": validate_bool, # display grid or not
|
||
|
"axes.grid.which": ["minor", "both", "major"], # which grids are drawn
|
||
|
"axes.grid.axis": ["x", "y", "both"], # grid type
|
||
|
"axes.labelsize": validate_fontsize, # fontsize of x & y labels
|
||
|
"axes.labelpad": validate_float, # space between label and axis
|
||
|
"axes.labelweight": validate_fontweight, # fontsize of x & y labels
|
||
|
"axes.labelcolor": validate_color, # color of axis label
|
||
|
# use scientific notation if log10 of the axis range is smaller than the
|
||
|
# first or larger than the second
|
||
|
"axes.formatter.limits": _listify_validator(validate_int, n=2),
|
||
|
# use current locale to format ticks
|
||
|
"axes.formatter.use_locale": validate_bool,
|
||
|
"axes.formatter.use_mathtext": validate_bool,
|
||
|
# minimum exponent to format in scientific notation
|
||
|
"axes.formatter.min_exponent": validate_int,
|
||
|
"axes.formatter.useoffset": validate_bool,
|
||
|
"axes.formatter.offset_threshold": validate_int,
|
||
|
"axes.unicode_minus": validate_bool,
|
||
|
# This entry can be either a cycler object or a string repr of a
|
||
|
# cycler-object, which gets eval()'ed to create the object.
|
||
|
"axes.prop_cycle": validate_cycler,
|
||
|
# If "data", axes limits are set close to the data.
|
||
|
# If "round_numbers" axes limits are set to the nearest round numbers.
|
||
|
"axes.autolimit_mode": ["data", "round_numbers"],
|
||
|
"axes.xmargin": _range_validators["0 <= x <= 1"], # margin added to xaxis
|
||
|
"axes.ymargin": _range_validators["0 <= x <= 1"], # margin added to yaxis
|
||
|
|
||
|
"polaraxes.grid": validate_bool, # display polar grid or not
|
||
|
"axes3d.grid": validate_bool, # display 3d grid
|
||
|
|
||
|
# scatter props
|
||
|
"scatter.marker": validate_string,
|
||
|
"scatter.edgecolors": validate_string,
|
||
|
|
||
|
"date.epoch": _validate_date,
|
||
|
"date.autoformatter.year": validate_string,
|
||
|
"date.autoformatter.month": validate_string,
|
||
|
"date.autoformatter.day": validate_string,
|
||
|
"date.autoformatter.hour": validate_string,
|
||
|
"date.autoformatter.minute": validate_string,
|
||
|
"date.autoformatter.second": validate_string,
|
||
|
"date.autoformatter.microsecond": validate_string,
|
||
|
|
||
|
# 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"]),
|
||
|
# the number of points in the legend line
|
||
|
"legend.numpoints": validate_int,
|
||
|
# the number of points in the legend line for scatter
|
||
|
"legend.scatterpoints": validate_int,
|
||
|
"legend.fontsize": validate_fontsize,
|
||
|
"legend.title_fontsize": validate_fontsize_None,
|
||
|
# the relative size of legend markers vs. original
|
||
|
"legend.markerscale": validate_float,
|
||
|
"legend.shadow": validate_bool,
|
||
|
# whether or not to draw a frame around legend
|
||
|
"legend.frameon": validate_bool,
|
||
|
# alpha value of the legend frame
|
||
|
"legend.framealpha": validate_float_or_None,
|
||
|
|
||
|
## the following dimensions are in fraction of the font size
|
||
|
"legend.borderpad": validate_float, # units are fontsize
|
||
|
# the vertical space between the legend entries
|
||
|
"legend.labelspacing": validate_float,
|
||
|
# the length of the legend lines
|
||
|
"legend.handlelength": validate_float,
|
||
|
# the length of the legend lines
|
||
|
"legend.handleheight": validate_float,
|
||
|
# the space between the legend line and legend text
|
||
|
"legend.handletextpad": validate_float,
|
||
|
# the border between the axes and legend edge
|
||
|
"legend.borderaxespad": validate_float,
|
||
|
# the border between the axes and legend edge
|
||
|
"legend.columnspacing": validate_float,
|
||
|
"legend.facecolor": validate_color_or_inherit,
|
||
|
"legend.edgecolor": validate_color_or_inherit,
|
||
|
|
||
|
# tick properties
|
||
|
"xtick.top": validate_bool, # draw ticks on top side
|
||
|
"xtick.bottom": validate_bool, # draw ticks on bottom side
|
||
|
"xtick.labeltop": validate_bool, # draw label on top
|
||
|
"xtick.labelbottom": validate_bool, # draw label on bottom
|
||
|
"xtick.major.size": validate_float, # major xtick size in points
|
||
|
"xtick.minor.size": validate_float, # minor xtick size in points
|
||
|
"xtick.major.width": validate_float, # major xtick width in points
|
||
|
"xtick.minor.width": validate_float, # minor xtick width in points
|
||
|
"xtick.major.pad": validate_float, # distance to label in points
|
||
|
"xtick.minor.pad": validate_float, # distance to label in points
|
||
|
"xtick.color": validate_color, # color of xtick labels
|
||
|
"xtick.minor.visible": validate_bool, # visibility of minor xticks
|
||
|
"xtick.minor.top": validate_bool, # draw top minor xticks
|
||
|
"xtick.minor.bottom": validate_bool, # draw bottom minor xticks
|
||
|
"xtick.major.top": validate_bool, # draw top major xticks
|
||
|
"xtick.major.bottom": validate_bool, # draw bottom major xticks
|
||
|
"xtick.labelsize": validate_fontsize, # fontsize of xtick labels
|
||
|
"xtick.direction": validate_string, # direction of xticks
|
||
|
"xtick.alignment": ["center", "right", "left"],
|
||
|
|
||
|
"ytick.left": validate_bool, # draw ticks on left side
|
||
|
"ytick.right": validate_bool, # draw ticks on right side
|
||
|
"ytick.labelleft": validate_bool, # draw tick labels on left side
|
||
|
"ytick.labelright": validate_bool, # draw tick labels on right side
|
||
|
"ytick.major.size": validate_float, # major ytick size in points
|
||
|
"ytick.minor.size": validate_float, # minor ytick size in points
|
||
|
"ytick.major.width": validate_float, # major ytick width in points
|
||
|
"ytick.minor.width": validate_float, # minor ytick width in points
|
||
|
"ytick.major.pad": validate_float, # distance to label in points
|
||
|
"ytick.minor.pad": validate_float, # distance to label in points
|
||
|
"ytick.color": validate_color, # color of ytick labels
|
||
|
"ytick.minor.visible": validate_bool, # visibility of minor yticks
|
||
|
"ytick.minor.left": validate_bool, # draw left minor yticks
|
||
|
"ytick.minor.right": validate_bool, # draw right minor yticks
|
||
|
"ytick.major.left": validate_bool, # draw left major yticks
|
||
|
"ytick.major.right": validate_bool, # draw right major yticks
|
||
|
"ytick.labelsize": validate_fontsize, # fontsize of ytick labels
|
||
|
"ytick.direction": validate_string, # direction of yticks
|
||
|
"ytick.alignment": [
|
||
|
"center", "top", "bottom", "baseline", "center_baseline"],
|
||
|
|
||
|
"grid.color": validate_color, # grid color
|
||
|
"grid.linestyle": _validate_linestyle, # solid
|
||
|
"grid.linewidth": validate_float, # in points
|
||
|
"grid.alpha": validate_float,
|
||
|
|
||
|
## figure props
|
||
|
# figure title
|
||
|
"figure.titlesize": validate_fontsize,
|
||
|
"figure.titleweight": validate_fontweight,
|
||
|
|
||
|
# figure size in inches: width by height
|
||
|
"figure.figsize": _listify_validator(validate_float, n=2),
|
||
|
"figure.dpi": validate_float,
|
||
|
"figure.facecolor": validate_color,
|
||
|
"figure.edgecolor": validate_color,
|
||
|
"figure.frameon": validate_bool,
|
||
|
"figure.autolayout": validate_bool,
|
||
|
"figure.max_open_warning": validate_int,
|
||
|
"figure.raise_window": validate_bool,
|
||
|
|
||
|
"figure.subplot.left": _range_validators["0 <= x <= 1"],
|
||
|
"figure.subplot.right": _range_validators["0 <= x <= 1"],
|
||
|
"figure.subplot.bottom": _range_validators["0 <= x <= 1"],
|
||
|
"figure.subplot.top": _range_validators["0 <= x <= 1"],
|
||
|
"figure.subplot.wspace": _range_validators["0 <= x < 1"],
|
||
|
"figure.subplot.hspace": _range_validators["0 <= x < 1"],
|
||
|
|
||
|
"figure.constrained_layout.use": validate_bool, # run constrained_layout?
|
||
|
# wspace and hspace are fraction of adjacent subplots to use for space.
|
||
|
# Much smaller than above because we don't need room for the text.
|
||
|
"figure.constrained_layout.hspace": _range_validators["0 <= x < 1"],
|
||
|
"figure.constrained_layout.wspace": _range_validators["0 <= x < 1"],
|
||
|
# buffer around the axes, in inches.
|
||
|
'figure.constrained_layout.h_pad': validate_float,
|
||
|
'figure.constrained_layout.w_pad': validate_float,
|
||
|
|
||
|
## Saving figure's properties
|
||
|
'savefig.dpi': validate_dpi,
|
||
|
'savefig.facecolor': validate_color_or_auto,
|
||
|
'savefig.edgecolor': validate_color_or_auto,
|
||
|
'savefig.orientation': ['landscape', 'portrait'],
|
||
|
'savefig.jpeg_quality': validate_int,
|
||
|
"savefig.format": _update_savefig_format,
|
||
|
"savefig.bbox": validate_bbox, # "tight", or "standard" (= None)
|
||
|
"savefig.pad_inches": validate_float,
|
||
|
# default directory in savefig dialog box
|
||
|
"savefig.directory": validate_string,
|
||
|
"savefig.transparent": validate_bool,
|
||
|
|
||
|
"tk.window_focus": validate_bool, # Maintain shell focus for TkAgg
|
||
|
|
||
|
# Set the papersize/type
|
||
|
"ps.papersize": _ignorecase(["auto", "letter", "legal", "ledger",
|
||
|
*[f"{ab}{i}"
|
||
|
for ab in "ab" for i in range(11)]]),
|
||
|
"ps.useafm": validate_bool,
|
||
|
# use ghostscript or xpdf to distill ps output
|
||
|
"ps.usedistiller": validate_ps_distiller,
|
||
|
"ps.distiller.res": validate_int, # dpi
|
||
|
"ps.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype)
|
||
|
"pdf.compression": validate_int, # 0-9 compression level; 0 to disable
|
||
|
"pdf.inheritcolor": validate_bool, # skip color setting commands
|
||
|
# use only the 14 PDF core fonts embedded in every PDF viewing application
|
||
|
"pdf.use14corefonts": validate_bool,
|
||
|
"pdf.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype)
|
||
|
|
||
|
"pgf.texsystem": ["xelatex", "lualatex", "pdflatex"], # latex variant used
|
||
|
"pgf.rcfonts": validate_bool, # use mpl's rc settings for font config
|
||
|
"pgf.preamble": _validate_tex_preamble, # custom LaTeX preamble
|
||
|
|
||
|
# write raster image data into the svg file
|
||
|
"svg.image_inline": validate_bool,
|
||
|
"svg.fonttype": ["none", "path"], # save text as text ("none") or "paths"
|
||
|
"svg.hashsalt": validate_string_or_None,
|
||
|
|
||
|
# set this when you want to generate hardcopy docstring
|
||
|
"docstring.hardcopy": validate_bool,
|
||
|
|
||
|
"path.simplify": validate_bool,
|
||
|
"path.simplify_threshold": _range_validators["0 <= x <= 1"],
|
||
|
"path.snap": validate_bool,
|
||
|
"path.sketch": validate_sketch,
|
||
|
"path.effects": validate_anylist,
|
||
|
"agg.path.chunksize": validate_int, # 0 to disable chunking
|
||
|
|
||
|
# key-mappings (multi-character mappings should be a list/tuple)
|
||
|
"keymap.fullscreen": validate_stringlist,
|
||
|
"keymap.home": validate_stringlist,
|
||
|
"keymap.back": validate_stringlist,
|
||
|
"keymap.forward": validate_stringlist,
|
||
|
"keymap.pan": validate_stringlist,
|
||
|
"keymap.zoom": validate_stringlist,
|
||
|
"keymap.save": validate_stringlist,
|
||
|
"keymap.quit": validate_stringlist,
|
||
|
"keymap.quit_all": validate_stringlist, # e.g.: "W", "cmd+W", "Q"
|
||
|
"keymap.grid": validate_stringlist,
|
||
|
"keymap.grid_minor": validate_stringlist,
|
||
|
"keymap.yscale": validate_stringlist,
|
||
|
"keymap.xscale": validate_stringlist,
|
||
|
"keymap.all_axes": validate_stringlist,
|
||
|
"keymap.help": validate_stringlist,
|
||
|
"keymap.copy": validate_stringlist,
|
||
|
|
||
|
# Animation settings
|
||
|
"animation.html": ["html5", "jshtml", "none"],
|
||
|
# Limit, in MB, of size of base64 encoded animation in HTML
|
||
|
# (i.e. IPython notebook)
|
||
|
"animation.embed_limit": validate_float,
|
||
|
"animation.writer": validate_string,
|
||
|
"animation.codec": validate_string,
|
||
|
"animation.bitrate": validate_int,
|
||
|
# Controls image format when frames are written to disk
|
||
|
"animation.frame_format": ["png", "jpeg", "tiff", "raw", "rgba"],
|
||
|
# 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,
|
||
|
# 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,
|
||
|
# 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,
|
||
|
# Additional arguments for convert movie writer (using pipes)
|
||
|
"animation.convert_args": validate_stringlist,
|
||
|
|
||
|
"mpl_toolkits.legacy_colorbar": validate_bool,
|
||
|
|
||
|
# Classic (pre 2.0) compatibility mode
|
||
|
# This is used for things that are hard to make backward compatible
|
||
|
# with a sane rcParam alone. This does *not* turn on classic mode
|
||
|
# altogether. For that use `matplotlib.style.use("classic")`.
|
||
|
"_internal.classic_mode": validate_bool
|
||
|
}
|
||
|
_hardcoded_defaults = { # Defaults not inferred from matplotlibrc.template...
|
||
|
# ... because it can"t be:
|
||
|
"backend": _auto_backend_sentinel,
|
||
|
# ... because they are private:
|
||
|
"_internal.classic_mode": False,
|
||
|
# ... because they are deprecated:
|
||
|
"animation.avconv_path": "avconv",
|
||
|
"animation.avconv_args": [],
|
||
|
"animation.html_args": [],
|
||
|
"mathtext.fallback_to_cm": None,
|
||
|
"keymap.all_axes": ["a"],
|
||
|
"savefig.jpeg_quality": 95,
|
||
|
"text.latex.preview": False,
|
||
|
}
|
||
|
_validators = {k: _convert_validator_spec(k, conv)
|
||
|
for k, conv in _validators.items()}
|