Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/__init__.py: 47%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

506 statements  

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