1"""
2An object-oriented plotting library.
3
4A procedural interface is provided by the companion pyplot module,
5which may be imported directly, e.g.::
6
7 import matplotlib.pyplot as plt
8
9or using ipython::
10
11 ipython
12
13at your terminal, followed by::
14
15 In [1]: %matplotlib
16 In [2]: import matplotlib.pyplot as plt
17
18at the ipython shell prompt.
19
20For the most part, direct use of the explicit object-oriented library is
21encouraged when programming; the implicit pyplot interface is primarily for
22working interactively. The exceptions to this suggestion are the pyplot
23functions `.pyplot.figure`, `.pyplot.subplot`, `.pyplot.subplots`, and
24`.pyplot.savefig`, which can greatly simplify scripting. See
25:ref:`api_interfaces` for an explanation of the tradeoffs between the implicit
26and explicit interfaces.
27
28Modules include:
29
30:mod:`matplotlib.axes`
31 The `~.axes.Axes` class. Most pyplot functions are wrappers for
32 `~.axes.Axes` methods. The axes module is the highest level of OO
33 access to the library.
34
35:mod:`matplotlib.figure`
36 The `.Figure` class.
37
38:mod:`matplotlib.artist`
39 The `.Artist` base class for all classes that draw things.
40
41:mod:`matplotlib.lines`
42 The `.Line2D` class for drawing lines and markers.
43
44:mod:`matplotlib.patches`
45 Classes for drawing polygons.
46
47:mod:`matplotlib.text`
48 The `.Text` and `.Annotation` classes.
49
50:mod:`matplotlib.image`
51 The `.AxesImage` and `.FigureImage` classes.
52
53:mod:`matplotlib.collections`
54 Classes for efficient drawing of groups of lines or polygons.
55
56:mod:`matplotlib.colors`
57 Color specifications and making colormaps.
58
59:mod:`matplotlib.cm`
60 Colormaps, and the `.ScalarMappable` mixin class for providing color
61 mapping functionality to other classes.
62
63:mod:`matplotlib.ticker`
64 Calculation of tick mark locations and formatting of tick labels.
65
66:mod:`matplotlib.backends`
67 A subpackage with modules for various GUI libraries and output formats.
68
69The base matplotlib namespace includes:
70
71`~matplotlib.rcParams`
72 Default configuration settings; their defaults may be overridden using
73 a :file:`matplotlibrc` file.
74
75`~matplotlib.use`
76 Setting the Matplotlib backend. This should be called before any
77 figure is created, because it is not possible to switch between
78 different GUI backends after that.
79
80The following environment variables can be used to customize the behavior:
81
82:envvar:`MPLBACKEND`
83 This optional variable can be set to choose the Matplotlib backend. See
84 :ref:`what-is-a-backend`.
85
86:envvar:`MPLCONFIGDIR`
87 This is the directory used to store user customizations to
88 Matplotlib, as well as some caches to improve performance. If
89 :envvar:`MPLCONFIGDIR` is not defined, :file:`{HOME}/.config/matplotlib`
90 and :file:`{HOME}/.cache/matplotlib` are used on Linux, and
91 :file:`{HOME}/.matplotlib` on other platforms, if they are
92 writable. Otherwise, the Python standard library's `tempfile.gettempdir`
93 is used to find a base directory in which the :file:`matplotlib`
94 subdirectory is created.
95
96Matplotlib was initially written by John D. Hunter (1968-2012) and is now
97developed and maintained by a host of others.
98
99Occasionally the internal documentation (python docstrings) will refer
100to MATLAB®, a registered trademark of The MathWorks, Inc.
101
102"""
103
104__all__ = [
105 "__bibtex__",
106 "__version__",
107 "__version_info__",
108 "set_loglevel",
109 "ExecutableNotFoundError",
110 "get_configdir",
111 "get_cachedir",
112 "get_data_path",
113 "matplotlib_fname",
114 "MatplotlibDeprecationWarning",
115 "RcParams",
116 "rc_params",
117 "rc_params_from_file",
118 "rcParamsDefault",
119 "rcParams",
120 "rcParamsOrig",
121 "defaultParams",
122 "rc",
123 "rcdefaults",
124 "rc_file_defaults",
125 "rc_file",
126 "rc_context",
127 "use",
128 "get_backend",
129 "interactive",
130 "is_interactive",
131 "colormaps",
132 "color_sequences",
133]
134
135
136import atexit
137from collections import namedtuple
138from collections.abc import MutableMapping
139import contextlib
140import functools
141import importlib
142import inspect
143from inspect import Parameter
144import locale
145import logging
146import os
147from pathlib import Path
148import pprint
149import re
150import shutil
151import subprocess
152import sys
153import tempfile
154
155from packaging.version import parse as parse_version
156
157# cbook must import matplotlib only within function
158# definitions, so it is safe to import from it here.
159from . import _api, _version, cbook, _docstring, rcsetup
160from matplotlib.cbook import sanitize_sequence
161from matplotlib._api import MatplotlibDeprecationWarning
162from matplotlib.rcsetup import cycler # noqa: F401
163from matplotlib.rcsetup import validate_backend
164
165
166_log = logging.getLogger(__name__)
167
168__bibtex__ = r"""@Article{Hunter:2007,
169 Author = {Hunter, J. D.},
170 Title = {Matplotlib: A 2D graphics environment},
171 Journal = {Computing in Science \& Engineering},
172 Volume = {9},
173 Number = {3},
174 Pages = {90--95},
175 abstract = {Matplotlib is a 2D graphics package used for Python
176 for application development, interactive scripting, and
177 publication-quality image generation across user
178 interfaces and operating systems.},
179 publisher = {IEEE COMPUTER SOC},
180 year = 2007
181}"""
182
183# modelled after sys.version_info
184_VersionInfo = namedtuple('_VersionInfo',
185 'major, minor, micro, releaselevel, serial')
186
187
188def _parse_to_version_info(version_str):
189 """
190 Parse a version string to a namedtuple analogous to sys.version_info.
191
192 See:
193 https://packaging.pypa.io/en/latest/version.html#packaging.version.parse
194 https://docs.python.org/3/library/sys.html#sys.version_info
195 """
196 v = parse_version(version_str)
197 if v.pre is None and v.post is None and v.dev is None:
198 return _VersionInfo(v.major, v.minor, v.micro, 'final', 0)
199 elif v.dev is not None:
200 return _VersionInfo(v.major, v.minor, v.micro, 'alpha', v.dev)
201 elif v.pre is not None:
202 releaselevel = {
203 'a': 'alpha',
204 'b': 'beta',
205 'rc': 'candidate'}.get(v.pre[0], 'alpha')
206 return _VersionInfo(v.major, v.minor, v.micro, releaselevel, v.pre[1])
207 else:
208 # fallback for v.post: guess-next-dev scheme from setuptools_scm
209 return _VersionInfo(v.major, v.minor, v.micro + 1, 'alpha', v.post)
210
211
212def _get_version():
213 """Return the version string used for __version__."""
214 # Only shell out to a git subprocess if really needed, i.e. when we are in
215 # a matplotlib git repo but not in a shallow clone, such as those used by
216 # CI, as the latter would trigger a warning from setuptools_scm.
217 root = Path(__file__).resolve().parents[2]
218 if ((root / ".matplotlib-repo").exists()
219 and (root / ".git").exists()
220 and not (root / ".git/shallow").exists()):
221 try:
222 import setuptools_scm
223 except ImportError:
224 pass
225 else:
226 return setuptools_scm.get_version(
227 root=root,
228 version_scheme="release-branch-semver",
229 local_scheme="node-and-date",
230 fallback_version=_version.version,
231 )
232 # Get the version from the _version.py file if not in repo or setuptools_scm is
233 # unavailable.
234 return _version.version
235
236
237@_api.caching_module_getattr
238class __getattr__:
239 __version__ = property(lambda self: _get_version())
240 __version_info__ = property(
241 lambda self: _parse_to_version_info(self.__version__))
242
243
244def _check_versions():
245
246 # Quickfix to ensure Microsoft Visual C++ redistributable
247 # DLLs are loaded before importing kiwisolver
248 from . import ft2font # noqa: F401
249
250 for modname, minver in [
251 ("cycler", "0.10"),
252 ("dateutil", "2.7"),
253 ("kiwisolver", "1.3.1"),
254 ("numpy", "1.23"),
255 ("pyparsing", "2.3.1"),
256 ]:
257 module = importlib.import_module(modname)
258 if parse_version(module.__version__) < parse_version(minver):
259 raise ImportError(f"Matplotlib requires {modname}>={minver}; "
260 f"you have {module.__version__}")
261
262
263_check_versions()
264
265
266# The decorator ensures this always returns the same handler (and it is only
267# attached once).
268@functools.cache
269def _ensure_handler():
270 """
271 The first time this function is called, attach a `StreamHandler` using the
272 same format as `logging.basicConfig` to the Matplotlib root logger.
273
274 Return this handler every time this function is called.
275 """
276 handler = logging.StreamHandler()
277 handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
278 _log.addHandler(handler)
279 return handler
280
281
282def set_loglevel(level):
283 """
284 Configure Matplotlib's logging levels.
285
286 Matplotlib uses the standard library `logging` framework under the root
287 logger 'matplotlib'. This is a helper function to:
288
289 - set Matplotlib's root logger level
290 - set the root logger handler's level, creating the handler
291 if it does not exist yet
292
293 Typically, one should call ``set_loglevel("info")`` or
294 ``set_loglevel("debug")`` to get additional debugging information.
295
296 Users or applications that are installing their own logging handlers
297 may want to directly manipulate ``logging.getLogger('matplotlib')`` rather
298 than use this function.
299
300 Parameters
301 ----------
302 level : {"notset", "debug", "info", "warning", "error", "critical"}
303 The log level of the handler.
304
305 Notes
306 -----
307 The first time this function is called, an additional handler is attached
308 to Matplotlib's root handler; this handler is reused every time and this
309 function simply manipulates the logger and handler's level.
310
311 """
312 _log.setLevel(level.upper())
313 _ensure_handler().setLevel(level.upper())
314
315
316def _logged_cached(fmt, func=None):
317 """
318 Decorator that logs a function's return value, and memoizes that value.
319
320 After ::
321
322 @_logged_cached(fmt)
323 def func(): ...
324
325 the first call to *func* will log its return value at the DEBUG level using
326 %-format string *fmt*, and memoize it; later calls to *func* will directly
327 return that value.
328 """
329 if func is None: # Return the actual decorator.
330 return functools.partial(_logged_cached, fmt)
331
332 called = False
333 ret = None
334
335 @functools.wraps(func)
336 def wrapper(**kwargs):
337 nonlocal called, ret
338 if not called:
339 ret = func(**kwargs)
340 called = True
341 _log.debug(fmt, ret)
342 return ret
343
344 return wrapper
345
346
347_ExecInfo = namedtuple("_ExecInfo", "executable raw_version version")
348
349
350class ExecutableNotFoundError(FileNotFoundError):
351 """
352 Error raised when an executable that Matplotlib optionally
353 depends on can't be found.
354 """
355 pass
356
357
358@functools.cache
359def _get_executable_info(name):
360 """
361 Get the version of some executable that Matplotlib optionally depends on.
362
363 .. warning::
364 The list of executables that this function supports is set according to
365 Matplotlib's internal needs, and may change without notice.
366
367 Parameters
368 ----------
369 name : str
370 The executable to query. The following values are currently supported:
371 "dvipng", "gs", "inkscape", "magick", "pdftocairo", "pdftops". This
372 list is subject to change without notice.
373
374 Returns
375 -------
376 tuple
377 A namedtuple with fields ``executable`` (`str`) and ``version``
378 (`packaging.Version`, or ``None`` if the version cannot be determined).
379
380 Raises
381 ------
382 ExecutableNotFoundError
383 If the executable is not found or older than the oldest version
384 supported by Matplotlib. For debugging purposes, it is also
385 possible to "hide" an executable from Matplotlib by adding it to the
386 :envvar:`_MPLHIDEEXECUTABLES` environment variable (a comma-separated
387 list), which must be set prior to any calls to this function.
388 ValueError
389 If the executable is not one that we know how to query.
390 """
391
392 def impl(args, regex, min_ver=None, ignore_exit_code=False):
393 # Execute the subprocess specified by args; capture stdout and stderr.
394 # Search for a regex match in the output; if the match succeeds, the
395 # first group of the match is the version.
396 # Return an _ExecInfo if the executable exists, and has a version of
397 # at least min_ver (if set); else, raise ExecutableNotFoundError.
398 try:
399 output = subprocess.check_output(
400 args, stderr=subprocess.STDOUT,
401 text=True, errors="replace")
402 except subprocess.CalledProcessError as _cpe:
403 if ignore_exit_code:
404 output = _cpe.output
405 else:
406 raise ExecutableNotFoundError(str(_cpe)) from _cpe
407 except OSError as _ose:
408 raise ExecutableNotFoundError(str(_ose)) from _ose
409 match = re.search(regex, output)
410 if match:
411 raw_version = match.group(1)
412 version = parse_version(raw_version)
413 if min_ver is not None and version < parse_version(min_ver):
414 raise ExecutableNotFoundError(
415 f"You have {args[0]} version {version} but the minimum "
416 f"version supported by Matplotlib is {min_ver}")
417 return _ExecInfo(args[0], raw_version, version)
418 else:
419 raise ExecutableNotFoundError(
420 f"Failed to determine the version of {args[0]} from "
421 f"{' '.join(args)}, which output {output}")
422
423 if name in os.environ.get("_MPLHIDEEXECUTABLES", "").split(","):
424 raise ExecutableNotFoundError(f"{name} was hidden")
425
426 if name == "dvipng":
427 return impl(["dvipng", "-version"], "(?m)^dvipng(?: .*)? (.+)", "1.6")
428 elif name == "gs":
429 execs = (["gswin32c", "gswin64c", "mgs", "gs"] # "mgs" for miktex.
430 if sys.platform == "win32" else
431 ["gs"])
432 for e in execs:
433 try:
434 return impl([e, "--version"], "(.*)", "9")
435 except ExecutableNotFoundError:
436 pass
437 message = "Failed to find a Ghostscript installation"
438 raise ExecutableNotFoundError(message)
439 elif name == "inkscape":
440 try:
441 # Try headless option first (needed for Inkscape version < 1.0):
442 return impl(["inkscape", "--without-gui", "-V"],
443 "Inkscape ([^ ]*)")
444 except ExecutableNotFoundError:
445 pass # Suppress exception chaining.
446 # If --without-gui is not accepted, we may be using Inkscape >= 1.0 so
447 # try without it:
448 return impl(["inkscape", "-V"], "Inkscape ([^ ]*)")
449 elif name == "magick":
450 if sys.platform == "win32":
451 # Check the registry to avoid confusing ImageMagick's convert with
452 # Windows's builtin convert.exe.
453 import winreg
454 binpath = ""
455 for flag in [0, winreg.KEY_WOW64_32KEY, winreg.KEY_WOW64_64KEY]:
456 try:
457 with winreg.OpenKeyEx(
458 winreg.HKEY_LOCAL_MACHINE,
459 r"Software\Imagemagick\Current",
460 0, winreg.KEY_QUERY_VALUE | flag) as hkey:
461 binpath = winreg.QueryValueEx(hkey, "BinPath")[0]
462 except OSError:
463 pass
464 path = None
465 if binpath:
466 for name in ["convert.exe", "magick.exe"]:
467 candidate = Path(binpath, name)
468 if candidate.exists():
469 path = str(candidate)
470 break
471 if path is None:
472 raise ExecutableNotFoundError(
473 "Failed to find an ImageMagick installation")
474 else:
475 path = "convert"
476 info = impl([path, "--version"], r"^Version: ImageMagick (\S*)")
477 if info.raw_version == "7.0.10-34":
478 # https://github.com/ImageMagick/ImageMagick/issues/2720
479 raise ExecutableNotFoundError(
480 f"You have ImageMagick {info.version}, which is unsupported")
481 return info
482 elif name == "pdftocairo":
483 return impl(["pdftocairo", "-v"], "pdftocairo version (.*)")
484 elif name == "pdftops":
485 info = impl(["pdftops", "-v"], "^pdftops version (.*)",
486 ignore_exit_code=True)
487 if info and not (
488 3 <= info.version.major or
489 # poppler version numbers.
490 parse_version("0.9") <= info.version < parse_version("1.0")):
491 raise ExecutableNotFoundError(
492 f"You have pdftops version {info.version} but the minimum "
493 f"version supported by Matplotlib is 3.0")
494 return info
495 else:
496 raise ValueError(f"Unknown executable: {name!r}")
497
498
499def _get_xdg_config_dir():
500 """
501 Return the XDG configuration directory, according to the XDG base
502 directory spec:
503
504 https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
505 """
506 return os.environ.get('XDG_CONFIG_HOME') or str(Path.home() / ".config")
507
508
509def _get_xdg_cache_dir():
510 """
511 Return the XDG cache directory, according to the XDG base directory spec:
512
513 https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
514 """
515 return os.environ.get('XDG_CACHE_HOME') or str(Path.home() / ".cache")
516
517
518def _get_config_or_cache_dir(xdg_base_getter):
519 configdir = os.environ.get('MPLCONFIGDIR')
520 if configdir:
521 configdir = Path(configdir)
522 elif sys.platform.startswith(('linux', 'freebsd')):
523 # Only call _xdg_base_getter here so that MPLCONFIGDIR is tried first,
524 # as _xdg_base_getter can throw.
525 configdir = Path(xdg_base_getter(), "matplotlib")
526 else:
527 configdir = Path.home() / ".matplotlib"
528 # Resolve the path to handle potential issues with inaccessible symlinks.
529 configdir = configdir.resolve()
530 try:
531 configdir.mkdir(parents=True, exist_ok=True)
532 except OSError as exc:
533 _log.warning("mkdir -p failed for path %s: %s", configdir, exc)
534 else:
535 if os.access(str(configdir), os.W_OK) and configdir.is_dir():
536 return str(configdir)
537 _log.warning("%s is not a writable directory", configdir)
538 # If the config or cache directory cannot be created or is not a writable
539 # directory, create a temporary one.
540 try:
541 tmpdir = tempfile.mkdtemp(prefix="matplotlib-")
542 except OSError as exc:
543 raise OSError(
544 f"Matplotlib requires access to a writable cache directory, but there "
545 f"was an issue with the default path ({configdir}), and a temporary "
546 f"directory could not be created; set the MPLCONFIGDIR environment "
547 f"variable to a writable directory") from exc
548 os.environ["MPLCONFIGDIR"] = tmpdir
549 atexit.register(shutil.rmtree, tmpdir)
550 _log.warning(
551 "Matplotlib created a temporary cache directory at %s because there was "
552 "an issue with the default path (%s); it is highly recommended to set the "
553 "MPLCONFIGDIR environment variable to a writable directory, in particular to "
554 "speed up the import of Matplotlib and to better support multiprocessing.",
555 tmpdir, configdir)
556 return tmpdir
557
558
559@_logged_cached('CONFIGDIR=%s')
560def get_configdir():
561 """
562 Return the string path of the configuration directory.
563
564 The directory is chosen as follows:
565
566 1. If the MPLCONFIGDIR environment variable is supplied, choose that.
567 2. On Linux, follow the XDG specification and look first in
568 ``$XDG_CONFIG_HOME``, if defined, or ``$HOME/.config``. On other
569 platforms, choose ``$HOME/.matplotlib``.
570 3. If the chosen directory exists and is writable, use that as the
571 configuration directory.
572 4. Else, create a temporary directory, and use it as the configuration
573 directory.
574 """
575 return _get_config_or_cache_dir(_get_xdg_config_dir)
576
577
578@_logged_cached('CACHEDIR=%s')
579def get_cachedir():
580 """
581 Return the string path of the cache directory.
582
583 The procedure used to find the directory is the same as for
584 `get_configdir`, except using ``$XDG_CACHE_HOME``/``$HOME/.cache`` instead.
585 """
586 return _get_config_or_cache_dir(_get_xdg_cache_dir)
587
588
589@_logged_cached('matplotlib data path: %s')
590def get_data_path():
591 """Return the path to Matplotlib data."""
592 return str(Path(__file__).with_name("mpl-data"))
593
594
595def matplotlib_fname():
596 """
597 Get the location of the config file.
598
599 The file location is determined in the following order
600
601 - ``$PWD/matplotlibrc``
602 - ``$MATPLOTLIBRC`` if it is not a directory
603 - ``$MATPLOTLIBRC/matplotlibrc``
604 - ``$MPLCONFIGDIR/matplotlibrc``
605 - On Linux,
606 - ``$XDG_CONFIG_HOME/matplotlib/matplotlibrc`` (if ``$XDG_CONFIG_HOME``
607 is defined)
608 - or ``$HOME/.config/matplotlib/matplotlibrc`` (if ``$XDG_CONFIG_HOME``
609 is not defined)
610 - On other platforms,
611 - ``$HOME/.matplotlib/matplotlibrc`` if ``$HOME`` is defined
612 - Lastly, it looks in ``$MATPLOTLIBDATA/matplotlibrc``, which should always
613 exist.
614 """
615
616 def gen_candidates():
617 # rely on down-stream code to make absolute. This protects us
618 # from having to directly get the current working directory
619 # which can fail if the user has ended up with a cwd that is
620 # non-existent.
621 yield 'matplotlibrc'
622 try:
623 matplotlibrc = os.environ['MATPLOTLIBRC']
624 except KeyError:
625 pass
626 else:
627 yield matplotlibrc
628 yield os.path.join(matplotlibrc, 'matplotlibrc')
629 yield os.path.join(get_configdir(), 'matplotlibrc')
630 yield os.path.join(get_data_path(), 'matplotlibrc')
631
632 for fname in gen_candidates():
633 if os.path.exists(fname) and not os.path.isdir(fname):
634 return fname
635
636 raise RuntimeError("Could not find matplotlibrc file; your Matplotlib "
637 "install is broken")
638
639
640# rcParams deprecated and automatically mapped to another key.
641# Values are tuples of (version, new_name, f_old2new, f_new2old).
642_deprecated_map = {}
643# rcParams deprecated; some can manually be mapped to another key.
644# Values are tuples of (version, new_name_or_None).
645_deprecated_ignore_map = {}
646# rcParams deprecated; can use None to suppress warnings; remain actually
647# listed in the rcParams.
648# Values are tuples of (version,)
649_deprecated_remain_as_none = {}
650
651
652@_docstring.Substitution(
653 "\n".join(map("- {}".format, sorted(rcsetup._validators, key=str.lower)))
654)
655class RcParams(MutableMapping, dict):
656 """
657 A dict-like key-value store for config parameters, including validation.
658
659 Validating functions are defined and associated with rc parameters in
660 :mod:`matplotlib.rcsetup`.
661
662 The list of rcParams is:
663
664 %s
665
666 See Also
667 --------
668 :ref:`customizing-with-matplotlibrc-files`
669 """
670
671 validate = rcsetup._validators
672
673 # validate values on the way in
674 def __init__(self, *args, **kwargs):
675 self.update(*args, **kwargs)
676
677 def _set(self, key, val):
678 """
679 Directly write data bypassing deprecation and validation logic.
680
681 Notes
682 -----
683 As end user or downstream library you almost always should use
684 ``rcParams[key] = val`` and not ``_set()``.
685
686 There are only very few special cases that need direct data access.
687 These cases previously used ``dict.__setitem__(rcParams, key, val)``,
688 which is now deprecated and replaced by ``rcParams._set(key, val)``.
689
690 Even though private, we guarantee API stability for ``rcParams._set``,
691 i.e. it is subject to Matplotlib's API and deprecation policy.
692
693 :meta public:
694 """
695 dict.__setitem__(self, key, val)
696
697 def _get(self, key):
698 """
699 Directly read data bypassing deprecation, backend and validation
700 logic.
701
702 Notes
703 -----
704 As end user or downstream library you almost always should use
705 ``val = rcParams[key]`` and not ``_get()``.
706
707 There are only very few special cases that need direct data access.
708 These cases previously used ``dict.__getitem__(rcParams, key, val)``,
709 which is now deprecated and replaced by ``rcParams._get(key)``.
710
711 Even though private, we guarantee API stability for ``rcParams._get``,
712 i.e. it is subject to Matplotlib's API and deprecation policy.
713
714 :meta public:
715 """
716 return dict.__getitem__(self, key)
717
718 def __setitem__(self, key, val):
719 try:
720 if key in _deprecated_map:
721 version, alt_key, alt_val, inverse_alt = _deprecated_map[key]
722 _api.warn_deprecated(
723 version, name=key, obj_type="rcparam", alternative=alt_key)
724 key = alt_key
725 val = alt_val(val)
726 elif key in _deprecated_remain_as_none and val is not None:
727 version, = _deprecated_remain_as_none[key]
728 _api.warn_deprecated(version, name=key, obj_type="rcparam")
729 elif key in _deprecated_ignore_map:
730 version, alt_key = _deprecated_ignore_map[key]
731 _api.warn_deprecated(
732 version, name=key, obj_type="rcparam", alternative=alt_key)
733 return
734 elif key == 'backend':
735 if val is rcsetup._auto_backend_sentinel:
736 if 'backend' in self:
737 return
738 try:
739 cval = self.validate[key](val)
740 except ValueError as ve:
741 raise ValueError(f"Key {key}: {ve}") from None
742 self._set(key, cval)
743 except KeyError as err:
744 raise KeyError(
745 f"{key} is not a valid rc parameter (see rcParams.keys() for "
746 f"a list of valid parameters)") from err
747
748 def __getitem__(self, key):
749 if key in _deprecated_map:
750 version, alt_key, alt_val, inverse_alt = _deprecated_map[key]
751 _api.warn_deprecated(
752 version, name=key, obj_type="rcparam", alternative=alt_key)
753 return inverse_alt(self._get(alt_key))
754
755 elif key in _deprecated_ignore_map:
756 version, alt_key = _deprecated_ignore_map[key]
757 _api.warn_deprecated(
758 version, name=key, obj_type="rcparam", alternative=alt_key)
759 return self._get(alt_key) if alt_key else None
760
761 # In theory, this should only ever be used after the global rcParams
762 # has been set up, but better be safe e.g. in presence of breakpoints.
763 elif key == "backend" and self is globals().get("rcParams"):
764 val = self._get(key)
765 if val is rcsetup._auto_backend_sentinel:
766 from matplotlib import pyplot as plt
767 plt.switch_backend(rcsetup._auto_backend_sentinel)
768
769 return self._get(key)
770
771 def _get_backend_or_none(self):
772 """Get the requested backend, if any, without triggering resolution."""
773 backend = self._get("backend")
774 return None if backend is rcsetup._auto_backend_sentinel else backend
775
776 def __repr__(self):
777 class_name = self.__class__.__name__
778 indent = len(class_name) + 1
779 with _api.suppress_matplotlib_deprecation_warning():
780 repr_split = pprint.pformat(dict(self), indent=1,
781 width=80 - indent).split('\n')
782 repr_indented = ('\n' + ' ' * indent).join(repr_split)
783 return f'{class_name}({repr_indented})'
784
785 def __str__(self):
786 return '\n'.join(map('{0[0]}: {0[1]}'.format, sorted(self.items())))
787
788 def __iter__(self):
789 """Yield sorted list of keys."""
790 with _api.suppress_matplotlib_deprecation_warning():
791 yield from sorted(dict.__iter__(self))
792
793 def __len__(self):
794 return dict.__len__(self)
795
796 def find_all(self, pattern):
797 """
798 Return the subset of this RcParams dictionary whose keys match,
799 using :func:`re.search`, the given ``pattern``.
800
801 .. note::
802
803 Changes to the returned dictionary are *not* propagated to
804 the parent RcParams dictionary.
805
806 """
807 pattern_re = re.compile(pattern)
808 return RcParams((key, value)
809 for key, value in self.items()
810 if pattern_re.search(key))
811
812 def copy(self):
813 """Copy this RcParams instance."""
814 rccopy = RcParams()
815 for k in self: # Skip deprecations and revalidation.
816 rccopy._set(k, self._get(k))
817 return rccopy
818
819
820def rc_params(fail_on_error=False):
821 """Construct a `RcParams` instance from the default Matplotlib rc file."""
822 return rc_params_from_file(matplotlib_fname(), fail_on_error)
823
824
825@functools.cache
826def _get_ssl_context():
827 try:
828 import certifi
829 except ImportError:
830 _log.debug("Could not import certifi.")
831 return None
832 import ssl
833 return ssl.create_default_context(cafile=certifi.where())
834
835
836@contextlib.contextmanager
837def _open_file_or_url(fname):
838 if (isinstance(fname, str)
839 and fname.startswith(('http://', 'https://', 'ftp://', 'file:'))):
840 import urllib.request
841 ssl_ctx = _get_ssl_context()
842 if ssl_ctx is None:
843 _log.debug(
844 "Could not get certifi ssl context, https may not work."
845 )
846 with urllib.request.urlopen(fname, context=ssl_ctx) as f:
847 yield (line.decode('utf-8') for line in f)
848 else:
849 fname = os.path.expanduser(fname)
850 with open(fname, encoding='utf-8') as f:
851 yield f
852
853
854def _rc_params_in_file(fname, transform=lambda x: x, fail_on_error=False):
855 """
856 Construct a `RcParams` instance from file *fname*.
857
858 Unlike `rc_params_from_file`, the configuration class only contains the
859 parameters specified in the file (i.e. default values are not filled in).
860
861 Parameters
862 ----------
863 fname : path-like
864 The loaded file.
865 transform : callable, default: the identity function
866 A function called on each individual line of the file to transform it,
867 before further parsing.
868 fail_on_error : bool, default: False
869 Whether invalid entries should result in an exception or a warning.
870 """
871 import matplotlib as mpl
872 rc_temp = {}
873 with _open_file_or_url(fname) as fd:
874 try:
875 for line_no, line in enumerate(fd, 1):
876 line = transform(line)
877 strippedline = cbook._strip_comment(line)
878 if not strippedline:
879 continue
880 tup = strippedline.split(':', 1)
881 if len(tup) != 2:
882 _log.warning('Missing colon in file %r, line %d (%r)',
883 fname, line_no, line.rstrip('\n'))
884 continue
885 key, val = tup
886 key = key.strip()
887 val = val.strip()
888 if val.startswith('"') and val.endswith('"'):
889 val = val[1:-1] # strip double quotes
890 if key in rc_temp:
891 _log.warning('Duplicate key in file %r, line %d (%r)',
892 fname, line_no, line.rstrip('\n'))
893 rc_temp[key] = (val, line, line_no)
894 except UnicodeDecodeError:
895 _log.warning('Cannot decode configuration file %r as utf-8.',
896 fname)
897 raise
898
899 config = RcParams()
900
901 for key, (val, line, line_no) in rc_temp.items():
902 if key in rcsetup._validators:
903 if fail_on_error:
904 config[key] = val # try to convert to proper type or raise
905 else:
906 try:
907 config[key] = val # try to convert to proper type or skip
908 except Exception as msg:
909 _log.warning('Bad value in file %r, line %d (%r): %s',
910 fname, line_no, line.rstrip('\n'), msg)
911 elif key in _deprecated_ignore_map:
912 version, alt_key = _deprecated_ignore_map[key]
913 _api.warn_deprecated(
914 version, name=key, alternative=alt_key, obj_type='rcparam',
915 addendum="Please update your matplotlibrc.")
916 else:
917 # __version__ must be looked up as an attribute to trigger the
918 # module-level __getattr__.
919 version = ('main' if '.post' in mpl.__version__
920 else f'v{mpl.__version__}')
921 _log.warning("""
922Bad key %(key)s in file %(fname)s, line %(line_no)s (%(line)r)
923You probably need to get an updated matplotlibrc file from
924https://github.com/matplotlib/matplotlib/blob/%(version)s/lib/matplotlib/mpl-data/matplotlibrc
925or from the matplotlib source distribution""",
926 dict(key=key, fname=fname, line_no=line_no,
927 line=line.rstrip('\n'), version=version))
928 return config
929
930
931def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
932 """
933 Construct a `RcParams` from file *fname*.
934
935 Parameters
936 ----------
937 fname : str or path-like
938 A file with Matplotlib rc settings.
939 fail_on_error : bool
940 If True, raise an error when the parser fails to convert a parameter.
941 use_default_template : bool
942 If True, initialize with default parameters before updating with those
943 in the given file. If False, the configuration class only contains the
944 parameters specified in the file. (Useful for updating dicts.)
945 """
946 config_from_file = _rc_params_in_file(fname, fail_on_error=fail_on_error)
947
948 if not use_default_template:
949 return config_from_file
950
951 with _api.suppress_matplotlib_deprecation_warning():
952 config = RcParams({**rcParamsDefault, **config_from_file})
953
954 if "".join(config['text.latex.preamble']):
955 _log.info("""
956*****************************************************************
957You have the following UNSUPPORTED LaTeX preamble customizations:
958%s
959Please do not ask for support with these customizations active.
960*****************************************************************
961""", '\n'.join(config['text.latex.preamble']))
962 _log.debug('loaded rc file %s', fname)
963
964 return config
965
966
967# When constructing the global instances, we need to perform certain updates
968# by explicitly calling the superclass (dict.update, dict.items) to avoid
969# triggering resolution of _auto_backend_sentinel.
970rcParamsDefault = _rc_params_in_file(
971 cbook._get_data_path("matplotlibrc"),
972 # Strip leading comment.
973 transform=lambda line: line[1:] if line.startswith("#") else line,
974 fail_on_error=True)
975dict.update(rcParamsDefault, rcsetup._hardcoded_defaults)
976# Normally, the default matplotlibrc file contains *no* entry for backend (the
977# corresponding line starts with ##, not #; we fill on _auto_backend_sentinel
978# in that case. However, packagers can set a different default backend
979# (resulting in a normal `#backend: foo` line) in which case we should *not*
980# fill in _auto_backend_sentinel.
981dict.setdefault(rcParamsDefault, "backend", rcsetup._auto_backend_sentinel)
982rcParams = RcParams() # The global instance.
983dict.update(rcParams, dict.items(rcParamsDefault))
984dict.update(rcParams, _rc_params_in_file(matplotlib_fname()))
985rcParamsOrig = rcParams.copy()
986with _api.suppress_matplotlib_deprecation_warning():
987 # This also checks that all rcParams are indeed listed in the template.
988 # Assigning to rcsetup.defaultParams is left only for backcompat.
989 defaultParams = rcsetup.defaultParams = {
990 # We want to resolve deprecated rcParams, but not backend...
991 key: [(rcsetup._auto_backend_sentinel if key == "backend" else
992 rcParamsDefault[key]),
993 validator]
994 for key, validator in rcsetup._validators.items()}
995if rcParams['axes.formatter.use_locale']:
996 locale.setlocale(locale.LC_ALL, '')
997
998
999def rc(group, **kwargs):
1000 """
1001 Set the current `.rcParams`. *group* is the grouping for the rc, e.g.,
1002 for ``lines.linewidth`` the group is ``lines``, for
1003 ``axes.facecolor``, the group is ``axes``, and so on. Group may
1004 also be a list or tuple of group names, e.g., (*xtick*, *ytick*).
1005 *kwargs* is a dictionary attribute name/value pairs, e.g.,::
1006
1007 rc('lines', linewidth=2, color='r')
1008
1009 sets the current `.rcParams` and is equivalent to::
1010
1011 rcParams['lines.linewidth'] = 2
1012 rcParams['lines.color'] = 'r'
1013
1014 The following aliases are available to save typing for interactive users:
1015
1016 ===== =================
1017 Alias Property
1018 ===== =================
1019 'lw' 'linewidth'
1020 'ls' 'linestyle'
1021 'c' 'color'
1022 'fc' 'facecolor'
1023 'ec' 'edgecolor'
1024 'mew' 'markeredgewidth'
1025 'aa' 'antialiased'
1026 ===== =================
1027
1028 Thus you could abbreviate the above call as::
1029
1030 rc('lines', lw=2, c='r')
1031
1032 Note you can use python's kwargs dictionary facility to store
1033 dictionaries of default parameters. e.g., you can customize the
1034 font rc as follows::
1035
1036 font = {'family' : 'monospace',
1037 'weight' : 'bold',
1038 'size' : 'larger'}
1039 rc('font', **font) # pass in the font dict as kwargs
1040
1041 This enables you to easily switch between several configurations. Use
1042 ``matplotlib.style.use('default')`` or :func:`~matplotlib.rcdefaults` to
1043 restore the default `.rcParams` after changes.
1044
1045 Notes
1046 -----
1047 Similar functionality is available by using the normal dict interface, i.e.
1048 ``rcParams.update({"lines.linewidth": 2, ...})`` (but ``rcParams.update``
1049 does not support abbreviations or grouping).
1050 """
1051
1052 aliases = {
1053 'lw': 'linewidth',
1054 'ls': 'linestyle',
1055 'c': 'color',
1056 'fc': 'facecolor',
1057 'ec': 'edgecolor',
1058 'mew': 'markeredgewidth',
1059 'aa': 'antialiased',
1060 }
1061
1062 if isinstance(group, str):
1063 group = (group,)
1064 for g in group:
1065 for k, v in kwargs.items():
1066 name = aliases.get(k) or k
1067 key = f'{g}.{name}'
1068 try:
1069 rcParams[key] = v
1070 except KeyError as err:
1071 raise KeyError(('Unrecognized key "%s" for group "%s" and '
1072 'name "%s"') % (key, g, name)) from err
1073
1074
1075def rcdefaults():
1076 """
1077 Restore the `.rcParams` from Matplotlib's internal default style.
1078
1079 Style-blacklisted `.rcParams` (defined in
1080 ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated.
1081
1082 See Also
1083 --------
1084 matplotlib.rc_file_defaults
1085 Restore the `.rcParams` from the rc file originally loaded by
1086 Matplotlib.
1087 matplotlib.style.use
1088 Use a specific style file. Call ``style.use('default')`` to restore
1089 the default style.
1090 """
1091 # Deprecation warnings were already handled when creating rcParamsDefault,
1092 # no need to reemit them here.
1093 with _api.suppress_matplotlib_deprecation_warning():
1094 from .style.core import STYLE_BLACKLIST
1095 rcParams.clear()
1096 rcParams.update({k: v for k, v in rcParamsDefault.items()
1097 if k not in STYLE_BLACKLIST})
1098
1099
1100def rc_file_defaults():
1101 """
1102 Restore the `.rcParams` from the original rc file loaded by Matplotlib.
1103
1104 Style-blacklisted `.rcParams` (defined in
1105 ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated.
1106 """
1107 # Deprecation warnings were already handled when creating rcParamsOrig, no
1108 # need to reemit them here.
1109 with _api.suppress_matplotlib_deprecation_warning():
1110 from .style.core import STYLE_BLACKLIST
1111 rcParams.update({k: rcParamsOrig[k] for k in rcParamsOrig
1112 if k not in STYLE_BLACKLIST})
1113
1114
1115def rc_file(fname, *, use_default_template=True):
1116 """
1117 Update `.rcParams` from file.
1118
1119 Style-blacklisted `.rcParams` (defined in
1120 ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated.
1121
1122 Parameters
1123 ----------
1124 fname : str or path-like
1125 A file with Matplotlib rc settings.
1126
1127 use_default_template : bool
1128 If True, initialize with default parameters before updating with those
1129 in the given file. If False, the current configuration persists
1130 and only the parameters specified in the file are updated.
1131 """
1132 # Deprecation warnings were already handled in rc_params_from_file, no need
1133 # to reemit them here.
1134 with _api.suppress_matplotlib_deprecation_warning():
1135 from .style.core import STYLE_BLACKLIST
1136 rc_from_file = rc_params_from_file(
1137 fname, use_default_template=use_default_template)
1138 rcParams.update({k: rc_from_file[k] for k in rc_from_file
1139 if k not in STYLE_BLACKLIST})
1140
1141
1142@contextlib.contextmanager
1143def rc_context(rc=None, fname=None):
1144 """
1145 Return a context manager for temporarily changing rcParams.
1146
1147 The :rc:`backend` will not be reset by the context manager.
1148
1149 rcParams changed both through the context manager invocation and
1150 in the body of the context will be reset on context exit.
1151
1152 Parameters
1153 ----------
1154 rc : dict
1155 The rcParams to temporarily set.
1156 fname : str or path-like
1157 A file with Matplotlib rc settings. If both *fname* and *rc* are given,
1158 settings from *rc* take precedence.
1159
1160 See Also
1161 --------
1162 :ref:`customizing-with-matplotlibrc-files`
1163
1164 Examples
1165 --------
1166 Passing explicit values via a dict::
1167
1168 with mpl.rc_context({'interactive': False}):
1169 fig, ax = plt.subplots()
1170 ax.plot(range(3), range(3))
1171 fig.savefig('example.png')
1172 plt.close(fig)
1173
1174 Loading settings from a file::
1175
1176 with mpl.rc_context(fname='print.rc'):
1177 plt.plot(x, y) # uses 'print.rc'
1178
1179 Setting in the context body::
1180
1181 with mpl.rc_context():
1182 # will be reset
1183 mpl.rcParams['lines.linewidth'] = 5
1184 plt.plot(x, y)
1185
1186 """
1187 orig = dict(rcParams.copy())
1188 del orig['backend']
1189 try:
1190 if fname:
1191 rc_file(fname)
1192 if rc:
1193 rcParams.update(rc)
1194 yield
1195 finally:
1196 dict.update(rcParams, orig) # Revert to the original rcs.
1197
1198
1199def use(backend, *, force=True):
1200 """
1201 Select the backend used for rendering and GUI integration.
1202
1203 If pyplot is already imported, `~matplotlib.pyplot.switch_backend` is used
1204 and if the new backend is different than the current backend, all Figures
1205 will be closed.
1206
1207 Parameters
1208 ----------
1209 backend : str
1210 The backend to switch to. This can either be one of the standard
1211 backend names, which are case-insensitive:
1212
1213 - interactive backends:
1214 GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, notebook, QtAgg,
1215 QtCairo, TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo
1216
1217 - non-interactive backends:
1218 agg, cairo, pdf, pgf, ps, svg, template
1219
1220 or a string of the form: ``module://my.module.name``.
1221
1222 notebook is a synonym for nbAgg.
1223
1224 Switching to an interactive backend is not possible if an unrelated
1225 event loop has already been started (e.g., switching to GTK3Agg if a
1226 TkAgg window has already been opened). Switching to a non-interactive
1227 backend is always possible.
1228
1229 force : bool, default: True
1230 If True (the default), raise an `ImportError` if the backend cannot be
1231 set up (either because it fails to import, or because an incompatible
1232 GUI interactive framework is already running); if False, silently
1233 ignore the failure.
1234
1235 See Also
1236 --------
1237 :ref:`backends`
1238 matplotlib.get_backend
1239 matplotlib.pyplot.switch_backend
1240
1241 """
1242 name = validate_backend(backend)
1243 # don't (prematurely) resolve the "auto" backend setting
1244 if rcParams._get_backend_or_none() == name:
1245 # Nothing to do if the requested backend is already set
1246 pass
1247 else:
1248 # if pyplot is not already imported, do not import it. Doing
1249 # so may trigger a `plt.switch_backend` to the _default_ backend
1250 # before we get a chance to change to the one the user just requested
1251 plt = sys.modules.get('matplotlib.pyplot')
1252 # if pyplot is imported, then try to change backends
1253 if plt is not None:
1254 try:
1255 # we need this import check here to re-raise if the
1256 # user does not have the libraries to support their
1257 # chosen backend installed.
1258 plt.switch_backend(name)
1259 except ImportError:
1260 if force:
1261 raise
1262 # if we have not imported pyplot, then we can set the rcParam
1263 # value which will be respected when the user finally imports
1264 # pyplot
1265 else:
1266 rcParams['backend'] = backend
1267 # if the user has asked for a given backend, do not helpfully
1268 # fallback
1269 rcParams['backend_fallback'] = False
1270
1271
1272if os.environ.get('MPLBACKEND'):
1273 rcParams['backend'] = os.environ.get('MPLBACKEND')
1274
1275
1276def get_backend():
1277 """
1278 Return the name of the current backend.
1279
1280 See Also
1281 --------
1282 matplotlib.use
1283 """
1284 return rcParams['backend']
1285
1286
1287def interactive(b):
1288 """
1289 Set whether to redraw after every plotting command (e.g. `.pyplot.xlabel`).
1290 """
1291 rcParams['interactive'] = b
1292
1293
1294def is_interactive():
1295 """
1296 Return whether to redraw after every plotting command.
1297
1298 .. note::
1299
1300 This function is only intended for use in backends. End users should
1301 use `.pyplot.isinteractive` instead.
1302 """
1303 return rcParams['interactive']
1304
1305
1306def _val_or_rc(val, rc_name):
1307 """
1308 If *val* is None, return ``mpl.rcParams[rc_name]``, otherwise return val.
1309 """
1310 return val if val is not None else rcParams[rc_name]
1311
1312
1313def _init_tests():
1314 # The version of FreeType to install locally for running the tests. This must match
1315 # the value in `meson.build`.
1316 LOCAL_FREETYPE_VERSION = '2.6.1'
1317
1318 from matplotlib import ft2font
1319 if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or
1320 ft2font.__freetype_build_type__ != 'local'):
1321 _log.warning(
1322 "Matplotlib is not built with the correct FreeType version to run tests. "
1323 "Rebuild without setting system-freetype=true in Meson setup options. "
1324 "Expect many image comparison failures below. "
1325 "Expected freetype version %s. "
1326 "Found freetype version %s. "
1327 "Freetype build type is %slocal.",
1328 LOCAL_FREETYPE_VERSION,
1329 ft2font.__freetype_version__,
1330 "" if ft2font.__freetype_build_type__ == 'local' else "not ")
1331
1332
1333def _replacer(data, value):
1334 """
1335 Either returns ``data[value]`` or passes ``data`` back, converts either to
1336 a sequence.
1337 """
1338 try:
1339 # if key isn't a string don't bother
1340 if isinstance(value, str):
1341 # try to use __getitem__
1342 value = data[value]
1343 except Exception:
1344 # key does not exist, silently fall back to key
1345 pass
1346 return sanitize_sequence(value)
1347
1348
1349def _label_from_arg(y, default_name):
1350 try:
1351 return y.name
1352 except AttributeError:
1353 if isinstance(default_name, str):
1354 return default_name
1355 return None
1356
1357
1358def _add_data_doc(docstring, replace_names):
1359 """
1360 Add documentation for a *data* field to the given docstring.
1361
1362 Parameters
1363 ----------
1364 docstring : str
1365 The input docstring.
1366 replace_names : list of str or None
1367 The list of parameter names which arguments should be replaced by
1368 ``data[name]`` (if ``data[name]`` does not throw an exception). If
1369 None, replacement is attempted for all arguments.
1370
1371 Returns
1372 -------
1373 str
1374 The augmented docstring.
1375 """
1376 if (docstring is None
1377 or replace_names is not None and len(replace_names) == 0):
1378 return docstring
1379 docstring = inspect.cleandoc(docstring)
1380
1381 data_doc = ("""\
1382 If given, all parameters also accept a string ``s``, which is
1383 interpreted as ``data[s]`` (unless this raises an exception)."""
1384 if replace_names is None else f"""\
1385 If given, the following parameters also accept a string ``s``, which is
1386 interpreted as ``data[s]`` (unless this raises an exception):
1387
1388 {', '.join(map('*{}*'.format, replace_names))}""")
1389 # using string replacement instead of formatting has the advantages
1390 # 1) simpler indent handling
1391 # 2) prevent problems with formatting characters '{', '%' in the docstring
1392 if _log.level <= logging.DEBUG:
1393 # test_data_parameter_replacement() tests against these log messages
1394 # make sure to keep message and test in sync
1395 if "data : indexable object, optional" not in docstring:
1396 _log.debug("data parameter docstring error: no data parameter")
1397 if 'DATA_PARAMETER_PLACEHOLDER' not in docstring:
1398 _log.debug("data parameter docstring error: missing placeholder")
1399 return docstring.replace(' DATA_PARAMETER_PLACEHOLDER', data_doc)
1400
1401
1402def _preprocess_data(func=None, *, replace_names=None, label_namer=None):
1403 """
1404 A decorator to add a 'data' kwarg to a function.
1405
1406 When applied::
1407
1408 @_preprocess_data()
1409 def func(ax, *args, **kwargs): ...
1410
1411 the signature is modified to ``decorated(ax, *args, data=None, **kwargs)``
1412 with the following behavior:
1413
1414 - if called with ``data=None``, forward the other arguments to ``func``;
1415 - otherwise, *data* must be a mapping; for any argument passed in as a
1416 string ``name``, replace the argument by ``data[name]`` (if this does not
1417 throw an exception), then forward the arguments to ``func``.
1418
1419 In either case, any argument that is a `MappingView` is also converted to a
1420 list.
1421
1422 Parameters
1423 ----------
1424 replace_names : list of str or None, default: None
1425 The list of parameter names for which lookup into *data* should be
1426 attempted. If None, replacement is attempted for all arguments.
1427 label_namer : str, default: None
1428 If set e.g. to "namer" (which must be a kwarg in the function's
1429 signature -- not as ``**kwargs``), if the *namer* argument passed in is
1430 a (string) key of *data* and no *label* kwarg is passed, then use the
1431 (string) value of the *namer* as *label*. ::
1432
1433 @_preprocess_data(label_namer="foo")
1434 def func(foo, label=None): ...
1435
1436 func("key", data={"key": value})
1437 # is equivalent to
1438 func.__wrapped__(value, label="key")
1439 """
1440
1441 if func is None: # Return the actual decorator.
1442 return functools.partial(
1443 _preprocess_data,
1444 replace_names=replace_names, label_namer=label_namer)
1445
1446 sig = inspect.signature(func)
1447 varargs_name = None
1448 varkwargs_name = None
1449 arg_names = []
1450 params = list(sig.parameters.values())
1451 for p in params:
1452 if p.kind is Parameter.VAR_POSITIONAL:
1453 varargs_name = p.name
1454 elif p.kind is Parameter.VAR_KEYWORD:
1455 varkwargs_name = p.name
1456 else:
1457 arg_names.append(p.name)
1458 data_param = Parameter("data", Parameter.KEYWORD_ONLY, default=None)
1459 if varkwargs_name:
1460 params.insert(-1, data_param)
1461 else:
1462 params.append(data_param)
1463 new_sig = sig.replace(parameters=params)
1464 arg_names = arg_names[1:] # remove the first "ax" / self arg
1465
1466 assert {*arg_names}.issuperset(replace_names or []) or varkwargs_name, (
1467 "Matplotlib internal error: invalid replace_names "
1468 f"({replace_names!r}) for {func.__name__!r}")
1469 assert label_namer is None or label_namer in arg_names, (
1470 "Matplotlib internal error: invalid label_namer "
1471 f"({label_namer!r}) for {func.__name__!r}")
1472
1473 @functools.wraps(func)
1474 def inner(ax, *args, data=None, **kwargs):
1475 if data is None:
1476 return func(
1477 ax,
1478 *map(sanitize_sequence, args),
1479 **{k: sanitize_sequence(v) for k, v in kwargs.items()})
1480
1481 bound = new_sig.bind(ax, *args, **kwargs)
1482 auto_label = (bound.arguments.get(label_namer)
1483 or bound.kwargs.get(label_namer))
1484
1485 for k, v in bound.arguments.items():
1486 if k == varkwargs_name:
1487 for k1, v1 in v.items():
1488 if replace_names is None or k1 in replace_names:
1489 v[k1] = _replacer(data, v1)
1490 elif k == varargs_name:
1491 if replace_names is None:
1492 bound.arguments[k] = tuple(_replacer(data, v1) for v1 in v)
1493 else:
1494 if replace_names is None or k in replace_names:
1495 bound.arguments[k] = _replacer(data, v)
1496
1497 new_args = bound.args
1498 new_kwargs = bound.kwargs
1499
1500 args_and_kwargs = {**bound.arguments, **bound.kwargs}
1501 if label_namer and "label" not in args_and_kwargs:
1502 new_kwargs["label"] = _label_from_arg(
1503 args_and_kwargs.get(label_namer), auto_label)
1504
1505 return func(*new_args, **new_kwargs)
1506
1507 inner.__doc__ = _add_data_doc(inner.__doc__, replace_names)
1508 inner.__signature__ = new_sig
1509 return inner
1510
1511
1512_log.debug('interactive is %s', is_interactive())
1513_log.debug('platform is %s', sys.platform)
1514
1515
1516# workaround: we must defer colormaps import to after loading rcParams, because
1517# colormap creation depends on rcParams
1518from matplotlib.cm import _colormaps as colormaps # noqa: E402
1519from matplotlib.colors import _color_sequences as color_sequences # noqa: E402