Spaces:
Running
Running
| """ | |
| An object-oriented plotting library. | |
| A procedural interface is provided by the companion pyplot module, | |
| which may be imported directly, e.g.:: | |
| import matplotlib.pyplot as plt | |
| or using ipython:: | |
| ipython | |
| at your terminal, followed by:: | |
| In [1]: %matplotlib | |
| In [2]: import matplotlib.pyplot as plt | |
| at the ipython shell prompt. | |
| For the most part, direct use of the explicit object-oriented library is | |
| encouraged when programming; the implicit pyplot interface is primarily for | |
| working interactively. The exceptions to this suggestion are the pyplot | |
| functions `.pyplot.figure`, `.pyplot.subplot`, `.pyplot.subplots`, and | |
| `.pyplot.savefig`, which can greatly simplify scripting. See | |
| :ref:`api_interfaces` for an explanation of the tradeoffs between the implicit | |
| and explicit interfaces. | |
| Modules include: | |
| :mod:`matplotlib.axes` | |
| The `~.axes.Axes` class. Most pyplot functions are wrappers for | |
| `~.axes.Axes` methods. The axes module is the highest level of OO | |
| access to the library. | |
| :mod:`matplotlib.figure` | |
| The `.Figure` class. | |
| :mod:`matplotlib.artist` | |
| The `.Artist` base class for all classes that draw things. | |
| :mod:`matplotlib.lines` | |
| The `.Line2D` class for drawing lines and markers. | |
| :mod:`matplotlib.patches` | |
| Classes for drawing polygons. | |
| :mod:`matplotlib.text` | |
| The `.Text` and `.Annotation` classes. | |
| :mod:`matplotlib.image` | |
| The `.AxesImage` and `.FigureImage` classes. | |
| :mod:`matplotlib.collections` | |
| Classes for efficient drawing of groups of lines or polygons. | |
| :mod:`matplotlib.colors` | |
| Color specifications and making colormaps. | |
| :mod:`matplotlib.cm` | |
| Colormaps, and the `.ScalarMappable` mixin class for providing color | |
| mapping functionality to other classes. | |
| :mod:`matplotlib.ticker` | |
| Calculation of tick mark locations and formatting of tick labels. | |
| :mod:`matplotlib.backends` | |
| A subpackage with modules for various GUI libraries and output formats. | |
| The base matplotlib namespace includes: | |
| `~matplotlib.rcParams` | |
| Default configuration settings; their defaults may be overridden using | |
| a :file:`matplotlibrc` file. | |
| `~matplotlib.use` | |
| Setting the Matplotlib backend. This should be called before any | |
| figure is created, because it is not possible to switch between | |
| different GUI backends after that. | |
| The following environment variables can be used to customize the behavior: | |
| :envvar:`MPLBACKEND` | |
| This optional variable can be set to choose the Matplotlib backend. See | |
| :ref:`what-is-a-backend`. | |
| :envvar:`MPLCONFIGDIR` | |
| This is the directory used to store user customizations to | |
| Matplotlib, as well as some caches to improve performance. If | |
| :envvar:`MPLCONFIGDIR` is not defined, :file:`{HOME}/.config/matplotlib` | |
| and :file:`{HOME}/.cache/matplotlib` are used on Linux, and | |
| :file:`{HOME}/.matplotlib` on other platforms, if they are | |
| writable. Otherwise, the Python standard library's `tempfile.gettempdir` | |
| is used to find a base directory in which the :file:`matplotlib` | |
| subdirectory is created. | |
| Matplotlib was initially written by John D. Hunter (1968-2012) and is now | |
| developed and maintained by a host of others. | |
| Occasionally the internal documentation (python docstrings) will refer | |
| to MATLAB®, a registered trademark of The MathWorks, Inc. | |
| """ | |
| __all__ = [ | |
| "__bibtex__", | |
| "__version__", | |
| "__version_info__", | |
| "set_loglevel", | |
| "ExecutableNotFoundError", | |
| "get_configdir", | |
| "get_cachedir", | |
| "get_data_path", | |
| "matplotlib_fname", | |
| "MatplotlibDeprecationWarning", | |
| "RcParams", | |
| "rc_params", | |
| "rc_params_from_file", | |
| "rcParamsDefault", | |
| "rcParams", | |
| "rcParamsOrig", | |
| "defaultParams", | |
| "rc", | |
| "rcdefaults", | |
| "rc_file_defaults", | |
| "rc_file", | |
| "rc_context", | |
| "use", | |
| "get_backend", | |
| "interactive", | |
| "is_interactive", | |
| "colormaps", | |
| "multivar_colormaps", | |
| "bivar_colormaps", | |
| "color_sequences", | |
| ] | |
| import atexit | |
| from collections import namedtuple | |
| from collections.abc import MutableMapping | |
| import contextlib | |
| import functools | |
| import importlib | |
| import inspect | |
| from inspect import Parameter | |
| import locale | |
| import logging | |
| import os | |
| from pathlib import Path | |
| import pprint | |
| import re | |
| import shutil | |
| import subprocess | |
| import sys | |
| import tempfile | |
| from packaging.version import parse as parse_version | |
| # cbook must import matplotlib only within function | |
| # definitions, so it is safe to import from it here. | |
| from . import _api, _version, cbook, _docstring, rcsetup | |
| from matplotlib._api import MatplotlibDeprecationWarning | |
| from matplotlib.rcsetup import cycler # noqa: F401 | |
| _log = logging.getLogger(__name__) | |
| __bibtex__ = r"""@Article{Hunter:2007, | |
| Author = {Hunter, J. D.}, | |
| Title = {Matplotlib: A 2D graphics environment}, | |
| Journal = {Computing in Science \& Engineering}, | |
| Volume = {9}, | |
| Number = {3}, | |
| Pages = {90--95}, | |
| abstract = {Matplotlib is a 2D graphics package used for Python | |
| for application development, interactive scripting, and | |
| publication-quality image generation across user | |
| interfaces and operating systems.}, | |
| publisher = {IEEE COMPUTER SOC}, | |
| year = 2007 | |
| }""" | |
| # modelled after sys.version_info | |
| _VersionInfo = namedtuple('_VersionInfo', | |
| 'major, minor, micro, releaselevel, serial') | |
| def _parse_to_version_info(version_str): | |
| """ | |
| Parse a version string to a namedtuple analogous to sys.version_info. | |
| See: | |
| https://packaging.pypa.io/en/latest/version.html#packaging.version.parse | |
| https://docs.python.org/3/library/sys.html#sys.version_info | |
| """ | |
| v = parse_version(version_str) | |
| if v.pre is None and v.post is None and v.dev is None: | |
| return _VersionInfo(v.major, v.minor, v.micro, 'final', 0) | |
| elif v.dev is not None: | |
| return _VersionInfo(v.major, v.minor, v.micro, 'alpha', v.dev) | |
| elif v.pre is not None: | |
| releaselevel = { | |
| 'a': 'alpha', | |
| 'b': 'beta', | |
| 'rc': 'candidate'}.get(v.pre[0], 'alpha') | |
| return _VersionInfo(v.major, v.minor, v.micro, releaselevel, v.pre[1]) | |
| else: | |
| # fallback for v.post: guess-next-dev scheme from setuptools_scm | |
| return _VersionInfo(v.major, v.minor, v.micro + 1, 'alpha', v.post) | |
| def _get_version(): | |
| """Return the version string used for __version__.""" | |
| # Only shell out to a git subprocess if really needed, i.e. when we are in | |
| # a matplotlib git repo but not in a shallow clone, such as those used by | |
| # CI, as the latter would trigger a warning from setuptools_scm. | |
| root = Path(__file__).resolve().parents[2] | |
| if ((root / ".matplotlib-repo").exists() | |
| and (root / ".git").exists() | |
| and not (root / ".git/shallow").exists()): | |
| try: | |
| import setuptools_scm | |
| except ImportError: | |
| pass | |
| else: | |
| return setuptools_scm.get_version( | |
| root=root, | |
| dist_name="matplotlib", | |
| version_scheme="release-branch-semver", | |
| local_scheme="node-and-date", | |
| fallback_version=_version.version, | |
| ) | |
| # Get the version from the _version.py file if not in repo or setuptools_scm is | |
| # unavailable. | |
| return _version.version | |
| class __getattr__: | |
| __version__ = property(lambda self: _get_version()) | |
| __version_info__ = property( | |
| lambda self: _parse_to_version_info(self.__version__)) | |
| def _check_versions(): | |
| # Quickfix to ensure Microsoft Visual C++ redistributable | |
| # DLLs are loaded before importing kiwisolver | |
| from . import ft2font # noqa: F401 | |
| for modname, minver in [ | |
| ("cycler", "0.10"), | |
| ("dateutil", "2.7"), | |
| ("kiwisolver", "1.3.1"), | |
| ("numpy", "1.23"), | |
| ("pyparsing", "2.3.1"), | |
| ]: | |
| module = importlib.import_module(modname) | |
| if parse_version(module.__version__) < parse_version(minver): | |
| raise ImportError(f"Matplotlib requires {modname}>={minver}; " | |
| f"you have {module.__version__}") | |
| _check_versions() | |
| # The decorator ensures this always returns the same handler (and it is only | |
| # attached once). | |
| def _ensure_handler(): | |
| """ | |
| The first time this function is called, attach a `StreamHandler` using the | |
| same format as `logging.basicConfig` to the Matplotlib root logger. | |
| Return this handler every time this function is called. | |
| """ | |
| handler = logging.StreamHandler() | |
| handler.setFormatter(logging.Formatter(logging.BASIC_FORMAT)) | |
| _log.addHandler(handler) | |
| return handler | |
| def set_loglevel(level): | |
| """ | |
| Configure Matplotlib's logging levels. | |
| Matplotlib uses the standard library `logging` framework under the root | |
| logger 'matplotlib'. This is a helper function to: | |
| - set Matplotlib's root logger level | |
| - set the root logger handler's level, creating the handler | |
| if it does not exist yet | |
| Typically, one should call ``set_loglevel("info")`` or | |
| ``set_loglevel("debug")`` to get additional debugging information. | |
| Users or applications that are installing their own logging handlers | |
| may want to directly manipulate ``logging.getLogger('matplotlib')`` rather | |
| than use this function. | |
| Parameters | |
| ---------- | |
| level : {"notset", "debug", "info", "warning", "error", "critical"} | |
| The log level of the handler. | |
| Notes | |
| ----- | |
| The first time this function is called, an additional handler is attached | |
| to Matplotlib's root handler; this handler is reused every time and this | |
| function simply manipulates the logger and handler's level. | |
| """ | |
| _log.setLevel(level.upper()) | |
| _ensure_handler().setLevel(level.upper()) | |
| def _logged_cached(fmt, func=None): | |
| """ | |
| Decorator that logs a function's return value, and memoizes that value. | |
| After :: | |
| @_logged_cached(fmt) | |
| def func(): ... | |
| the first call to *func* will log its return value at the DEBUG level using | |
| %-format string *fmt*, and memoize it; later calls to *func* will directly | |
| return that value. | |
| """ | |
| if func is None: # Return the actual decorator. | |
| return functools.partial(_logged_cached, fmt) | |
| called = False | |
| ret = None | |
| def wrapper(**kwargs): | |
| nonlocal called, ret | |
| if not called: | |
| ret = func(**kwargs) | |
| called = True | |
| _log.debug(fmt, ret) | |
| return ret | |
| return wrapper | |
| _ExecInfo = namedtuple("_ExecInfo", "executable raw_version version") | |
| class ExecutableNotFoundError(FileNotFoundError): | |
| """ | |
| Error raised when an executable that Matplotlib optionally | |
| depends on can't be found. | |
| """ | |
| pass | |
| def _get_executable_info(name): | |
| """ | |
| Get the version of some executable that Matplotlib optionally depends on. | |
| .. warning:: | |
| The list of executables that this function supports is set according to | |
| Matplotlib's internal needs, and may change without notice. | |
| Parameters | |
| ---------- | |
| name : str | |
| The executable to query. The following values are currently supported: | |
| "dvipng", "gs", "inkscape", "magick", "pdftocairo", "pdftops". This | |
| list is subject to change without notice. | |
| Returns | |
| ------- | |
| tuple | |
| A namedtuple with fields ``executable`` (`str`) and ``version`` | |
| (`packaging.Version`, or ``None`` if the version cannot be determined). | |
| Raises | |
| ------ | |
| ExecutableNotFoundError | |
| If the executable is not found or older than the oldest version | |
| supported by Matplotlib. For debugging purposes, it is also | |
| possible to "hide" an executable from Matplotlib by adding it to the | |
| :envvar:`_MPLHIDEEXECUTABLES` environment variable (a comma-separated | |
| list), which must be set prior to any calls to this function. | |
| ValueError | |
| If the executable is not one that we know how to query. | |
| """ | |
| def impl(args, regex, min_ver=None, ignore_exit_code=False): | |
| # Execute the subprocess specified by args; capture stdout and stderr. | |
| # Search for a regex match in the output; if the match succeeds, the | |
| # first group of the match is the version. | |
| # Return an _ExecInfo if the executable exists, and has a version of | |
| # at least min_ver (if set); else, raise ExecutableNotFoundError. | |
| try: | |
| output = subprocess.check_output( | |
| args, stderr=subprocess.STDOUT, | |
| text=True, errors="replace", timeout=30) | |
| except subprocess.CalledProcessError as _cpe: | |
| if ignore_exit_code: | |
| output = _cpe.output | |
| else: | |
| raise ExecutableNotFoundError(str(_cpe)) from _cpe | |
| except subprocess.TimeoutExpired as _te: | |
| msg = f"Timed out running {cbook._pformat_subprocess(args)}" | |
| raise ExecutableNotFoundError(msg) from _te | |
| except OSError as _ose: | |
| raise ExecutableNotFoundError(str(_ose)) from _ose | |
| match = re.search(regex, output) | |
| if match: | |
| raw_version = match.group(1) | |
| version = parse_version(raw_version) | |
| if min_ver is not None and version < parse_version(min_ver): | |
| raise ExecutableNotFoundError( | |
| f"You have {args[0]} version {version} but the minimum " | |
| f"version supported by Matplotlib is {min_ver}") | |
| return _ExecInfo(args[0], raw_version, version) | |
| else: | |
| raise ExecutableNotFoundError( | |
| f"Failed to determine the version of {args[0]} from " | |
| f"{' '.join(args)}, which output {output}") | |
| if name in os.environ.get("_MPLHIDEEXECUTABLES", "").split(","): | |
| raise ExecutableNotFoundError(f"{name} was hidden") | |
| if name == "dvipng": | |
| return impl(["dvipng", "-version"], "(?m)^dvipng(?: .*)? (.+)", "1.6") | |
| elif name == "gs": | |
| execs = (["gswin32c", "gswin64c", "mgs", "gs"] # "mgs" for miktex. | |
| if sys.platform == "win32" else | |
| ["gs"]) | |
| for e in execs: | |
| try: | |
| return impl([e, "--version"], "(.*)", "9") | |
| except ExecutableNotFoundError: | |
| pass | |
| message = "Failed to find a Ghostscript installation" | |
| raise ExecutableNotFoundError(message) | |
| elif name == "inkscape": | |
| try: | |
| # Try headless option first (needed for Inkscape version < 1.0): | |
| return impl(["inkscape", "--without-gui", "-V"], | |
| "Inkscape ([^ ]*)") | |
| except ExecutableNotFoundError: | |
| pass # Suppress exception chaining. | |
| # If --without-gui is not accepted, we may be using Inkscape >= 1.0 so | |
| # try without it: | |
| return impl(["inkscape", "-V"], "Inkscape ([^ ]*)") | |
| elif name == "magick": | |
| if sys.platform == "win32": | |
| # Check the registry to avoid confusing ImageMagick's convert with | |
| # Windows's builtin convert.exe. | |
| import winreg | |
| binpath = "" | |
| for flag in [0, winreg.KEY_WOW64_32KEY, winreg.KEY_WOW64_64KEY]: | |
| try: | |
| with winreg.OpenKeyEx( | |
| winreg.HKEY_LOCAL_MACHINE, | |
| r"Software\Imagemagick\Current", | |
| 0, winreg.KEY_QUERY_VALUE | flag) as hkey: | |
| binpath = winreg.QueryValueEx(hkey, "BinPath")[0] | |
| except OSError: | |
| pass | |
| path = None | |
| if binpath: | |
| for name in ["convert.exe", "magick.exe"]: | |
| candidate = Path(binpath, name) | |
| if candidate.exists(): | |
| path = str(candidate) | |
| break | |
| if path is None: | |
| raise ExecutableNotFoundError( | |
| "Failed to find an ImageMagick installation") | |
| else: | |
| path = "convert" | |
| info = impl([path, "--version"], r"^Version: ImageMagick (\S*)") | |
| if info.raw_version == "7.0.10-34": | |
| # https://github.com/ImageMagick/ImageMagick/issues/2720 | |
| raise ExecutableNotFoundError( | |
| f"You have ImageMagick {info.version}, which is unsupported") | |
| return info | |
| elif name == "pdftocairo": | |
| return impl(["pdftocairo", "-v"], "pdftocairo version (.*)") | |
| elif name == "pdftops": | |
| info = impl(["pdftops", "-v"], "^pdftops version (.*)", | |
| ignore_exit_code=True) | |
| if info and not ( | |
| 3 <= info.version.major or | |
| # poppler version numbers. | |
| parse_version("0.9") <= info.version < parse_version("1.0")): | |
| raise ExecutableNotFoundError( | |
| f"You have pdftops version {info.version} but the minimum " | |
| f"version supported by Matplotlib is 3.0") | |
| return info | |
| else: | |
| raise ValueError(f"Unknown executable: {name!r}") | |
| def _get_xdg_config_dir(): | |
| """ | |
| Return the XDG configuration directory, according to the XDG base | |
| directory spec: | |
| https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html | |
| """ | |
| return os.environ.get('XDG_CONFIG_HOME') or str(Path.home() / ".config") | |
| def _get_xdg_cache_dir(): | |
| """ | |
| Return the XDG cache directory, according to the XDG base directory spec: | |
| https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html | |
| """ | |
| return os.environ.get('XDG_CACHE_HOME') or str(Path.home() / ".cache") | |
| def _get_config_or_cache_dir(xdg_base_getter): | |
| configdir = os.environ.get('MPLCONFIGDIR') | |
| if configdir: | |
| configdir = Path(configdir) | |
| elif sys.platform.startswith(('linux', 'freebsd')): | |
| # Only call _xdg_base_getter here so that MPLCONFIGDIR is tried first, | |
| # as _xdg_base_getter can throw. | |
| configdir = Path(xdg_base_getter(), "matplotlib") | |
| else: | |
| configdir = Path.home() / ".matplotlib" | |
| # Resolve the path to handle potential issues with inaccessible symlinks. | |
| configdir = configdir.resolve() | |
| try: | |
| configdir.mkdir(parents=True, exist_ok=True) | |
| except OSError as exc: | |
| _log.warning("mkdir -p failed for path %s: %s", configdir, exc) | |
| else: | |
| if os.access(str(configdir), os.W_OK) and configdir.is_dir(): | |
| return str(configdir) | |
| _log.warning("%s is not a writable directory", configdir) | |
| # If the config or cache directory cannot be created or is not a writable | |
| # directory, create a temporary one. | |
| try: | |
| tmpdir = tempfile.mkdtemp(prefix="matplotlib-") | |
| except OSError as exc: | |
| raise OSError( | |
| f"Matplotlib requires access to a writable cache directory, but there " | |
| f"was an issue with the default path ({configdir}), and a temporary " | |
| f"directory could not be created; set the MPLCONFIGDIR environment " | |
| f"variable to a writable directory") from exc | |
| os.environ["MPLCONFIGDIR"] = tmpdir | |
| atexit.register(shutil.rmtree, tmpdir) | |
| _log.warning( | |
| "Matplotlib created a temporary cache directory at %s because there was " | |
| "an issue with the default path (%s); it is highly recommended to set the " | |
| "MPLCONFIGDIR environment variable to a writable directory, in particular to " | |
| "speed up the import of Matplotlib and to better support multiprocessing.", | |
| tmpdir, configdir) | |
| return tmpdir | |
| def get_configdir(): | |
| """ | |
| Return the string path of the configuration directory. | |
| The directory is chosen as follows: | |
| 1. If the MPLCONFIGDIR environment variable is supplied, choose that. | |
| 2. On Linux, follow the XDG specification and look first in | |
| ``$XDG_CONFIG_HOME``, if defined, or ``$HOME/.config``. On other | |
| platforms, choose ``$HOME/.matplotlib``. | |
| 3. If the chosen directory exists and is writable, use that as the | |
| configuration directory. | |
| 4. Else, create a temporary directory, and use it as the configuration | |
| directory. | |
| """ | |
| return _get_config_or_cache_dir(_get_xdg_config_dir) | |
| def get_cachedir(): | |
| """ | |
| Return the string path of the cache directory. | |
| The procedure used to find the directory is the same as for | |
| `get_configdir`, except using ``$XDG_CACHE_HOME``/``$HOME/.cache`` instead. | |
| """ | |
| return _get_config_or_cache_dir(_get_xdg_cache_dir) | |
| def get_data_path(): | |
| """Return the path to Matplotlib data.""" | |
| return str(Path(__file__).with_name("mpl-data")) | |
| def matplotlib_fname(): | |
| """ | |
| Get the location of the config file. | |
| The file location is determined in the following order | |
| - ``$PWD/matplotlibrc`` | |
| - ``$MATPLOTLIBRC`` if it is not a directory | |
| - ``$MATPLOTLIBRC/matplotlibrc`` | |
| - ``$MPLCONFIGDIR/matplotlibrc`` | |
| - On Linux, | |
| - ``$XDG_CONFIG_HOME/matplotlib/matplotlibrc`` (if ``$XDG_CONFIG_HOME`` | |
| is defined) | |
| - or ``$HOME/.config/matplotlib/matplotlibrc`` (if ``$XDG_CONFIG_HOME`` | |
| is not defined) | |
| - On other platforms, | |
| - ``$HOME/.matplotlib/matplotlibrc`` if ``$HOME`` is defined | |
| - Lastly, it looks in ``$MATPLOTLIBDATA/matplotlibrc``, which should always | |
| exist. | |
| """ | |
| def gen_candidates(): | |
| # rely on down-stream code to make absolute. This protects us | |
| # from having to directly get the current working directory | |
| # which can fail if the user has ended up with a cwd that is | |
| # non-existent. | |
| yield 'matplotlibrc' | |
| try: | |
| matplotlibrc = os.environ['MATPLOTLIBRC'] | |
| except KeyError: | |
| pass | |
| else: | |
| yield matplotlibrc | |
| yield os.path.join(matplotlibrc, 'matplotlibrc') | |
| yield os.path.join(get_configdir(), 'matplotlibrc') | |
| yield os.path.join(get_data_path(), 'matplotlibrc') | |
| for fname in gen_candidates(): | |
| if os.path.exists(fname) and not os.path.isdir(fname): | |
| return fname | |
| raise RuntimeError("Could not find matplotlibrc file; your Matplotlib " | |
| "install is broken") | |
| # rcParams deprecated and automatically mapped to another key. | |
| # Values are tuples of (version, new_name, f_old2new, f_new2old). | |
| _deprecated_map = {} | |
| # rcParams deprecated; some can manually be mapped to another key. | |
| # Values are tuples of (version, new_name_or_None). | |
| _deprecated_ignore_map = {} | |
| # rcParams deprecated; can use None to suppress warnings; remain actually | |
| # listed in the rcParams. | |
| # Values are tuples of (version,) | |
| _deprecated_remain_as_none = {} | |
| class RcParams(MutableMapping, dict): | |
| """ | |
| A dict-like key-value store for config parameters, including validation. | |
| Validating functions are defined and associated with rc parameters in | |
| :mod:`matplotlib.rcsetup`. | |
| The list of rcParams is: | |
| %s | |
| See Also | |
| -------- | |
| :ref:`customizing-with-matplotlibrc-files` | |
| """ | |
| validate = rcsetup._validators | |
| # validate values on the way in | |
| def __init__(self, *args, **kwargs): | |
| self.update(*args, **kwargs) | |
| def _set(self, key, val): | |
| """ | |
| Directly write data bypassing deprecation and validation logic. | |
| Notes | |
| ----- | |
| As end user or downstream library you almost always should use | |
| ``rcParams[key] = val`` and not ``_set()``. | |
| There are only very few special cases that need direct data access. | |
| These cases previously used ``dict.__setitem__(rcParams, key, val)``, | |
| which is now deprecated and replaced by ``rcParams._set(key, val)``. | |
| Even though private, we guarantee API stability for ``rcParams._set``, | |
| i.e. it is subject to Matplotlib's API and deprecation policy. | |
| :meta public: | |
| """ | |
| dict.__setitem__(self, key, val) | |
| def _get(self, key): | |
| """ | |
| Directly read data bypassing deprecation, backend and validation | |
| logic. | |
| Notes | |
| ----- | |
| As end user or downstream library you almost always should use | |
| ``val = rcParams[key]`` and not ``_get()``. | |
| There are only very few special cases that need direct data access. | |
| These cases previously used ``dict.__getitem__(rcParams, key, val)``, | |
| which is now deprecated and replaced by ``rcParams._get(key)``. | |
| Even though private, we guarantee API stability for ``rcParams._get``, | |
| i.e. it is subject to Matplotlib's API and deprecation policy. | |
| :meta public: | |
| """ | |
| return dict.__getitem__(self, key) | |
| def _update_raw(self, other_params): | |
| """ | |
| Directly update the data from *other_params*, bypassing deprecation, | |
| backend and validation logic on both sides. | |
| This ``rcParams._update_raw(params)`` replaces the previous pattern | |
| ``dict.update(rcParams, params)``. | |
| Parameters | |
| ---------- | |
| other_params : dict or `.RcParams` | |
| The input mapping from which to update. | |
| """ | |
| if isinstance(other_params, RcParams): | |
| other_params = dict.items(other_params) | |
| dict.update(self, other_params) | |
| def _ensure_has_backend(self): | |
| """ | |
| Ensure that a "backend" entry exists. | |
| Normally, the default matplotlibrc file contains *no* entry for "backend" (the | |
| corresponding line starts with ##, not #; we fill in _auto_backend_sentinel | |
| in that case. However, packagers can set a different default backend | |
| (resulting in a normal `#backend: foo` line) in which case we should *not* | |
| fill in _auto_backend_sentinel. | |
| """ | |
| dict.setdefault(self, "backend", rcsetup._auto_backend_sentinel) | |
| def __setitem__(self, key, val): | |
| try: | |
| if key in _deprecated_map: | |
| version, alt_key, alt_val, inverse_alt = _deprecated_map[key] | |
| _api.warn_deprecated( | |
| version, name=key, obj_type="rcparam", alternative=alt_key) | |
| key = alt_key | |
| val = alt_val(val) | |
| elif key in _deprecated_remain_as_none and val is not None: | |
| version, = _deprecated_remain_as_none[key] | |
| _api.warn_deprecated(version, name=key, obj_type="rcparam") | |
| elif key in _deprecated_ignore_map: | |
| version, alt_key = _deprecated_ignore_map[key] | |
| _api.warn_deprecated( | |
| version, name=key, obj_type="rcparam", alternative=alt_key) | |
| return | |
| elif key == 'backend': | |
| if val is rcsetup._auto_backend_sentinel: | |
| if 'backend' in self: | |
| return | |
| try: | |
| cval = self.validate[key](val) | |
| except ValueError as ve: | |
| raise ValueError(f"Key {key}: {ve}") from None | |
| self._set(key, cval) | |
| except KeyError as err: | |
| raise KeyError( | |
| f"{key} is not a valid rc parameter (see rcParams.keys() for " | |
| f"a list of valid parameters)") from err | |
| def __getitem__(self, key): | |
| if key in _deprecated_map: | |
| version, alt_key, alt_val, inverse_alt = _deprecated_map[key] | |
| _api.warn_deprecated( | |
| version, name=key, obj_type="rcparam", alternative=alt_key) | |
| return inverse_alt(self._get(alt_key)) | |
| elif key in _deprecated_ignore_map: | |
| version, alt_key = _deprecated_ignore_map[key] | |
| _api.warn_deprecated( | |
| version, name=key, obj_type="rcparam", alternative=alt_key) | |
| return self._get(alt_key) if alt_key else None | |
| # In theory, this should only ever be used after the global rcParams | |
| # has been set up, but better be safe e.g. in presence of breakpoints. | |
| elif key == "backend" and self is globals().get("rcParams"): | |
| val = self._get(key) | |
| if val is rcsetup._auto_backend_sentinel: | |
| from matplotlib import pyplot as plt | |
| plt.switch_backend(rcsetup._auto_backend_sentinel) | |
| return self._get(key) | |
| def _get_backend_or_none(self): | |
| """Get the requested backend, if any, without triggering resolution.""" | |
| backend = self._get("backend") | |
| return None if backend is rcsetup._auto_backend_sentinel else backend | |
| def __repr__(self): | |
| class_name = self.__class__.__name__ | |
| indent = len(class_name) + 1 | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| repr_split = pprint.pformat(dict(self), indent=1, | |
| width=80 - indent).split('\n') | |
| repr_indented = ('\n' + ' ' * indent).join(repr_split) | |
| return f'{class_name}({repr_indented})' | |
| def __str__(self): | |
| return '\n'.join(map('{0[0]}: {0[1]}'.format, sorted(self.items()))) | |
| def __iter__(self): | |
| """Yield sorted list of keys.""" | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| yield from sorted(dict.__iter__(self)) | |
| def __len__(self): | |
| return dict.__len__(self) | |
| def find_all(self, pattern): | |
| """ | |
| Return the subset of this RcParams dictionary whose keys match, | |
| using :func:`re.search`, the given ``pattern``. | |
| .. note:: | |
| Changes to the returned dictionary are *not* propagated to | |
| the parent RcParams dictionary. | |
| """ | |
| pattern_re = re.compile(pattern) | |
| return RcParams((key, value) | |
| for key, value in self.items() | |
| if pattern_re.search(key)) | |
| def copy(self): | |
| """Copy this RcParams instance.""" | |
| rccopy = RcParams() | |
| for k in self: # Skip deprecations and revalidation. | |
| rccopy._set(k, self._get(k)) | |
| return rccopy | |
| def rc_params(fail_on_error=False): | |
| """Construct a `RcParams` instance from the default Matplotlib rc file.""" | |
| return rc_params_from_file(matplotlib_fname(), fail_on_error) | |
| def _get_ssl_context(): | |
| try: | |
| import certifi | |
| except ImportError: | |
| _log.debug("Could not import certifi.") | |
| return None | |
| import ssl | |
| return ssl.create_default_context(cafile=certifi.where()) | |
| def _open_file_or_url(fname): | |
| if (isinstance(fname, str) | |
| and fname.startswith(('http://', 'https://', 'ftp://', 'file:'))): | |
| import urllib.request | |
| ssl_ctx = _get_ssl_context() | |
| if ssl_ctx is None: | |
| _log.debug( | |
| "Could not get certifi ssl context, https may not work." | |
| ) | |
| with urllib.request.urlopen(fname, context=ssl_ctx) as f: | |
| yield (line.decode('utf-8') for line in f) | |
| else: | |
| fname = os.path.expanduser(fname) | |
| with open(fname, encoding='utf-8') as f: | |
| yield f | |
| def _rc_params_in_file(fname, transform=lambda x: x, fail_on_error=False): | |
| """ | |
| Construct a `RcParams` instance from file *fname*. | |
| Unlike `rc_params_from_file`, the configuration class only contains the | |
| parameters specified in the file (i.e. default values are not filled in). | |
| Parameters | |
| ---------- | |
| fname : path-like | |
| The loaded file. | |
| transform : callable, default: the identity function | |
| A function called on each individual line of the file to transform it, | |
| before further parsing. | |
| fail_on_error : bool, default: False | |
| Whether invalid entries should result in an exception or a warning. | |
| """ | |
| import matplotlib as mpl | |
| rc_temp = {} | |
| with _open_file_or_url(fname) as fd: | |
| try: | |
| for line_no, line in enumerate(fd, 1): | |
| line = transform(line) | |
| strippedline = cbook._strip_comment(line) | |
| if not strippedline: | |
| continue | |
| tup = strippedline.split(':', 1) | |
| if len(tup) != 2: | |
| _log.warning('Missing colon in file %r, line %d (%r)', | |
| fname, line_no, line.rstrip('\n')) | |
| continue | |
| key, val = tup | |
| key = key.strip() | |
| val = val.strip() | |
| if val.startswith('"') and val.endswith('"'): | |
| val = val[1:-1] # strip double quotes | |
| if key in rc_temp: | |
| _log.warning('Duplicate key in file %r, line %d (%r)', | |
| fname, line_no, line.rstrip('\n')) | |
| rc_temp[key] = (val, line, line_no) | |
| except UnicodeDecodeError: | |
| _log.warning('Cannot decode configuration file %r as utf-8.', | |
| fname) | |
| raise | |
| config = RcParams() | |
| for key, (val, line, line_no) in rc_temp.items(): | |
| if key in rcsetup._validators: | |
| if fail_on_error: | |
| config[key] = val # try to convert to proper type or raise | |
| else: | |
| try: | |
| config[key] = val # try to convert to proper type or skip | |
| except Exception as msg: | |
| _log.warning('Bad value in file %r, line %d (%r): %s', | |
| fname, line_no, line.rstrip('\n'), msg) | |
| elif key in _deprecated_ignore_map: | |
| version, alt_key = _deprecated_ignore_map[key] | |
| _api.warn_deprecated( | |
| version, name=key, alternative=alt_key, obj_type='rcparam', | |
| addendum="Please update your matplotlibrc.") | |
| else: | |
| # __version__ must be looked up as an attribute to trigger the | |
| # module-level __getattr__. | |
| version = ('main' if '.post' in mpl.__version__ | |
| else f'v{mpl.__version__}') | |
| _log.warning(""" | |
| Bad key %(key)s in file %(fname)s, line %(line_no)s (%(line)r) | |
| You probably need to get an updated matplotlibrc file from | |
| https://github.com/matplotlib/matplotlib/blob/%(version)s/lib/matplotlib/mpl-data/matplotlibrc | |
| or from the matplotlib source distribution""", | |
| dict(key=key, fname=fname, line_no=line_no, | |
| line=line.rstrip('\n'), version=version)) | |
| return config | |
| def rc_params_from_file(fname, fail_on_error=False, use_default_template=True): | |
| """ | |
| Construct a `RcParams` from file *fname*. | |
| Parameters | |
| ---------- | |
| fname : str or path-like | |
| A file with Matplotlib rc settings. | |
| fail_on_error : bool | |
| If True, raise an error when the parser fails to convert a parameter. | |
| use_default_template : bool | |
| If True, initialize with default parameters before updating with those | |
| in the given file. If False, the configuration class only contains the | |
| parameters specified in the file. (Useful for updating dicts.) | |
| """ | |
| config_from_file = _rc_params_in_file(fname, fail_on_error=fail_on_error) | |
| if not use_default_template: | |
| return config_from_file | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| config = RcParams({**rcParamsDefault, **config_from_file}) | |
| if "".join(config['text.latex.preamble']): | |
| _log.info(""" | |
| ***************************************************************** | |
| You have the following UNSUPPORTED LaTeX preamble customizations: | |
| %s | |
| Please do not ask for support with these customizations active. | |
| ***************************************************************** | |
| """, '\n'.join(config['text.latex.preamble'])) | |
| _log.debug('loaded rc file %s', fname) | |
| return config | |
| rcParamsDefault = _rc_params_in_file( | |
| cbook._get_data_path("matplotlibrc"), | |
| # Strip leading comment. | |
| transform=lambda line: line[1:] if line.startswith("#") else line, | |
| fail_on_error=True) | |
| rcParamsDefault._update_raw(rcsetup._hardcoded_defaults) | |
| rcParamsDefault._ensure_has_backend() | |
| rcParams = RcParams() # The global instance. | |
| rcParams._update_raw(rcParamsDefault) | |
| rcParams._update_raw(_rc_params_in_file(matplotlib_fname())) | |
| rcParamsOrig = rcParams.copy() | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| # This also checks that all rcParams are indeed listed in the template. | |
| # Assigning to rcsetup.defaultParams is left only for backcompat. | |
| defaultParams = rcsetup.defaultParams = { | |
| # We want to resolve deprecated rcParams, but not backend... | |
| key: [(rcsetup._auto_backend_sentinel if key == "backend" else | |
| rcParamsDefault[key]), | |
| validator] | |
| for key, validator in rcsetup._validators.items()} | |
| if rcParams['axes.formatter.use_locale']: | |
| locale.setlocale(locale.LC_ALL, '') | |
| def rc(group, **kwargs): | |
| """ | |
| Set the current `.rcParams`. *group* is the grouping for the rc, e.g., | |
| for ``lines.linewidth`` the group is ``lines``, for | |
| ``axes.facecolor``, the group is ``axes``, and so on. Group may | |
| also be a list or tuple of group names, e.g., (*xtick*, *ytick*). | |
| *kwargs* is a dictionary attribute name/value pairs, e.g.,:: | |
| rc('lines', linewidth=2, color='r') | |
| sets the current `.rcParams` and is equivalent to:: | |
| rcParams['lines.linewidth'] = 2 | |
| rcParams['lines.color'] = 'r' | |
| The following aliases are available to save typing for interactive users: | |
| ===== ================= | |
| Alias Property | |
| ===== ================= | |
| 'lw' 'linewidth' | |
| 'ls' 'linestyle' | |
| 'c' 'color' | |
| 'fc' 'facecolor' | |
| 'ec' 'edgecolor' | |
| 'mew' 'markeredgewidth' | |
| 'aa' 'antialiased' | |
| ===== ================= | |
| Thus you could abbreviate the above call as:: | |
| rc('lines', lw=2, c='r') | |
| Note you can use python's kwargs dictionary facility to store | |
| dictionaries of default parameters. e.g., you can customize the | |
| font rc as follows:: | |
| font = {'family' : 'monospace', | |
| 'weight' : 'bold', | |
| 'size' : 'larger'} | |
| rc('font', **font) # pass in the font dict as kwargs | |
| This enables you to easily switch between several configurations. Use | |
| ``matplotlib.style.use('default')`` or :func:`~matplotlib.rcdefaults` to | |
| restore the default `.rcParams` after changes. | |
| Notes | |
| ----- | |
| Similar functionality is available by using the normal dict interface, i.e. | |
| ``rcParams.update({"lines.linewidth": 2, ...})`` (but ``rcParams.update`` | |
| does not support abbreviations or grouping). | |
| """ | |
| aliases = { | |
| 'lw': 'linewidth', | |
| 'ls': 'linestyle', | |
| 'c': 'color', | |
| 'fc': 'facecolor', | |
| 'ec': 'edgecolor', | |
| 'mew': 'markeredgewidth', | |
| 'aa': 'antialiased', | |
| } | |
| if isinstance(group, str): | |
| group = (group,) | |
| for g in group: | |
| for k, v in kwargs.items(): | |
| name = aliases.get(k) or k | |
| key = f'{g}.{name}' | |
| try: | |
| rcParams[key] = v | |
| except KeyError as err: | |
| raise KeyError(('Unrecognized key "%s" for group "%s" and ' | |
| 'name "%s"') % (key, g, name)) from err | |
| def rcdefaults(): | |
| """ | |
| Restore the `.rcParams` from Matplotlib's internal default style. | |
| Style-blacklisted `.rcParams` (defined in | |
| ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. | |
| See Also | |
| -------- | |
| matplotlib.rc_file_defaults | |
| Restore the `.rcParams` from the rc file originally loaded by | |
| Matplotlib. | |
| matplotlib.style.use | |
| Use a specific style file. Call ``style.use('default')`` to restore | |
| the default style. | |
| """ | |
| # Deprecation warnings were already handled when creating rcParamsDefault, | |
| # no need to reemit them here. | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| from .style.core import STYLE_BLACKLIST | |
| rcParams.clear() | |
| rcParams.update({k: v for k, v in rcParamsDefault.items() | |
| if k not in STYLE_BLACKLIST}) | |
| def rc_file_defaults(): | |
| """ | |
| Restore the `.rcParams` from the original rc file loaded by Matplotlib. | |
| Style-blacklisted `.rcParams` (defined in | |
| ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. | |
| """ | |
| # Deprecation warnings were already handled when creating rcParamsOrig, no | |
| # need to reemit them here. | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| from .style.core import STYLE_BLACKLIST | |
| rcParams.update({k: rcParamsOrig[k] for k in rcParamsOrig | |
| if k not in STYLE_BLACKLIST}) | |
| def rc_file(fname, *, use_default_template=True): | |
| """ | |
| Update `.rcParams` from file. | |
| Style-blacklisted `.rcParams` (defined in | |
| ``matplotlib.style.core.STYLE_BLACKLIST``) are not updated. | |
| Parameters | |
| ---------- | |
| fname : str or path-like | |
| A file with Matplotlib rc settings. | |
| use_default_template : bool | |
| If True, initialize with default parameters before updating with those | |
| in the given file. If False, the current configuration persists | |
| and only the parameters specified in the file are updated. | |
| """ | |
| # Deprecation warnings were already handled in rc_params_from_file, no need | |
| # to reemit them here. | |
| with _api.suppress_matplotlib_deprecation_warning(): | |
| from .style.core import STYLE_BLACKLIST | |
| rc_from_file = rc_params_from_file( | |
| fname, use_default_template=use_default_template) | |
| rcParams.update({k: rc_from_file[k] for k in rc_from_file | |
| if k not in STYLE_BLACKLIST}) | |
| def rc_context(rc=None, fname=None): | |
| """ | |
| Return a context manager for temporarily changing rcParams. | |
| The :rc:`backend` will not be reset by the context manager. | |
| rcParams changed both through the context manager invocation and | |
| in the body of the context will be reset on context exit. | |
| Parameters | |
| ---------- | |
| rc : dict | |
| The rcParams to temporarily set. | |
| fname : str or path-like | |
| A file with Matplotlib rc settings. If both *fname* and *rc* are given, | |
| settings from *rc* take precedence. | |
| See Also | |
| -------- | |
| :ref:`customizing-with-matplotlibrc-files` | |
| Examples | |
| -------- | |
| Passing explicit values via a dict:: | |
| with mpl.rc_context({'interactive': False}): | |
| fig, ax = plt.subplots() | |
| ax.plot(range(3), range(3)) | |
| fig.savefig('example.png') | |
| plt.close(fig) | |
| Loading settings from a file:: | |
| with mpl.rc_context(fname='print.rc'): | |
| plt.plot(x, y) # uses 'print.rc' | |
| Setting in the context body:: | |
| with mpl.rc_context(): | |
| # will be reset | |
| mpl.rcParams['lines.linewidth'] = 5 | |
| plt.plot(x, y) | |
| """ | |
| orig = dict(rcParams.copy()) | |
| del orig['backend'] | |
| try: | |
| if fname: | |
| rc_file(fname) | |
| if rc: | |
| rcParams.update(rc) | |
| yield | |
| finally: | |
| rcParams._update_raw(orig) # Revert to the original rcs. | |
| def use(backend, *, force=True): | |
| """ | |
| Select the backend used for rendering and GUI integration. | |
| If pyplot is already imported, `~matplotlib.pyplot.switch_backend` is used | |
| and if the new backend is different than the current backend, all Figures | |
| will be closed. | |
| Parameters | |
| ---------- | |
| backend : str | |
| The backend to switch to. This can either be one of the standard | |
| backend names, which are case-insensitive: | |
| - interactive backends: | |
| GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, notebook, QtAgg, | |
| QtCairo, TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo | |
| - non-interactive backends: | |
| agg, cairo, pdf, pgf, ps, svg, template | |
| or a string of the form: ``module://my.module.name``. | |
| notebook is a synonym for nbAgg. | |
| Switching to an interactive backend is not possible if an unrelated | |
| event loop has already been started (e.g., switching to GTK3Agg if a | |
| TkAgg window has already been opened). Switching to a non-interactive | |
| backend is always possible. | |
| force : bool, default: True | |
| If True (the default), raise an `ImportError` if the backend cannot be | |
| set up (either because it fails to import, or because an incompatible | |
| GUI interactive framework is already running); if False, silently | |
| ignore the failure. | |
| See Also | |
| -------- | |
| :ref:`backends` | |
| matplotlib.get_backend | |
| matplotlib.pyplot.switch_backend | |
| """ | |
| name = rcsetup.validate_backend(backend) | |
| # don't (prematurely) resolve the "auto" backend setting | |
| if rcParams._get_backend_or_none() == name: | |
| # Nothing to do if the requested backend is already set | |
| pass | |
| else: | |
| # if pyplot is not already imported, do not import it. Doing | |
| # so may trigger a `plt.switch_backend` to the _default_ backend | |
| # before we get a chance to change to the one the user just requested | |
| plt = sys.modules.get('matplotlib.pyplot') | |
| # if pyplot is imported, then try to change backends | |
| if plt is not None: | |
| try: | |
| # we need this import check here to re-raise if the | |
| # user does not have the libraries to support their | |
| # chosen backend installed. | |
| plt.switch_backend(name) | |
| except ImportError: | |
| if force: | |
| raise | |
| # if we have not imported pyplot, then we can set the rcParam | |
| # value which will be respected when the user finally imports | |
| # pyplot | |
| else: | |
| rcParams['backend'] = backend | |
| # if the user has asked for a given backend, do not helpfully | |
| # fallback | |
| rcParams['backend_fallback'] = False | |
| if os.environ.get('MPLBACKEND'): | |
| rcParams['backend'] = os.environ.get('MPLBACKEND') | |
| def get_backend(*, auto_select=True): | |
| """ | |
| Return the name of the current backend. | |
| Parameters | |
| ---------- | |
| auto_select : bool, default: True | |
| Whether to trigger backend resolution if no backend has been | |
| selected so far. If True, this ensures that a valid backend | |
| is returned. If False, this returns None if no backend has been | |
| selected so far. | |
| .. versionadded:: 3.10 | |
| .. admonition:: Provisional | |
| The *auto_select* flag is provisional. It may be changed or removed | |
| without prior warning. | |
| See Also | |
| -------- | |
| matplotlib.use | |
| """ | |
| if auto_select: | |
| return rcParams['backend'] | |
| else: | |
| backend = rcParams._get('backend') | |
| if backend is rcsetup._auto_backend_sentinel: | |
| return None | |
| else: | |
| return backend | |
| def interactive(b): | |
| """ | |
| Set whether to redraw after every plotting command (e.g. `.pyplot.xlabel`). | |
| """ | |
| rcParams['interactive'] = b | |
| def is_interactive(): | |
| """ | |
| Return whether to redraw after every plotting command. | |
| .. note:: | |
| This function is only intended for use in backends. End users should | |
| use `.pyplot.isinteractive` instead. | |
| """ | |
| return rcParams['interactive'] | |
| def _val_or_rc(val, rc_name): | |
| """ | |
| If *val* is None, return ``mpl.rcParams[rc_name]``, otherwise return val. | |
| """ | |
| return val if val is not None else rcParams[rc_name] | |
| def _init_tests(): | |
| # The version of FreeType to install locally for running the tests. This must match | |
| # the value in `meson.build`. | |
| LOCAL_FREETYPE_VERSION = '2.6.1' | |
| from matplotlib import ft2font | |
| if (ft2font.__freetype_version__ != LOCAL_FREETYPE_VERSION or | |
| ft2font.__freetype_build_type__ != 'local'): | |
| _log.warning( | |
| "Matplotlib is not built with the correct FreeType version to run tests. " | |
| "Rebuild without setting system-freetype=true in Meson setup options. " | |
| "Expect many image comparison failures below. " | |
| "Expected freetype version %s. " | |
| "Found freetype version %s. " | |
| "Freetype build type is %slocal.", | |
| LOCAL_FREETYPE_VERSION, | |
| ft2font.__freetype_version__, | |
| "" if ft2font.__freetype_build_type__ == 'local' else "not ") | |
| def _replacer(data, value): | |
| """ | |
| Either returns ``data[value]`` or passes ``data`` back, converts either to | |
| a sequence. | |
| """ | |
| try: | |
| # if key isn't a string don't bother | |
| if isinstance(value, str): | |
| # try to use __getitem__ | |
| value = data[value] | |
| except Exception: | |
| # key does not exist, silently fall back to key | |
| pass | |
| return cbook.sanitize_sequence(value) | |
| def _label_from_arg(y, default_name): | |
| try: | |
| return y.name | |
| except AttributeError: | |
| if isinstance(default_name, str): | |
| return default_name | |
| return None | |
| def _add_data_doc(docstring, replace_names): | |
| """ | |
| Add documentation for a *data* field to the given docstring. | |
| Parameters | |
| ---------- | |
| docstring : str | |
| The input docstring. | |
| replace_names : list of str or None | |
| The list of parameter names which arguments should be replaced by | |
| ``data[name]`` (if ``data[name]`` does not throw an exception). If | |
| None, replacement is attempted for all arguments. | |
| Returns | |
| ------- | |
| str | |
| The augmented docstring. | |
| """ | |
| if (docstring is None | |
| or replace_names is not None and len(replace_names) == 0): | |
| return docstring | |
| docstring = inspect.cleandoc(docstring) | |
| data_doc = ("""\ | |
| If given, all parameters also accept a string ``s``, which is | |
| interpreted as ``data[s]`` if ``s`` is a key in ``data``.""" | |
| if replace_names is None else f"""\ | |
| If given, the following parameters also accept a string ``s``, which is | |
| interpreted as ``data[s]`` if ``s`` is a key in ``data``: | |
| {', '.join(map('*{}*'.format, replace_names))}""") | |
| # using string replacement instead of formatting has the advantages | |
| # 1) simpler indent handling | |
| # 2) prevent problems with formatting characters '{', '%' in the docstring | |
| if _log.level <= logging.DEBUG: | |
| # test_data_parameter_replacement() tests against these log messages | |
| # make sure to keep message and test in sync | |
| if "data : indexable object, optional" not in docstring: | |
| _log.debug("data parameter docstring error: no data parameter") | |
| if 'DATA_PARAMETER_PLACEHOLDER' not in docstring: | |
| _log.debug("data parameter docstring error: missing placeholder") | |
| return docstring.replace(' DATA_PARAMETER_PLACEHOLDER', data_doc) | |
| def _preprocess_data(func=None, *, replace_names=None, label_namer=None): | |
| """ | |
| A decorator to add a 'data' kwarg to a function. | |
| When applied:: | |
| @_preprocess_data() | |
| def func(ax, *args, **kwargs): ... | |
| the signature is modified to ``decorated(ax, *args, data=None, **kwargs)`` | |
| with the following behavior: | |
| - if called with ``data=None``, forward the other arguments to ``func``; | |
| - otherwise, *data* must be a mapping; for any argument passed in as a | |
| string ``name``, replace the argument by ``data[name]`` (if this does not | |
| throw an exception), then forward the arguments to ``func``. | |
| In either case, any argument that is a `MappingView` is also converted to a | |
| list. | |
| Parameters | |
| ---------- | |
| replace_names : list of str or None, default: None | |
| The list of parameter names for which lookup into *data* should be | |
| attempted. If None, replacement is attempted for all arguments. | |
| label_namer : str, default: None | |
| If set e.g. to "namer" (which must be a kwarg in the function's | |
| signature -- not as ``**kwargs``), if the *namer* argument passed in is | |
| a (string) key of *data* and no *label* kwarg is passed, then use the | |
| (string) value of the *namer* as *label*. :: | |
| @_preprocess_data(label_namer="foo") | |
| def func(foo, label=None): ... | |
| func("key", data={"key": value}) | |
| # is equivalent to | |
| func.__wrapped__(value, label="key") | |
| """ | |
| if func is None: # Return the actual decorator. | |
| return functools.partial( | |
| _preprocess_data, | |
| replace_names=replace_names, label_namer=label_namer) | |
| sig = inspect.signature(func) | |
| varargs_name = None | |
| varkwargs_name = None | |
| arg_names = [] | |
| params = list(sig.parameters.values()) | |
| for p in params: | |
| if p.kind is Parameter.VAR_POSITIONAL: | |
| varargs_name = p.name | |
| elif p.kind is Parameter.VAR_KEYWORD: | |
| varkwargs_name = p.name | |
| else: | |
| arg_names.append(p.name) | |
| data_param = Parameter("data", Parameter.KEYWORD_ONLY, default=None) | |
| if varkwargs_name: | |
| params.insert(-1, data_param) | |
| else: | |
| params.append(data_param) | |
| new_sig = sig.replace(parameters=params) | |
| arg_names = arg_names[1:] # remove the first "ax" / self arg | |
| assert {*arg_names}.issuperset(replace_names or []) or varkwargs_name, ( | |
| "Matplotlib internal error: invalid replace_names " | |
| f"({replace_names!r}) for {func.__name__!r}") | |
| assert label_namer is None or label_namer in arg_names, ( | |
| "Matplotlib internal error: invalid label_namer " | |
| f"({label_namer!r}) for {func.__name__!r}") | |
| def inner(ax, *args, data=None, **kwargs): | |
| if data is None: | |
| return func( | |
| ax, | |
| *map(cbook.sanitize_sequence, args), | |
| **{k: cbook.sanitize_sequence(v) for k, v in kwargs.items()}) | |
| bound = new_sig.bind(ax, *args, **kwargs) | |
| auto_label = (bound.arguments.get(label_namer) | |
| or bound.kwargs.get(label_namer)) | |
| for k, v in bound.arguments.items(): | |
| if k == varkwargs_name: | |
| for k1, v1 in v.items(): | |
| if replace_names is None or k1 in replace_names: | |
| v[k1] = _replacer(data, v1) | |
| elif k == varargs_name: | |
| if replace_names is None: | |
| bound.arguments[k] = tuple(_replacer(data, v1) for v1 in v) | |
| else: | |
| if replace_names is None or k in replace_names: | |
| bound.arguments[k] = _replacer(data, v) | |
| new_args = bound.args | |
| new_kwargs = bound.kwargs | |
| args_and_kwargs = {**bound.arguments, **bound.kwargs} | |
| if label_namer and "label" not in args_and_kwargs: | |
| new_kwargs["label"] = _label_from_arg( | |
| args_and_kwargs.get(label_namer), auto_label) | |
| return func(*new_args, **new_kwargs) | |
| inner.__doc__ = _add_data_doc(inner.__doc__, replace_names) | |
| inner.__signature__ = new_sig | |
| return inner | |
| _log.debug('interactive is %s', is_interactive()) | |
| _log.debug('platform is %s', sys.platform) | |
| def sanitize_sequence(data): | |
| return cbook.sanitize_sequence(data) | |
| def validate_backend(s): | |
| return rcsetup.validate_backend(s) | |
| # workaround: we must defer colormaps import to after loading rcParams, because | |
| # colormap creation depends on rcParams | |
| from matplotlib.cm import _colormaps as colormaps # noqa: E402 | |
| from matplotlib.cm import _multivar_colormaps as multivar_colormaps # noqa: E402 | |
| from matplotlib.cm import _bivar_colormaps as bivar_colormaps # noqa: E402 | |
| from matplotlib.colors import _color_sequences as color_sequences # noqa: E402 | |