Skip to content

Commit 186f36d

Browse files
authored
Merge pull request #28730 from timhoffm/cleanup-rcParams-upate
MNT: Don't rely on RcParams being a dict subclass in internal code
2 parents 843aadd + 01ceeef commit 186f36d

File tree

3 files changed

+41
-16
lines changed

3 files changed

+41
-16
lines changed

lib/matplotlib/__init__.py

+35-13
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,35 @@ def _get(self, key):
712712
"""
713713
return dict.__getitem__(self, key)
714714

715+
def _update_raw(self, other_params):
716+
"""
717+
Directly update the data from *other_params*, bypassing deprecation,
718+
backend and validation logic on both sides.
719+
720+
This ``rcParams._update_raw(params)`` replaces the previous pattern
721+
``dict.update(rcParams, params)``.
722+
723+
Parameters
724+
----------
725+
other_params : dict or `.RcParams`
726+
The input mapping from which to update.
727+
"""
728+
if isinstance(other_params, RcParams):
729+
other_params = dict.items(other_params)
730+
dict.update(self, other_params)
731+
732+
def _ensure_has_backend(self):
733+
"""
734+
Ensure that a "backend" entry exists.
735+
736+
Normally, the default matplotlibrc file contains *no* entry for "backend" (the
737+
corresponding line starts with ##, not #; we fill in _auto_backend_sentinel
738+
in that case. However, packagers can set a different default backend
739+
(resulting in a normal `#backend: foo` line) in which case we should *not*
740+
fill in _auto_backend_sentinel.
741+
"""
742+
dict.setdefault(self, "backend", rcsetup._auto_backend_sentinel)
743+
715744
def __setitem__(self, key, val):
716745
try:
717746
if key in _deprecated_map:
@@ -961,24 +990,17 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
961990
return config
962991

963992

964-
# When constructing the global instances, we need to perform certain updates
965-
# by explicitly calling the superclass (dict.update, dict.items) to avoid
966-
# triggering resolution of _auto_backend_sentinel.
967993
rcParamsDefault = _rc_params_in_file(
968994
cbook._get_data_path("matplotlibrc"),
969995
# Strip leading comment.
970996
transform=lambda line: line[1:] if line.startswith("#") else line,
971997
fail_on_error=True)
972-
dict.update(rcParamsDefault, rcsetup._hardcoded_defaults)
973-
# Normally, the default matplotlibrc file contains *no* entry for backend (the
974-
# corresponding line starts with ##, not #; we fill on _auto_backend_sentinel
975-
# in that case. However, packagers can set a different default backend
976-
# (resulting in a normal `#backend: foo` line) in which case we should *not*
977-
# fill in _auto_backend_sentinel.
978-
dict.setdefault(rcParamsDefault, "backend", rcsetup._auto_backend_sentinel)
998+
rcParamsDefault._update_raw(rcsetup._hardcoded_defaults)
999+
rcParamsDefault._ensure_has_backend()
1000+
9791001
rcParams = RcParams() # The global instance.
980-
dict.update(rcParams, dict.items(rcParamsDefault))
981-
dict.update(rcParams, _rc_params_in_file(matplotlib_fname()))
1002+
rcParams._update_raw(rcParamsDefault)
1003+
rcParams._update_raw(_rc_params_in_file(matplotlib_fname()))
9821004
rcParamsOrig = rcParams.copy()
9831005
with _api.suppress_matplotlib_deprecation_warning():
9841006
# This also checks that all rcParams are indeed listed in the template.
@@ -1190,7 +1212,7 @@ def rc_context(rc=None, fname=None):
11901212
rcParams.update(rc)
11911213
yield
11921214
finally:
1193-
dict.update(rcParams, orig) # Revert to the original rcs.
1215+
rcParams._update_raw(orig) # Revert to the original rcs.
11941216

11951217

11961218
def use(backend, *, force=True):

lib/matplotlib/__init__.pyi

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ class RcParams(dict[str, Any]):
7070
def __init__(self, *args, **kwargs) -> None: ...
7171
def _set(self, key: str, val: Any) -> None: ...
7272
def _get(self, key: str) -> Any: ...
73+
74+
def _update_raw(self, other_params: dict | RcParams) -> None: ...
75+
76+
def _ensure_has_backend(self) -> None: ...
7377
def __setitem__(self, key: str, val: Any) -> None: ...
7478
def __getitem__(self, key: str) -> Any: ...
7579
def __iter__(self) -> Generator[str, None, None]: ...

lib/matplotlib/pyplot.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,7 @@ def switch_backend(newbackend: str) -> None:
410410
switch_backend("agg")
411411
rcParamsOrig["backend"] = "agg"
412412
return
413-
# have to escape the switch on access logic
414-
old_backend = dict.__getitem__(rcParams, 'backend')
413+
old_backend = rcParams._get('backend') # get without triggering backend resolution
415414

416415
module = backend_registry.load_backend_module(newbackend)
417416
canvas_class = module.FigureCanvas
@@ -841,7 +840,7 @@ def xkcd(
841840
"xkcd mode is not compatible with text.usetex = True")
842841

843842
stack = ExitStack()
844-
stack.callback(dict.update, rcParams, rcParams.copy()) # type: ignore[arg-type]
843+
stack.callback(rcParams._update_raw, rcParams.copy()) # type: ignore[arg-type]
845844

846845
from matplotlib import patheffects
847846
rcParams.update({

0 commit comments

Comments
 (0)