1"""
2The rcsetup module contains the validation code for customization using
3Matplotlib's rc settings.
4
5Each rc setting is assigned a function used to validate any attempted changes
6to that setting. The validation functions are defined in the rcsetup module,
7and are used to construct the rcParams global object which stores the settings
8and is referenced throughout Matplotlib.
9
10The default values of the rc settings are set in the default matplotlibrc file.
11Any additions or deletions to the parameter set listed here should also be
12propagated to the :file:`lib/matplotlib/mpl-data/matplotlibrc` in Matplotlib's
13root source directory.
14"""
15
16import ast
17from functools import lru_cache, reduce
18from numbers import Real
19import operator
20import os
21import re
22
23import numpy as np
24
25from matplotlib import _api, cbook
26from matplotlib.backends import BackendFilter, backend_registry
27from matplotlib.cbook import ls_mapper
28from matplotlib.colors import Colormap, is_color_like
29from matplotlib._fontconfig_pattern import parse_fontconfig_pattern
30from matplotlib._enums import JoinStyle, CapStyle
31
32# Don't let the original cycler collide with our validating cycler
33from cycler import Cycler, cycler as ccycler
34
35
36@_api.caching_module_getattr
37class __getattr__:
38 @_api.deprecated(
39 "3.9",
40 alternative="``matplotlib.backends.backend_registry.list_builtin"
41 "(matplotlib.backends.BackendFilter.INTERACTIVE)``")
42 @property
43 def interactive_bk(self):
44 return backend_registry.list_builtin(BackendFilter.INTERACTIVE)
45
46 @_api.deprecated(
47 "3.9",
48 alternative="``matplotlib.backends.backend_registry.list_builtin"
49 "(matplotlib.backends.BackendFilter.NON_INTERACTIVE)``")
50 @property
51 def non_interactive_bk(self):
52 return backend_registry.list_builtin(BackendFilter.NON_INTERACTIVE)
53
54 @_api.deprecated(
55 "3.9",
56 alternative="``matplotlib.backends.backend_registry.list_builtin()``")
57 @property
58 def all_backends(self):
59 return backend_registry.list_builtin()
60
61
62class ValidateInStrings:
63 def __init__(self, key, valid, ignorecase=False, *,
64 _deprecated_since=None):
65 """*valid* is a list of legal strings."""
66 self.key = key
67 self.ignorecase = ignorecase
68 self._deprecated_since = _deprecated_since
69
70 def func(s):
71 if ignorecase:
72 return s.lower()
73 else:
74 return s
75 self.valid = {func(k): k for k in valid}
76
77 def __call__(self, s):
78 if self._deprecated_since:
79 name, = (k for k, v in globals().items() if v is self)
80 _api.warn_deprecated(
81 self._deprecated_since, name=name, obj_type="function")
82 if self.ignorecase and isinstance(s, str):
83 s = s.lower()
84 if s in self.valid:
85 return self.valid[s]
86 msg = (f"{s!r} is not a valid value for {self.key}; supported values "
87 f"are {[*self.valid.values()]}")
88 if (isinstance(s, str)
89 and (s.startswith('"') and s.endswith('"')
90 or s.startswith("'") and s.endswith("'"))
91 and s[1:-1] in self.valid):
92 msg += "; remove quotes surrounding your string"
93 raise ValueError(msg)
94
95
96@lru_cache
97def _listify_validator(scalar_validator, allow_stringlist=False, *,
98 n=None, doc=None):
99 def f(s):
100 if isinstance(s, str):
101 try:
102 val = [scalar_validator(v.strip()) for v in s.split(',')
103 if v.strip()]
104 except Exception:
105 if allow_stringlist:
106 # Sometimes, a list of colors might be a single string
107 # of single-letter colornames. So give that a shot.
108 val = [scalar_validator(v.strip()) for v in s if v.strip()]
109 else:
110 raise
111 # Allow any ordered sequence type -- generators, np.ndarray, pd.Series
112 # -- but not sets, whose iteration order is non-deterministic.
113 elif np.iterable(s) and not isinstance(s, (set, frozenset)):
114 # The condition on this list comprehension will preserve the
115 # behavior of filtering out any empty strings (behavior was
116 # from the original validate_stringlist()), while allowing
117 # any non-string/text scalar values such as numbers and arrays.
118 val = [scalar_validator(v) for v in s
119 if not isinstance(v, str) or v]
120 else:
121 raise ValueError(
122 f"Expected str or other non-set iterable, but got {s}")
123 if n is not None and len(val) != n:
124 raise ValueError(
125 f"Expected {n} values, but there are {len(val)} values in {s}")
126 return val
127
128 try:
129 f.__name__ = f"{scalar_validator.__name__}list"
130 except AttributeError: # class instance.
131 f.__name__ = f"{type(scalar_validator).__name__}List"
132 f.__qualname__ = f.__qualname__.rsplit(".", 1)[0] + "." + f.__name__
133 f.__doc__ = doc if doc is not None else scalar_validator.__doc__
134 return f
135
136
137def validate_any(s):
138 return s
139validate_anylist = _listify_validator(validate_any)
140
141
142def _validate_date(s):
143 try:
144 np.datetime64(s)
145 return s
146 except ValueError:
147 raise ValueError(
148 f'{s!r} should be a string that can be parsed by numpy.datetime64')
149
150
151def validate_bool(b):
152 """Convert b to ``bool`` or raise."""
153 if isinstance(b, str):
154 b = b.lower()
155 if b in ('t', 'y', 'yes', 'on', 'true', '1', 1, True):
156 return True
157 elif b in ('f', 'n', 'no', 'off', 'false', '0', 0, False):
158 return False
159 else:
160 raise ValueError(f'Cannot convert {b!r} to bool')
161
162
163def validate_axisbelow(s):
164 try:
165 return validate_bool(s)
166 except ValueError:
167 if isinstance(s, str):
168 if s == 'line':
169 return 'line'
170 raise ValueError(f'{s!r} cannot be interpreted as'
171 ' True, False, or "line"')
172
173
174def validate_dpi(s):
175 """Confirm s is string 'figure' or convert s to float or raise."""
176 if s == 'figure':
177 return s
178 try:
179 return float(s)
180 except ValueError as e:
181 raise ValueError(f'{s!r} is not string "figure" and '
182 f'could not convert {s!r} to float') from e
183
184
185def _make_type_validator(cls, *, allow_none=False):
186 """
187 Return a validator that converts inputs to *cls* or raises (and possibly
188 allows ``None`` as well).
189 """
190
191 def validator(s):
192 if (allow_none and
193 (s is None or cbook._str_lower_equal(s, "none"))):
194 return None
195 if cls is str and not isinstance(s, str):
196 raise ValueError(f'Could not convert {s!r} to str')
197 try:
198 return cls(s)
199 except (TypeError, ValueError) as e:
200 raise ValueError(
201 f'Could not convert {s!r} to {cls.__name__}') from e
202
203 validator.__name__ = f"validate_{cls.__name__}"
204 if allow_none:
205 validator.__name__ += "_or_None"
206 validator.__qualname__ = (
207 validator.__qualname__.rsplit(".", 1)[0] + "." + validator.__name__)
208 return validator
209
210
211validate_string = _make_type_validator(str)
212validate_string_or_None = _make_type_validator(str, allow_none=True)
213validate_stringlist = _listify_validator(
214 validate_string, doc='return a list of strings')
215validate_int = _make_type_validator(int)
216validate_int_or_None = _make_type_validator(int, allow_none=True)
217validate_float = _make_type_validator(float)
218validate_float_or_None = _make_type_validator(float, allow_none=True)
219validate_floatlist = _listify_validator(
220 validate_float, doc='return a list of floats')
221
222
223def _validate_marker(s):
224 try:
225 return validate_int(s)
226 except ValueError as e:
227 try:
228 return validate_string(s)
229 except ValueError as e:
230 raise ValueError('Supported markers are [string, int]') from e
231
232
233_validate_markerlist = _listify_validator(
234 _validate_marker, doc='return a list of markers')
235
236
237def _validate_pathlike(s):
238 if isinstance(s, (str, os.PathLike)):
239 # Store value as str because savefig.directory needs to distinguish
240 # between "" (cwd) and "." (cwd, but gets updated by user selections).
241 return os.fsdecode(s)
242 else:
243 return validate_string(s)
244
245
246def validate_fonttype(s):
247 """
248 Confirm that this is a Postscript or PDF font type that we know how to
249 convert to.
250 """
251 fonttypes = {'type3': 3,
252 'truetype': 42}
253 try:
254 fonttype = validate_int(s)
255 except ValueError:
256 try:
257 return fonttypes[s.lower()]
258 except KeyError as e:
259 raise ValueError('Supported Postscript/PDF font types are %s'
260 % list(fonttypes)) from e
261 else:
262 if fonttype not in fonttypes.values():
263 raise ValueError(
264 'Supported Postscript/PDF font types are %s' %
265 list(fonttypes.values()))
266 return fonttype
267
268
269_auto_backend_sentinel = object()
270
271
272def validate_backend(s):
273 if s is _auto_backend_sentinel or backend_registry.is_valid_backend(s):
274 return s
275 else:
276 msg = (f"'{s}' is not a valid value for backend; supported values are "
277 f"{backend_registry.list_all()}")
278 raise ValueError(msg)
279
280
281def _validate_toolbar(s):
282 s = ValidateInStrings(
283 'toolbar', ['None', 'toolbar2', 'toolmanager'], ignorecase=True)(s)
284 if s == 'toolmanager':
285 _api.warn_external(
286 "Treat the new Tool classes introduced in v1.5 as experimental "
287 "for now; the API and rcParam may change in future versions.")
288 return s
289
290
291def validate_color_or_inherit(s):
292 """Return a valid color arg."""
293 if cbook._str_equal(s, 'inherit'):
294 return s
295 return validate_color(s)
296
297
298def validate_color_or_auto(s):
299 if cbook._str_equal(s, 'auto'):
300 return s
301 return validate_color(s)
302
303
304def validate_color_for_prop_cycle(s):
305 # N-th color cycle syntax can't go into the color cycle.
306 if isinstance(s, str) and re.match("^C[0-9]$", s):
307 raise ValueError(f"Cannot put cycle reference ({s!r}) in prop_cycler")
308 return validate_color(s)
309
310
311def _validate_color_or_linecolor(s):
312 if cbook._str_equal(s, 'linecolor'):
313 return s
314 elif cbook._str_equal(s, 'mfc') or cbook._str_equal(s, 'markerfacecolor'):
315 return 'markerfacecolor'
316 elif cbook._str_equal(s, 'mec') or cbook._str_equal(s, 'markeredgecolor'):
317 return 'markeredgecolor'
318 elif s is None:
319 return None
320 elif isinstance(s, str) and len(s) == 6 or len(s) == 8:
321 stmp = '#' + s
322 if is_color_like(stmp):
323 return stmp
324 if s.lower() == 'none':
325 return None
326 elif is_color_like(s):
327 return s
328
329 raise ValueError(f'{s!r} does not look like a color arg')
330
331
332def validate_color(s):
333 """Return a valid color arg."""
334 if isinstance(s, str):
335 if s.lower() == 'none':
336 return 'none'
337 if len(s) == 6 or len(s) == 8:
338 stmp = '#' + s
339 if is_color_like(stmp):
340 return stmp
341
342 if is_color_like(s):
343 return s
344
345 # If it is still valid, it must be a tuple (as a string from matplotlibrc).
346 try:
347 color = ast.literal_eval(s)
348 except (SyntaxError, ValueError):
349 pass
350 else:
351 if is_color_like(color):
352 return color
353
354 raise ValueError(f'{s!r} does not look like a color arg')
355
356
357validate_colorlist = _listify_validator(
358 validate_color, allow_stringlist=True, doc='return a list of colorspecs')
359
360
361def _validate_cmap(s):
362 _api.check_isinstance((str, Colormap), cmap=s)
363 return s
364
365
366def validate_aspect(s):
367 if s in ('auto', 'equal'):
368 return s
369 try:
370 return float(s)
371 except ValueError as e:
372 raise ValueError('not a valid aspect specification') from e
373
374
375def validate_fontsize_None(s):
376 if s is None or s == 'None':
377 return None
378 else:
379 return validate_fontsize(s)
380
381
382def validate_fontsize(s):
383 fontsizes = ['xx-small', 'x-small', 'small', 'medium', 'large',
384 'x-large', 'xx-large', 'smaller', 'larger']
385 if isinstance(s, str):
386 s = s.lower()
387 if s in fontsizes:
388 return s
389 try:
390 return float(s)
391 except ValueError as e:
392 raise ValueError("%s is not a valid font size. Valid font sizes "
393 "are %s." % (s, ", ".join(fontsizes))) from e
394
395
396validate_fontsizelist = _listify_validator(validate_fontsize)
397
398
399def validate_fontweight(s):
400 weights = [
401 'ultralight', 'light', 'normal', 'regular', 'book', 'medium', 'roman',
402 'semibold', 'demibold', 'demi', 'bold', 'heavy', 'extra bold', 'black']
403 # Note: Historically, weights have been case-sensitive in Matplotlib
404 if s in weights:
405 return s
406 try:
407 return int(s)
408 except (ValueError, TypeError) as e:
409 raise ValueError(f'{s} is not a valid font weight.') from e
410
411
412def validate_fontstretch(s):
413 stretchvalues = [
414 'ultra-condensed', 'extra-condensed', 'condensed', 'semi-condensed',
415 'normal', 'semi-expanded', 'expanded', 'extra-expanded',
416 'ultra-expanded']
417 # Note: Historically, stretchvalues have been case-sensitive in Matplotlib
418 if s in stretchvalues:
419 return s
420 try:
421 return int(s)
422 except (ValueError, TypeError) as e:
423 raise ValueError(f'{s} is not a valid font stretch.') from e
424
425
426def validate_font_properties(s):
427 parse_fontconfig_pattern(s)
428 return s
429
430
431def _validate_mathtext_fallback(s):
432 _fallback_fonts = ['cm', 'stix', 'stixsans']
433 if isinstance(s, str):
434 s = s.lower()
435 if s is None or s == 'none':
436 return None
437 elif s.lower() in _fallback_fonts:
438 return s
439 else:
440 raise ValueError(
441 f"{s} is not a valid fallback font name. Valid fallback font "
442 f"names are {','.join(_fallback_fonts)}. Passing 'None' will turn "
443 "fallback off.")
444
445
446def validate_whiskers(s):
447 try:
448 return _listify_validator(validate_float, n=2)(s)
449 except (TypeError, ValueError):
450 try:
451 return float(s)
452 except ValueError as e:
453 raise ValueError("Not a valid whisker value [float, "
454 "(float, float)]") from e
455
456
457def validate_ps_distiller(s):
458 if isinstance(s, str):
459 s = s.lower()
460 if s in ('none', None, 'false', False):
461 return None
462 else:
463 return ValidateInStrings('ps.usedistiller', ['ghostscript', 'xpdf'])(s)
464
465
466def _validate_papersize(s):
467 # Re-inline this validator when the 'auto' deprecation expires.
468 s = ValidateInStrings("ps.papersize",
469 ["figure", "auto", "letter", "legal", "ledger",
470 *[f"{ab}{i}" for ab in "ab" for i in range(11)]],
471 ignorecase=True)(s)
472 if s == "auto":
473 _api.warn_deprecated("3.8", name="ps.papersize='auto'",
474 addendum="Pass an explicit paper type, figure, or omit "
475 "the *ps.papersize* rcParam entirely.")
476 return s
477
478
479# A validator dedicated to the named line styles, based on the items in
480# ls_mapper, and a list of possible strings read from Line2D.set_linestyle
481_validate_named_linestyle = ValidateInStrings(
482 'linestyle',
483 [*ls_mapper.keys(), *ls_mapper.values(), 'None', 'none', ' ', ''],
484 ignorecase=True)
485
486
487def _validate_linestyle(ls):
488 """
489 A validator for all possible line styles, the named ones *and*
490 the on-off ink sequences.
491 """
492 if isinstance(ls, str):
493 try: # Look first for a valid named line style, like '--' or 'solid'.
494 return _validate_named_linestyle(ls)
495 except ValueError:
496 pass
497 try:
498 ls = ast.literal_eval(ls) # Parsing matplotlibrc.
499 except (SyntaxError, ValueError):
500 pass # Will error with the ValueError at the end.
501
502 def _is_iterable_not_string_like(x):
503 # Explicitly exclude bytes/bytearrays so that they are not
504 # nonsensically interpreted as sequences of numbers (codepoints).
505 return np.iterable(x) and not isinstance(x, (str, bytes, bytearray))
506
507 if _is_iterable_not_string_like(ls):
508 if len(ls) == 2 and _is_iterable_not_string_like(ls[1]):
509 # (offset, (on, off, on, off, ...))
510 offset, onoff = ls
511 else:
512 # For backcompat: (on, off, on, off, ...); the offset is implicit.
513 offset = 0
514 onoff = ls
515
516 if (isinstance(offset, Real)
517 and len(onoff) % 2 == 0
518 and all(isinstance(elem, Real) for elem in onoff)):
519 return (offset, onoff)
520
521 raise ValueError(f"linestyle {ls!r} is not a valid on-off ink sequence.")
522
523
524validate_fillstyle = ValidateInStrings(
525 'markers.fillstyle', ['full', 'left', 'right', 'bottom', 'top', 'none'])
526
527
528validate_fillstylelist = _listify_validator(validate_fillstyle)
529
530
531def validate_markevery(s):
532 """
533 Validate the markevery property of a Line2D object.
534
535 Parameters
536 ----------
537 s : None, int, (int, int), slice, float, (float, float), or list[int]
538
539 Returns
540 -------
541 None, int, (int, int), slice, float, (float, float), or list[int]
542 """
543 # Validate s against type slice float int and None
544 if isinstance(s, (slice, float, int, type(None))):
545 return s
546 # Validate s against type tuple
547 if isinstance(s, tuple):
548 if (len(s) == 2
549 and (all(isinstance(e, int) for e in s)
550 or all(isinstance(e, float) for e in s))):
551 return s
552 else:
553 raise TypeError(
554 "'markevery' tuple must be pair of ints or of floats")
555 # Validate s against type list
556 if isinstance(s, list):
557 if all(isinstance(e, int) for e in s):
558 return s
559 else:
560 raise TypeError(
561 "'markevery' list must have all elements of type int")
562 raise TypeError("'markevery' is of an invalid type")
563
564
565validate_markeverylist = _listify_validator(validate_markevery)
566
567
568def validate_bbox(s):
569 if isinstance(s, str):
570 s = s.lower()
571 if s == 'tight':
572 return s
573 if s == 'standard':
574 return None
575 raise ValueError("bbox should be 'tight' or 'standard'")
576 elif s is not None:
577 # Backwards compatibility. None is equivalent to 'standard'.
578 raise ValueError("bbox should be 'tight' or 'standard'")
579 return s
580
581
582def validate_sketch(s):
583
584 if isinstance(s, str):
585 s = s.lower().strip()
586 if s.startswith("(") and s.endswith(")"):
587 s = s[1:-1]
588 if s == 'none' or s is None:
589 return None
590 try:
591 return tuple(_listify_validator(validate_float, n=3)(s))
592 except ValueError as exc:
593 raise ValueError("Expected a (scale, length, randomness) tuple") from exc
594
595
596def _validate_greaterthan_minushalf(s):
597 s = validate_float(s)
598 if s > -0.5:
599 return s
600 else:
601 raise RuntimeError(f'Value must be >-0.5; got {s}')
602
603
604def _validate_greaterequal0_lessequal1(s):
605 s = validate_float(s)
606 if 0 <= s <= 1:
607 return s
608 else:
609 raise RuntimeError(f'Value must be >=0 and <=1; got {s}')
610
611
612def _validate_int_greaterequal0(s):
613 s = validate_int(s)
614 if s >= 0:
615 return s
616 else:
617 raise RuntimeError(f'Value must be >=0; got {s}')
618
619
620def validate_hatch(s):
621 r"""
622 Validate a hatch pattern.
623 A hatch pattern string can have any sequence of the following
624 characters: ``\ / | - + * . x o O``.
625 """
626 if not isinstance(s, str):
627 raise ValueError("Hatch pattern must be a string")
628 _api.check_isinstance(str, hatch_pattern=s)
629 unknown = set(s) - {'\\', '/', '|', '-', '+', '*', '.', 'x', 'o', 'O'}
630 if unknown:
631 raise ValueError("Unknown hatch symbol(s): %s" % list(unknown))
632 return s
633
634
635validate_hatchlist = _listify_validator(validate_hatch)
636validate_dashlist = _listify_validator(validate_floatlist)
637
638
639def _validate_minor_tick_ndivs(n):
640 """
641 Validate ndiv parameter related to the minor ticks.
642 It controls the number of minor ticks to be placed between
643 two major ticks.
644 """
645
646 if cbook._str_lower_equal(n, 'auto'):
647 return n
648 try:
649 n = _validate_int_greaterequal0(n)
650 return n
651 except (RuntimeError, ValueError):
652 pass
653
654 raise ValueError("'tick.minor.ndivs' must be 'auto' or non-negative int")
655
656
657_prop_validators = {
658 'color': _listify_validator(validate_color_for_prop_cycle,
659 allow_stringlist=True),
660 'linewidth': validate_floatlist,
661 'linestyle': _listify_validator(_validate_linestyle),
662 'facecolor': validate_colorlist,
663 'edgecolor': validate_colorlist,
664 'joinstyle': _listify_validator(JoinStyle),
665 'capstyle': _listify_validator(CapStyle),
666 'fillstyle': validate_fillstylelist,
667 'markerfacecolor': validate_colorlist,
668 'markersize': validate_floatlist,
669 'markeredgewidth': validate_floatlist,
670 'markeredgecolor': validate_colorlist,
671 'markevery': validate_markeverylist,
672 'alpha': validate_floatlist,
673 'marker': _validate_markerlist,
674 'hatch': validate_hatchlist,
675 'dashes': validate_dashlist,
676 }
677_prop_aliases = {
678 'c': 'color',
679 'lw': 'linewidth',
680 'ls': 'linestyle',
681 'fc': 'facecolor',
682 'ec': 'edgecolor',
683 'mfc': 'markerfacecolor',
684 'mec': 'markeredgecolor',
685 'mew': 'markeredgewidth',
686 'ms': 'markersize',
687 }
688
689
690def cycler(*args, **kwargs):
691 """
692 Create a `~cycler.Cycler` object much like :func:`cycler.cycler`,
693 but includes input validation.
694
695 Call signatures::
696
697 cycler(cycler)
698 cycler(label=values[, label2=values2[, ...]])
699 cycler(label, values)
700
701 Form 1 copies a given `~cycler.Cycler` object.
702
703 Form 2 creates a `~cycler.Cycler` which cycles over one or more
704 properties simultaneously. If multiple properties are given, their
705 value lists must have the same length.
706
707 Form 3 creates a `~cycler.Cycler` for a single property. This form
708 exists for compatibility with the original cycler. Its use is
709 discouraged in favor of the kwarg form, i.e. ``cycler(label=values)``.
710
711 Parameters
712 ----------
713 cycler : Cycler
714 Copy constructor for Cycler.
715
716 label : str
717 The property key. Must be a valid `.Artist` property.
718 For example, 'color' or 'linestyle'. Aliases are allowed,
719 such as 'c' for 'color' and 'lw' for 'linewidth'.
720
721 values : iterable
722 Finite-length iterable of the property values. These values
723 are validated and will raise a ValueError if invalid.
724
725 Returns
726 -------
727 Cycler
728 A new :class:`~cycler.Cycler` for the given properties.
729
730 Examples
731 --------
732 Creating a cycler for a single property:
733
734 >>> c = cycler(color=['red', 'green', 'blue'])
735
736 Creating a cycler for simultaneously cycling over multiple properties
737 (e.g. red circle, green plus, blue cross):
738
739 >>> c = cycler(color=['red', 'green', 'blue'],
740 ... marker=['o', '+', 'x'])
741
742 """
743 if args and kwargs:
744 raise TypeError("cycler() can only accept positional OR keyword "
745 "arguments -- not both.")
746 elif not args and not kwargs:
747 raise TypeError("cycler() must have positional OR keyword arguments")
748
749 if len(args) == 1:
750 if not isinstance(args[0], Cycler):
751 raise TypeError("If only one positional argument given, it must "
752 "be a Cycler instance.")
753 return validate_cycler(args[0])
754 elif len(args) == 2:
755 pairs = [(args[0], args[1])]
756 elif len(args) > 2:
757 raise _api.nargs_error('cycler', '0-2', len(args))
758 else:
759 pairs = kwargs.items()
760
761 validated = []
762 for prop, vals in pairs:
763 norm_prop = _prop_aliases.get(prop, prop)
764 validator = _prop_validators.get(norm_prop, None)
765 if validator is None:
766 raise TypeError("Unknown artist property: %s" % prop)
767 vals = validator(vals)
768 # We will normalize the property names as well to reduce
769 # the amount of alias handling code elsewhere.
770 validated.append((norm_prop, vals))
771
772 return reduce(operator.add, (ccycler(k, v) for k, v in validated))
773
774
775class _DunderChecker(ast.NodeVisitor):
776 def visit_Attribute(self, node):
777 if node.attr.startswith("__") and node.attr.endswith("__"):
778 raise ValueError("cycler strings with dunders are forbidden")
779 self.generic_visit(node)
780
781
782# A validator dedicated to the named legend loc
783_validate_named_legend_loc = ValidateInStrings(
784 'legend.loc',
785 [
786 "best",
787 "upper right", "upper left", "lower left", "lower right", "right",
788 "center left", "center right", "lower center", "upper center",
789 "center"],
790 ignorecase=True)
791
792
793def _validate_legend_loc(loc):
794 """
795 Confirm that loc is a type which rc.Params["legend.loc"] supports.
796
797 .. versionadded:: 3.8
798
799 Parameters
800 ----------
801 loc : str | int | (float, float) | str((float, float))
802 The location of the legend.
803
804 Returns
805 -------
806 loc : str | int | (float, float) or raise ValueError exception
807 The location of the legend.
808 """
809 if isinstance(loc, str):
810 try:
811 return _validate_named_legend_loc(loc)
812 except ValueError:
813 pass
814 try:
815 loc = ast.literal_eval(loc)
816 except (SyntaxError, ValueError):
817 pass
818 if isinstance(loc, int):
819 if 0 <= loc <= 10:
820 return loc
821 if isinstance(loc, tuple):
822 if len(loc) == 2 and all(isinstance(e, Real) for e in loc):
823 return loc
824 raise ValueError(f"{loc} is not a valid legend location.")
825
826
827def validate_cycler(s):
828 """Return a Cycler object from a string repr or the object itself."""
829 if isinstance(s, str):
830 # TODO: We might want to rethink this...
831 # While I think I have it quite locked down, it is execution of
832 # arbitrary code without sanitation.
833 # Combine this with the possibility that rcparams might come from the
834 # internet (future plans), this could be downright dangerous.
835 # I locked it down by only having the 'cycler()' function available.
836 # UPDATE: Partly plugging a security hole.
837 # I really should have read this:
838 # https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html
839 # We should replace this eval with a combo of PyParsing and
840 # ast.literal_eval()
841 try:
842 _DunderChecker().visit(ast.parse(s))
843 s = eval(s, {'cycler': cycler, '__builtins__': {}})
844 except BaseException as e:
845 raise ValueError(f"{s!r} is not a valid cycler construction: {e}"
846 ) from e
847 # Should make sure what comes from the above eval()
848 # is a Cycler object.
849 if isinstance(s, Cycler):
850 cycler_inst = s
851 else:
852 raise ValueError(f"Object is not a string or Cycler instance: {s!r}")
853
854 unknowns = cycler_inst.keys - (set(_prop_validators) | set(_prop_aliases))
855 if unknowns:
856 raise ValueError("Unknown artist properties: %s" % unknowns)
857
858 # Not a full validation, but it'll at least normalize property names
859 # A fuller validation would require v0.10 of cycler.
860 checker = set()
861 for prop in cycler_inst.keys:
862 norm_prop = _prop_aliases.get(prop, prop)
863 if norm_prop != prop and norm_prop in cycler_inst.keys:
864 raise ValueError(f"Cannot specify both {norm_prop!r} and alias "
865 f"{prop!r} in the same prop_cycle")
866 if norm_prop in checker:
867 raise ValueError(f"Another property was already aliased to "
868 f"{norm_prop!r}. Collision normalizing {prop!r}.")
869 checker.update([norm_prop])
870
871 # This is just an extra-careful check, just in case there is some
872 # edge-case I haven't thought of.
873 assert len(checker) == len(cycler_inst.keys)
874
875 # Now, it should be safe to mutate this cycler
876 for prop in cycler_inst.keys:
877 norm_prop = _prop_aliases.get(prop, prop)
878 cycler_inst.change_key(prop, norm_prop)
879
880 for key, vals in cycler_inst.by_key().items():
881 _prop_validators[key](vals)
882
883 return cycler_inst
884
885
886def validate_hist_bins(s):
887 valid_strs = ["auto", "sturges", "fd", "doane", "scott", "rice", "sqrt"]
888 if isinstance(s, str) and s in valid_strs:
889 return s
890 try:
891 return int(s)
892 except (TypeError, ValueError):
893 pass
894 try:
895 return validate_floatlist(s)
896 except ValueError:
897 pass
898 raise ValueError(f"'hist.bins' must be one of {valid_strs}, an int or"
899 " a sequence of floats")
900
901
902class _ignorecase(list):
903 """A marker class indicating that a list-of-str is case-insensitive."""
904
905
906def _convert_validator_spec(key, conv):
907 if isinstance(conv, list):
908 ignorecase = isinstance(conv, _ignorecase)
909 return ValidateInStrings(key, conv, ignorecase=ignorecase)
910 else:
911 return conv
912
913
914# Mapping of rcParams to validators.
915# Converters given as lists or _ignorecase are converted to ValidateInStrings
916# immediately below.
917# The rcParams defaults are defined in lib/matplotlib/mpl-data/matplotlibrc, which
918# gets copied to matplotlib/mpl-data/matplotlibrc by the setup script.
919_validators = {
920 "backend": validate_backend,
921 "backend_fallback": validate_bool,
922 "figure.hooks": validate_stringlist,
923 "toolbar": _validate_toolbar,
924 "interactive": validate_bool,
925 "timezone": validate_string,
926
927 "webagg.port": validate_int,
928 "webagg.address": validate_string,
929 "webagg.open_in_browser": validate_bool,
930 "webagg.port_retries": validate_int,
931
932 # line props
933 "lines.linewidth": validate_float, # line width in points
934 "lines.linestyle": _validate_linestyle, # solid line
935 "lines.color": validate_color, # first color in color cycle
936 "lines.marker": _validate_marker, # marker name
937 "lines.markerfacecolor": validate_color_or_auto, # default color
938 "lines.markeredgecolor": validate_color_or_auto, # default color
939 "lines.markeredgewidth": validate_float,
940 "lines.markersize": validate_float, # markersize, in points
941 "lines.antialiased": validate_bool, # antialiased (no jaggies)
942 "lines.dash_joinstyle": JoinStyle,
943 "lines.solid_joinstyle": JoinStyle,
944 "lines.dash_capstyle": CapStyle,
945 "lines.solid_capstyle": CapStyle,
946 "lines.dashed_pattern": validate_floatlist,
947 "lines.dashdot_pattern": validate_floatlist,
948 "lines.dotted_pattern": validate_floatlist,
949 "lines.scale_dashes": validate_bool,
950
951 # marker props
952 "markers.fillstyle": validate_fillstyle,
953
954 ## pcolor(mesh) props:
955 "pcolor.shading": ["auto", "flat", "nearest", "gouraud"],
956 "pcolormesh.snap": validate_bool,
957
958 ## patch props
959 "patch.linewidth": validate_float, # line width in points
960 "patch.edgecolor": validate_color,
961 "patch.force_edgecolor": validate_bool,
962 "patch.facecolor": validate_color, # first color in cycle
963 "patch.antialiased": validate_bool, # antialiased (no jaggies)
964
965 ## hatch props
966 "hatch.color": validate_color,
967 "hatch.linewidth": validate_float,
968
969 ## Histogram properties
970 "hist.bins": validate_hist_bins,
971
972 ## Boxplot properties
973 "boxplot.notch": validate_bool,
974 "boxplot.vertical": validate_bool,
975 "boxplot.whiskers": validate_whiskers,
976 "boxplot.bootstrap": validate_int_or_None,
977 "boxplot.patchartist": validate_bool,
978 "boxplot.showmeans": validate_bool,
979 "boxplot.showcaps": validate_bool,
980 "boxplot.showbox": validate_bool,
981 "boxplot.showfliers": validate_bool,
982 "boxplot.meanline": validate_bool,
983
984 "boxplot.flierprops.color": validate_color,
985 "boxplot.flierprops.marker": _validate_marker,
986 "boxplot.flierprops.markerfacecolor": validate_color_or_auto,
987 "boxplot.flierprops.markeredgecolor": validate_color,
988 "boxplot.flierprops.markeredgewidth": validate_float,
989 "boxplot.flierprops.markersize": validate_float,
990 "boxplot.flierprops.linestyle": _validate_linestyle,
991 "boxplot.flierprops.linewidth": validate_float,
992
993 "boxplot.boxprops.color": validate_color,
994 "boxplot.boxprops.linewidth": validate_float,
995 "boxplot.boxprops.linestyle": _validate_linestyle,
996
997 "boxplot.whiskerprops.color": validate_color,
998 "boxplot.whiskerprops.linewidth": validate_float,
999 "boxplot.whiskerprops.linestyle": _validate_linestyle,
1000
1001 "boxplot.capprops.color": validate_color,
1002 "boxplot.capprops.linewidth": validate_float,
1003 "boxplot.capprops.linestyle": _validate_linestyle,
1004
1005 "boxplot.medianprops.color": validate_color,
1006 "boxplot.medianprops.linewidth": validate_float,
1007 "boxplot.medianprops.linestyle": _validate_linestyle,
1008
1009 "boxplot.meanprops.color": validate_color,
1010 "boxplot.meanprops.marker": _validate_marker,
1011 "boxplot.meanprops.markerfacecolor": validate_color,
1012 "boxplot.meanprops.markeredgecolor": validate_color,
1013 "boxplot.meanprops.markersize": validate_float,
1014 "boxplot.meanprops.linestyle": _validate_linestyle,
1015 "boxplot.meanprops.linewidth": validate_float,
1016
1017 ## font props
1018 "font.family": validate_stringlist, # used by text object
1019 "font.style": validate_string,
1020 "font.variant": validate_string,
1021 "font.stretch": validate_fontstretch,
1022 "font.weight": validate_fontweight,
1023 "font.size": validate_float, # Base font size in points
1024 "font.serif": validate_stringlist,
1025 "font.sans-serif": validate_stringlist,
1026 "font.cursive": validate_stringlist,
1027 "font.fantasy": validate_stringlist,
1028 "font.monospace": validate_stringlist,
1029
1030 # text props
1031 "text.color": validate_color,
1032 "text.usetex": validate_bool,
1033 "text.latex.preamble": validate_string,
1034 "text.hinting": ["default", "no_autohint", "force_autohint",
1035 "no_hinting", "auto", "native", "either", "none"],
1036 "text.hinting_factor": validate_int,
1037 "text.kerning_factor": validate_int,
1038 "text.antialiased": validate_bool,
1039 "text.parse_math": validate_bool,
1040
1041 "mathtext.cal": validate_font_properties,
1042 "mathtext.rm": validate_font_properties,
1043 "mathtext.tt": validate_font_properties,
1044 "mathtext.it": validate_font_properties,
1045 "mathtext.bf": validate_font_properties,
1046 "mathtext.bfit": validate_font_properties,
1047 "mathtext.sf": validate_font_properties,
1048 "mathtext.fontset": ["dejavusans", "dejavuserif", "cm", "stix",
1049 "stixsans", "custom"],
1050 "mathtext.default": ["rm", "cal", "bfit", "it", "tt", "sf", "bf", "default",
1051 "bb", "frak", "scr", "regular"],
1052 "mathtext.fallback": _validate_mathtext_fallback,
1053
1054 "image.aspect": validate_aspect, # equal, auto, a number
1055 "image.interpolation": validate_string,
1056 "image.interpolation_stage": ["data", "rgba"],
1057 "image.cmap": _validate_cmap, # gray, jet, etc.
1058 "image.lut": validate_int, # lookup table
1059 "image.origin": ["upper", "lower"],
1060 "image.resample": validate_bool,
1061 # Specify whether vector graphics backends will combine all images on a
1062 # set of Axes into a single composite image
1063 "image.composite_image": validate_bool,
1064
1065 # contour props
1066 "contour.negative_linestyle": _validate_linestyle,
1067 "contour.corner_mask": validate_bool,
1068 "contour.linewidth": validate_float_or_None,
1069 "contour.algorithm": ["mpl2005", "mpl2014", "serial", "threaded"],
1070
1071 # errorbar props
1072 "errorbar.capsize": validate_float,
1073
1074 # axis props
1075 # alignment of x/y axis title
1076 "xaxis.labellocation": ["left", "center", "right"],
1077 "yaxis.labellocation": ["bottom", "center", "top"],
1078
1079 # Axes props
1080 "axes.axisbelow": validate_axisbelow,
1081 "axes.facecolor": validate_color, # background color
1082 "axes.edgecolor": validate_color, # edge color
1083 "axes.linewidth": validate_float, # edge linewidth
1084
1085 "axes.spines.left": validate_bool, # Set visibility of axes spines,
1086 "axes.spines.right": validate_bool, # i.e., the lines around the chart
1087 "axes.spines.bottom": validate_bool, # denoting data boundary.
1088 "axes.spines.top": validate_bool,
1089
1090 "axes.titlesize": validate_fontsize, # Axes title fontsize
1091 "axes.titlelocation": ["left", "center", "right"], # Axes title alignment
1092 "axes.titleweight": validate_fontweight, # Axes title font weight
1093 "axes.titlecolor": validate_color_or_auto, # Axes title font color
1094 # title location, axes units, None means auto
1095 "axes.titley": validate_float_or_None,
1096 # pad from Axes top decoration to title in points
1097 "axes.titlepad": validate_float,
1098 "axes.grid": validate_bool, # display grid or not
1099 "axes.grid.which": ["minor", "both", "major"], # which grids are drawn
1100 "axes.grid.axis": ["x", "y", "both"], # grid type
1101 "axes.labelsize": validate_fontsize, # fontsize of x & y labels
1102 "axes.labelpad": validate_float, # space between label and axis
1103 "axes.labelweight": validate_fontweight, # fontsize of x & y labels
1104 "axes.labelcolor": validate_color, # color of axis label
1105 # use scientific notation if log10 of the axis range is smaller than the
1106 # first or larger than the second
1107 "axes.formatter.limits": _listify_validator(validate_int, n=2),
1108 # use current locale to format ticks
1109 "axes.formatter.use_locale": validate_bool,
1110 "axes.formatter.use_mathtext": validate_bool,
1111 # minimum exponent to format in scientific notation
1112 "axes.formatter.min_exponent": validate_int,
1113 "axes.formatter.useoffset": validate_bool,
1114 "axes.formatter.offset_threshold": validate_int,
1115 "axes.unicode_minus": validate_bool,
1116 # This entry can be either a cycler object or a string repr of a
1117 # cycler-object, which gets eval()'ed to create the object.
1118 "axes.prop_cycle": validate_cycler,
1119 # If "data", axes limits are set close to the data.
1120 # If "round_numbers" axes limits are set to the nearest round numbers.
1121 "axes.autolimit_mode": ["data", "round_numbers"],
1122 "axes.xmargin": _validate_greaterthan_minushalf, # margin added to xaxis
1123 "axes.ymargin": _validate_greaterthan_minushalf, # margin added to yaxis
1124 "axes.zmargin": _validate_greaterthan_minushalf, # margin added to zaxis
1125
1126 "polaraxes.grid": validate_bool, # display polar grid or not
1127 "axes3d.grid": validate_bool, # display 3d grid
1128 "axes3d.automargin": validate_bool, # automatically add margin when
1129 # manually setting 3D axis limits
1130
1131 "axes3d.xaxis.panecolor": validate_color, # 3d background pane
1132 "axes3d.yaxis.panecolor": validate_color, # 3d background pane
1133 "axes3d.zaxis.panecolor": validate_color, # 3d background pane
1134
1135 # scatter props
1136 "scatter.marker": _validate_marker,
1137 "scatter.edgecolors": validate_string,
1138
1139 "date.epoch": _validate_date,
1140 "date.autoformatter.year": validate_string,
1141 "date.autoformatter.month": validate_string,
1142 "date.autoformatter.day": validate_string,
1143 "date.autoformatter.hour": validate_string,
1144 "date.autoformatter.minute": validate_string,
1145 "date.autoformatter.second": validate_string,
1146 "date.autoformatter.microsecond": validate_string,
1147
1148 'date.converter': ['auto', 'concise'],
1149 # for auto date locator, choose interval_multiples
1150 'date.interval_multiples': validate_bool,
1151
1152 # legend properties
1153 "legend.fancybox": validate_bool,
1154 "legend.loc": _validate_legend_loc,
1155
1156 # the number of points in the legend line
1157 "legend.numpoints": validate_int,
1158 # the number of points in the legend line for scatter
1159 "legend.scatterpoints": validate_int,
1160 "legend.fontsize": validate_fontsize,
1161 "legend.title_fontsize": validate_fontsize_None,
1162 # color of the legend
1163 "legend.labelcolor": _validate_color_or_linecolor,
1164 # the relative size of legend markers vs. original
1165 "legend.markerscale": validate_float,
1166 # using dict in rcParams not yet supported, so make sure it is bool
1167 "legend.shadow": validate_bool,
1168 # whether or not to draw a frame around legend
1169 "legend.frameon": validate_bool,
1170 # alpha value of the legend frame
1171 "legend.framealpha": validate_float_or_None,
1172
1173 ## the following dimensions are in fraction of the font size
1174 "legend.borderpad": validate_float, # units are fontsize
1175 # the vertical space between the legend entries
1176 "legend.labelspacing": validate_float,
1177 # the length of the legend lines
1178 "legend.handlelength": validate_float,
1179 # the length of the legend lines
1180 "legend.handleheight": validate_float,
1181 # the space between the legend line and legend text
1182 "legend.handletextpad": validate_float,
1183 # the border between the Axes and legend edge
1184 "legend.borderaxespad": validate_float,
1185 # the border between the Axes and legend edge
1186 "legend.columnspacing": validate_float,
1187 "legend.facecolor": validate_color_or_inherit,
1188 "legend.edgecolor": validate_color_or_inherit,
1189
1190 # tick properties
1191 "xtick.top": validate_bool, # draw ticks on top side
1192 "xtick.bottom": validate_bool, # draw ticks on bottom side
1193 "xtick.labeltop": validate_bool, # draw label on top
1194 "xtick.labelbottom": validate_bool, # draw label on bottom
1195 "xtick.major.size": validate_float, # major xtick size in points
1196 "xtick.minor.size": validate_float, # minor xtick size in points
1197 "xtick.major.width": validate_float, # major xtick width in points
1198 "xtick.minor.width": validate_float, # minor xtick width in points
1199 "xtick.major.pad": validate_float, # distance to label in points
1200 "xtick.minor.pad": validate_float, # distance to label in points
1201 "xtick.color": validate_color, # color of xticks
1202 "xtick.labelcolor": validate_color_or_inherit, # color of xtick labels
1203 "xtick.minor.visible": validate_bool, # visibility of minor xticks
1204 "xtick.minor.top": validate_bool, # draw top minor xticks
1205 "xtick.minor.bottom": validate_bool, # draw bottom minor xticks
1206 "xtick.major.top": validate_bool, # draw top major xticks
1207 "xtick.major.bottom": validate_bool, # draw bottom major xticks
1208 # number of minor xticks
1209 "xtick.minor.ndivs": _validate_minor_tick_ndivs,
1210 "xtick.labelsize": validate_fontsize, # fontsize of xtick labels
1211 "xtick.direction": ["out", "in", "inout"], # direction of xticks
1212 "xtick.alignment": ["center", "right", "left"],
1213
1214 "ytick.left": validate_bool, # draw ticks on left side
1215 "ytick.right": validate_bool, # draw ticks on right side
1216 "ytick.labelleft": validate_bool, # draw tick labels on left side
1217 "ytick.labelright": validate_bool, # draw tick labels on right side
1218 "ytick.major.size": validate_float, # major ytick size in points
1219 "ytick.minor.size": validate_float, # minor ytick size in points
1220 "ytick.major.width": validate_float, # major ytick width in points
1221 "ytick.minor.width": validate_float, # minor ytick width in points
1222 "ytick.major.pad": validate_float, # distance to label in points
1223 "ytick.minor.pad": validate_float, # distance to label in points
1224 "ytick.color": validate_color, # color of yticks
1225 "ytick.labelcolor": validate_color_or_inherit, # color of ytick labels
1226 "ytick.minor.visible": validate_bool, # visibility of minor yticks
1227 "ytick.minor.left": validate_bool, # draw left minor yticks
1228 "ytick.minor.right": validate_bool, # draw right minor yticks
1229 "ytick.major.left": validate_bool, # draw left major yticks
1230 "ytick.major.right": validate_bool, # draw right major yticks
1231 # number of minor yticks
1232 "ytick.minor.ndivs": _validate_minor_tick_ndivs,
1233 "ytick.labelsize": validate_fontsize, # fontsize of ytick labels
1234 "ytick.direction": ["out", "in", "inout"], # direction of yticks
1235 "ytick.alignment": [
1236 "center", "top", "bottom", "baseline", "center_baseline"],
1237
1238 "grid.color": validate_color, # grid color
1239 "grid.linestyle": _validate_linestyle, # solid
1240 "grid.linewidth": validate_float, # in points
1241 "grid.alpha": validate_float,
1242
1243 ## figure props
1244 # figure title
1245 "figure.titlesize": validate_fontsize,
1246 "figure.titleweight": validate_fontweight,
1247
1248 # figure labels
1249 "figure.labelsize": validate_fontsize,
1250 "figure.labelweight": validate_fontweight,
1251
1252 # figure size in inches: width by height
1253 "figure.figsize": _listify_validator(validate_float, n=2),
1254 "figure.dpi": validate_float,
1255 "figure.facecolor": validate_color,
1256 "figure.edgecolor": validate_color,
1257 "figure.frameon": validate_bool,
1258 "figure.autolayout": validate_bool,
1259 "figure.max_open_warning": validate_int,
1260 "figure.raise_window": validate_bool,
1261 "macosx.window_mode": ["system", "tab", "window"],
1262
1263 "figure.subplot.left": validate_float,
1264 "figure.subplot.right": validate_float,
1265 "figure.subplot.bottom": validate_float,
1266 "figure.subplot.top": validate_float,
1267 "figure.subplot.wspace": validate_float,
1268 "figure.subplot.hspace": validate_float,
1269
1270 "figure.constrained_layout.use": validate_bool, # run constrained_layout?
1271 # wspace and hspace are fraction of adjacent subplots to use for space.
1272 # Much smaller than above because we don't need room for the text.
1273 "figure.constrained_layout.hspace": validate_float,
1274 "figure.constrained_layout.wspace": validate_float,
1275 # buffer around the Axes, in inches.
1276 "figure.constrained_layout.h_pad": validate_float,
1277 "figure.constrained_layout.w_pad": validate_float,
1278
1279 ## Saving figure's properties
1280 'savefig.dpi': validate_dpi,
1281 'savefig.facecolor': validate_color_or_auto,
1282 'savefig.edgecolor': validate_color_or_auto,
1283 'savefig.orientation': ['landscape', 'portrait'],
1284 "savefig.format": validate_string,
1285 "savefig.bbox": validate_bbox, # "tight", or "standard" (= None)
1286 "savefig.pad_inches": validate_float,
1287 # default directory in savefig dialog box
1288 "savefig.directory": _validate_pathlike,
1289 "savefig.transparent": validate_bool,
1290
1291 "tk.window_focus": validate_bool, # Maintain shell focus for TkAgg
1292
1293 # Set the papersize/type
1294 "ps.papersize": _validate_papersize,
1295 "ps.useafm": validate_bool,
1296 # use ghostscript or xpdf to distill ps output
1297 "ps.usedistiller": validate_ps_distiller,
1298 "ps.distiller.res": validate_int, # dpi
1299 "ps.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype)
1300 "pdf.compression": validate_int, # 0-9 compression level; 0 to disable
1301 "pdf.inheritcolor": validate_bool, # skip color setting commands
1302 # use only the 14 PDF core fonts embedded in every PDF viewing application
1303 "pdf.use14corefonts": validate_bool,
1304 "pdf.fonttype": validate_fonttype, # 3 (Type3) or 42 (Truetype)
1305
1306 "pgf.texsystem": ["xelatex", "lualatex", "pdflatex"], # latex variant used
1307 "pgf.rcfonts": validate_bool, # use mpl's rc settings for font config
1308 "pgf.preamble": validate_string, # custom LaTeX preamble
1309
1310 # write raster image data into the svg file
1311 "svg.image_inline": validate_bool,
1312 "svg.fonttype": ["none", "path"], # save text as text ("none") or "paths"
1313 "svg.hashsalt": validate_string_or_None,
1314
1315 # set this when you want to generate hardcopy docstring
1316 "docstring.hardcopy": validate_bool,
1317
1318 "path.simplify": validate_bool,
1319 "path.simplify_threshold": _validate_greaterequal0_lessequal1,
1320 "path.snap": validate_bool,
1321 "path.sketch": validate_sketch,
1322 "path.effects": validate_anylist,
1323 "agg.path.chunksize": validate_int, # 0 to disable chunking
1324
1325 # key-mappings (multi-character mappings should be a list/tuple)
1326 "keymap.fullscreen": validate_stringlist,
1327 "keymap.home": validate_stringlist,
1328 "keymap.back": validate_stringlist,
1329 "keymap.forward": validate_stringlist,
1330 "keymap.pan": validate_stringlist,
1331 "keymap.zoom": validate_stringlist,
1332 "keymap.save": validate_stringlist,
1333 "keymap.quit": validate_stringlist,
1334 "keymap.quit_all": validate_stringlist, # e.g.: "W", "cmd+W", "Q"
1335 "keymap.grid": validate_stringlist,
1336 "keymap.grid_minor": validate_stringlist,
1337 "keymap.yscale": validate_stringlist,
1338 "keymap.xscale": validate_stringlist,
1339 "keymap.help": validate_stringlist,
1340 "keymap.copy": validate_stringlist,
1341
1342 # Animation settings
1343 "animation.html": ["html5", "jshtml", "none"],
1344 # Limit, in MB, of size of base64 encoded animation in HTML
1345 # (i.e. IPython notebook)
1346 "animation.embed_limit": validate_float,
1347 "animation.writer": validate_string,
1348 "animation.codec": validate_string,
1349 "animation.bitrate": validate_int,
1350 # Controls image format when frames are written to disk
1351 "animation.frame_format": ["png", "jpeg", "tiff", "raw", "rgba", "ppm",
1352 "sgi", "bmp", "pbm", "svg"],
1353 # Path to ffmpeg binary. If just binary name, subprocess uses $PATH.
1354 "animation.ffmpeg_path": _validate_pathlike,
1355 # Additional arguments for ffmpeg movie writer (using pipes)
1356 "animation.ffmpeg_args": validate_stringlist,
1357 # Path to convert binary. If just binary name, subprocess uses $PATH.
1358 "animation.convert_path": _validate_pathlike,
1359 # Additional arguments for convert movie writer (using pipes)
1360 "animation.convert_args": validate_stringlist,
1361
1362 # Classic (pre 2.0) compatibility mode
1363 # This is used for things that are hard to make backward compatible
1364 # with a sane rcParam alone. This does *not* turn on classic mode
1365 # altogether. For that use `matplotlib.style.use("classic")`.
1366 "_internal.classic_mode": validate_bool
1367}
1368_hardcoded_defaults = { # Defaults not inferred from
1369 # lib/matplotlib/mpl-data/matplotlibrc...
1370 # ... because they are private:
1371 "_internal.classic_mode": False,
1372 # ... because they are deprecated:
1373 # No current deprecations.
1374 # backend is handled separately when constructing rcParamsDefault.
1375}
1376_validators = {k: _convert_validator_spec(k, conv)
1377 for k, conv in _validators.items()}