From 3399dc4b1ea51a14fda31bdc315528f775192d58 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Sat, 20 Oct 2018 23:41:01 +0200 Subject: [PATCH] RcParams should not inherit from dict --- lib/matplotlib/__init__.py | 52 +++++++++++++++++----------- lib/matplotlib/backends/qt_compat.py | 20 +++++------ lib/matplotlib/cbook/__init__.py | 2 +- lib/matplotlib/pyplot.py | 4 +-- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/lib/matplotlib/__init__.py b/lib/matplotlib/__init__.py index f504673776af..bb4f926a26d8 100644 --- a/lib/matplotlib/__init__.py +++ b/lib/matplotlib/__init__.py @@ -117,6 +117,7 @@ import atexit from collections.abc import MutableMapping import contextlib +import copy import distutils.version import functools import importlib @@ -759,10 +760,9 @@ def gen_candidates(): _all_deprecated = {*_deprecated_map, *_deprecated_ignore_map} -class RcParams(MutableMapping, dict): - +class RcParams(MutableMapping): """ - A dictionary object including validation + A dictionary object including validation. validating functions are defined and associated with rc parameters in :mod:`matplotlib.rcsetup` @@ -803,6 +803,7 @@ def msg_backend_obsolete(self): # validate values on the way in def __init__(self, *args, **kwargs): + self._data = {} self.update(*args, **kwargs) def __setitem__(self, key, val): @@ -840,7 +841,7 @@ def __setitem__(self, key, val): cval = self.validate[key](val) except ValueError as ve: raise ValueError("Key %s: %s" % (key, str(ve))) - dict.__setitem__(self, key, cval) + self._data[key] = cval except KeyError: raise KeyError( '%s is not a valid rc parameter. See rcParams.keys() for a ' @@ -851,13 +852,13 @@ def __getitem__(self, key): version, alt_key, alt_val, inverse_alt = _deprecated_map[key] cbook.warn_deprecated( version, key, obj_type="rcparam", alternative=alt_key) - return inverse_alt(dict.__getitem__(self, alt_key)) + return inverse_alt(self._data[alt_key]) elif key in _deprecated_ignore_map: version, alt_key = _deprecated_ignore_map[key] cbook.warn_deprecated( version, key, obj_type="rcparam", alternative=alt_key) - return dict.__getitem__(self, alt_key) if alt_key else None + return self._data[alt_key] if alt_key else None elif key == 'examples.directory': cbook.warn_deprecated( @@ -865,27 +866,37 @@ def __getitem__(self, key): "found relative to the 'datapath' directory.".format(key)) elif key == "backend": - val = dict.__getitem__(self, key) + val = self._data[key] if val is rcsetup._auto_backend_sentinel: from matplotlib import pyplot as plt plt.switch_backend(rcsetup._auto_backend_sentinel) - return dict.__getitem__(self, key) + return self._data[key] def __repr__(self): class_name = self.__class__.__name__ indent = len(class_name) + 1 - repr_split = pprint.pformat(dict(self), indent=1, + repr_split = pprint.pformat(self._data, indent=1, width=80 - indent).split('\n') repr_indented = ('\n' + ' ' * indent).join(repr_split) return '{}({})'.format(class_name, repr_indented) def __str__(self): - return '\n'.join(map('{0[0]}: {0[1]}'.format, sorted(self.items()))) + return '\n'.join(map('{0[0]}: {0[1]}'.format, + sorted(self._data.items()))) def __iter__(self): """Yield sorted list of keys.""" - yield from sorted(dict.__iter__(self)) + yield from sorted(self._data) + + def __len__(self): + return len(self._data) + + def __delitem__(self, key): + del self._data[key] + + def copy(self): + return copy.deepcopy(self) def find_all(self, pattern): """ @@ -900,7 +911,7 @@ def find_all(self, pattern): """ pattern_re = re.compile(pattern) return RcParams((key, value) - for key, value in self.items() + for key, value in self._data.items() if pattern_re.search(key)) @@ -1060,24 +1071,23 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True): rcParams = rc_params() # Don't trigger deprecation warning when just fetching. -if dict.__getitem__(rcParams, 'examples.directory'): +with warnings.catch_warnings(): + warnings.simplefilter("ignore", MatplotlibDeprecationWarning) + examples_directory = rcParams['examples.directory'] # paths that are intended to be relative to matplotlib_fname() # are allowed for the examples.directory parameter. # However, we will need to fully qualify the path because # Sphinx requires absolute paths. - if not os.path.isabs(rcParams['examples.directory']): + if examples_directory and not os.path.isabs(examples_directory): _basedir, _fname = os.path.split(matplotlib_fname()) # Sometimes matplotlib_fname() can return relative paths, # Also, using realpath() guarantees that Sphinx will use # the same path that matplotlib sees (in case of weird symlinks). _basedir = os.path.realpath(_basedir) - _fullpath = os.path.join(_basedir, rcParams['examples.directory']) + _fullpath = os.path.join(_basedir, examples_directory) rcParams['examples.directory'] = _fullpath - -with warnings.catch_warnings(): - warnings.simplefilter("ignore", MatplotlibDeprecationWarning) - rcParamsOrig = RcParams(rcParams.copy()) + rcParamsOrig = RcParams(rcParams._data.copy()) rcParamsDefault = RcParams([(key, default) for key, (default, converter) in defaultParams.items() if key not in _all_deprecated]) @@ -1275,7 +1285,7 @@ def __init__(self, rc=None, fname=None): def __fallback(self): # If anything goes wrong, revert to the original rcs. updated_backend = self._orig['backend'] - dict.update(rcParams, self._orig) + rcParams._data.update(self._orig._data) # except for the backend. If the context block triggered resolving # the auto backend resolution keep that value around if self._orig['backend'] is rcsetup._auto_backend_sentinel: @@ -1328,7 +1338,7 @@ def use(arg, warn=True, force=False): name = validate_backend(arg) # if setting back to the same thing, do nothing - if (dict.__getitem__(rcParams, 'backend') == name): + if (rcParams._data['backend'] == name): pass # Check if we have already imported pyplot and triggered diff --git a/lib/matplotlib/backends/qt_compat.py b/lib/matplotlib/backends/qt_compat.py index b0fa0a907371..8e4993497d29 100644 --- a/lib/matplotlib/backends/qt_compat.py +++ b/lib/matplotlib/backends/qt_compat.py @@ -33,31 +33,31 @@ # First, check if anything is already imported. if "PyQt5" in sys.modules: QT_API = QT_API_PYQT5 - dict.__setitem__(rcParams, "backend.qt5", QT_API) + rcParams._data["backend.qt5"] = QT_API elif "PySide2" in sys.modules: QT_API = QT_API_PYSIDE2 - dict.__setitem__(rcParams, "backend.qt5", QT_API) + rcParams._data["backend.qt5"] = QT_API elif "PyQt4" in sys.modules: QT_API = QT_API_PYQTv2 - dict.__setitem__(rcParams, "backend.qt4", QT_API) + rcParams._data["backend.qt4"] = QT_API elif "PySide" in sys.modules: QT_API = QT_API_PYSIDE - dict.__setitem__(rcParams, "backend.qt4", QT_API) + rcParams._data["backend.qt4"] = QT_API # Otherwise, check the QT_API environment variable (from Enthought). This can # only override the binding, not the backend (in other words, we check that the # requested backend actually matches). elif rcParams["backend"] in ["Qt5Agg", "Qt5Cairo"]: if QT_API_ENV == "pyqt5": - dict.__setitem__(rcParams, "backend.qt5", QT_API_PYQT5) + rcParams._data["backend.qt5"] = QT_API_PYQT5 elif QT_API_ENV == "pyside2": - dict.__setitem__(rcParams, "backend.qt5", QT_API_PYSIDE2) - QT_API = dict.__getitem__(rcParams, "backend.qt5") + rcParams._data["backend.qt5"] = QT_API_PYSIDE2 + QT_API = rcParams._data["backend.qt5"] elif rcParams["backend"] in ["Qt4Agg", "Qt4Cairo"]: if QT_API_ENV == "pyqt4": - dict.__setitem__(rcParams, "backend.qt4", QT_API_PYQTv2) + rcParams._data["backend.qt4"] = QT_API_PYQTv2 elif QT_API_ENV == "pyside": - dict.__setitem__(rcParams, "backend.qt4", QT_API_PYSIDE) - QT_API = dict.__getitem__(rcParams, "backend.qt4") + rcParams._data["backend.qt4"] = QT_API_PYSIDE + QT_API = rcParams._data["backend.qt4"] # A non-Qt backend was selected but we still got there (possible, e.g., when # fully manually embedding Matplotlib in a Qt app without using pyplot). else: diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 473c2324a7a7..874735f4efd4 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -445,7 +445,7 @@ def get_sample_data(fname, asfileobj=True): If the filename ends in .gz, the file is implicitly ungzipped. """ # Don't trigger deprecation warning when just fetching. - if dict.__getitem__(matplotlib.rcParams, 'examples.directory'): + if dict._data['examples.directory']: root = matplotlib.rcParams['examples.directory'] else: root = os.path.join(matplotlib._get_data_path(), 'sample_data') diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 6ceced49285c..a4323bfcd93f 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2343,9 +2343,9 @@ def _autogen_docstring(base): # requested, ignore rcParams['backend'] and force selection of a backend that # is compatible with the current running interactive framework. if (rcParams["backend_fallback"] - and dict.__getitem__(rcParams, "backend") in _interactive_bk + and rcParams._data["backend"] in _interactive_bk and _get_running_interactive_framework()): - dict.__setitem__(rcParams, "backend", rcsetup._auto_backend_sentinel) + rcParams._data["backend"] = rcsetup._auto_backend_sentinel # Set up the backend. switch_backend(rcParams["backend"])
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: