8000 MNT: Don't rely on RcParams being a dict subclass in internal code by timhoffm · Pull Request #28730 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

MNT: Don't rely on RcParams being a dict subclass in internal code #28730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions lib/matplotlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,35 @@ def _get(self, key):
"""
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:
Expand Down Expand Up @@ -961,24 +990,17 @@ def rc_params_from_file(fname, fail_on_error=False, use_default_template=True):
return config


# When constructing the global instances, we need to perform certain updates
# by explicitly calling the superclass (dict.update, dict.items) to avoid
# triggering resolution of _auto_backend_sentinel.
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)
dict.update(rcParamsDefault, rcsetup._hardcoded_defaults)
# Normally, the default matplotlibrc file contains *no* entry for backend (the
# corresponding line starts with ##, not #; we fill on _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(rcParamsDefault, "backend", rcsetup._auto_backend_sentinel)
rcParamsDefault._update_raw(rcsetup._hardcoded_defaults)
rcParamsDefault._ensure_has_backend()

rcParams = RcParams() # The global instance.
dict.update(rcParams, dict.items(rcParamsDefault))
dict.update(rcParams, _rc_params_in_file(matplotlib_fname()))
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.
Expand Down Expand Up @@ -1190,7 +1212,7 @@ def rc_context(rc=None, fname=None):
rcParams.update(rc)
yield
finally:
dict.update(rcParams, orig) # Revert to the original rcs.
rcParams._update_raw(orig) # Revert to the original rcs.


def use(backend, *, force=True):
Expand Down
4 changes: 4 additions & 0 deletions lib/matplotlib/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class RcParams(dict[str, Any]):
def __init__(self, *args, **kwargs) -> None: ...
def _set(self, key: str, val: Any) -> None: ...
def _get(self, key: str) -> Any: ...

def _update_raw(self, other_params: dict | RcParams) -> None: ...

def _ensure_has_backend(self) -> None: ...
def __setitem__(self, key: str, val: Any) -> None: ...
def __getitem__(self, key: str) -> Any: ...
def __iter__(self) -> Generator[str, None, None]: ...
Expand Down
5 changes: 2 additions & 3 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,7 @@ def switch_backend(newbackend: str) -> None:
switch_backend("agg")
rcParamsOrig["backend"] = "agg"
return
# have to escape the switch on access logic
old_backend = dict.__getitem__(rcParams, 'backend')
old_backend = rcParams._get('backend') # get without triggering backend resolution

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

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

from matplotlib import patheffects
rcParams.update({
Expand Down
Loading
0