rcsetup.py 50 KB

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