Skip to content

Commit 3813a63

Browse files
authored
Merge pull request matplotlib#27719 from ianthomas23/backend_registry_enum
Add BackendRegistry singleton class
2 parents 4ebc8ce + 64aa6d7 commit 3813a63

File tree

13 files changed

+241
-54
lines changed

13 files changed

+241
-54
lines changed

doc/api/backend_registry_api.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
********************************
2+
``matplotlib.backends.registry``
3+
********************************
4+
5+
.. automodule:: matplotlib.backends.registry
6+
:members:
7+
:undoc-members:
8+
:show-inheritance:

doc/api/index_backend_api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
backend_pdf_api.rst
1818
backend_pgf_api.rst
1919
backend_ps_api.rst
20+
backend_registry_api.rst
2021
backend_qt_api.rst
2122
backend_svg_api.rst
2223
backend_tk_api.rst
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
``rcsetup.interactive_bk``, ``rcsetup.non_interactive_bk`` and ``rcsetup.all_backends``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
... are deprecated and replaced by ``matplotlib.backends.backend_registry.list_builtin``
5+
with the following arguments
6+
7+
- ``matplotlib.backends.BackendFilter.INTERACTIVE``
8+
- ``matplotlib.backends.BackendFilter.NON_INTERACTIVE``
9+
- ``None``
10+
11+
respectively.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
BackendRegistry
2+
~~~~~~~~~~~~~~~
3+
4+
New :class:`~matplotlib.backends.registry.BackendRegistry` class is the single
5+
source of truth for available backends. The singleton instance is
6+
``matplotlib.backends.backend_registry``.

lib/matplotlib/backend_bases.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -93,32 +93,6 @@
9393
}
9494

9595

96-
def _safe_pyplot_import():
97-
"""
98-
Import and return ``pyplot``, correctly setting the backend if one is
99-
already forced.
100-
"""
101-
try:
102-
import matplotlib.pyplot as plt
103-
except ImportError: # Likely due to a framework mismatch.
104-
current_framework = cbook._get_running_interactive_framework()
105-
if current_framework is None:
106-
raise # No, something else went wrong, likely with the install...
107-
backend_mapping = {
108-
'qt': 'qtagg',
109-
'gtk3': 'gtk3agg',
110-
'gtk4': 'gtk4agg',
111-
'wx': 'wxagg',
112-
'tk': 'tkagg',
113-
'macosx': 'macosx',
114-
'headless': 'agg',
115-
}
116-
backend = backend_mapping[current_framework]
117-
rcParams["backend"] = mpl.rcParamsOrig["backend"] = backend
118-
import matplotlib.pyplot as plt # Now this should succeed.
119-
return plt
120-
121-
12296
def register_backend(format, backend, description=None):
12397
"""
12498
Register a backend for saving to a given file format.

lib/matplotlib/backends/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from .registry import BackendFilter, backend_registry # noqa: F401
2+
13
# NOTE: plt.switch_backend() (called at import time) will add a "backend"
24
# attribute here for backcompat.
35
_QT_FORCE_QT5_BINDING = False

lib/matplotlib/backends/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ python_sources = [
3333
'backend_wxagg.py',
3434
'backend_wxcairo.py',
3535
'qt_compat.py',
36+
'registry.py',
3637
]
3738

3839
typing_sources = [

lib/matplotlib/backends/registry.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from enum import Enum
2+
3+
4+
class BackendFilter(Enum):
5+
"""
6+
Filter used with :meth:`~matplotlib.backends.registry.BackendRegistry.list_builtin`
7+
8+
.. versionadded:: 3.9
9+
"""
10+
INTERACTIVE = 0
11+
NON_INTERACTIVE = 1
12+
13+
14+
class BackendRegistry:
15+
"""
16+
Registry of backends available within Matplotlib.
17+
18+
This is the single source of truth for available backends.
19+
20+
All use of ``BackendRegistry`` should be via the singleton instance
21+
``backend_registry`` which can be imported from ``matplotlib.backends``.
22+
23+
.. versionadded:: 3.9
24+
"""
25+
# Built-in backends are those which are included in the Matplotlib repo.
26+
# A backend with name 'name' is located in the module
27+
# f'matplotlib.backends.backend_{name.lower()}'
28+
29+
# The capitalized forms are needed for ipython at present; this may
30+
# change for later versions.
31+
_BUILTIN_INTERACTIVE = [
32+
"GTK3Agg", "GTK3Cairo", "GTK4Agg", "GTK4Cairo",
33+
"MacOSX",
34+
"nbAgg",
35+
"QtAgg", "QtCairo", "Qt5Agg", "Qt5Cairo",
36+
"TkAgg", "TkCairo",
37+
"WebAgg",
38+
"WX", "WXAgg", "WXCairo",
39+
]
40+
_BUILTIN_NOT_INTERACTIVE = [
41+
"agg", "cairo", "pdf", "pgf", "ps", "svg", "template",
42+
]
43+
_GUI_FRAMEWORK_TO_BACKEND_MAPPING = {
44+
"qt": "qtagg",
45+
"gtk3": "gtk3agg",
46+
"gtk4": "gtk4agg",
47+
"wx": "wxagg",
48+
"tk": "tkagg",
49+
"macosx": "macosx",
50+
"headless": "agg",
51+
}
52+
53+
def backend_for_gui_framework(self, framework):
54+
"""
55+
Return the name of the backend corresponding to the specified GUI framework.
56+
57+
Parameters
58+
----------
59+
framework : str
60+
GUI framework such as "qt".
61+
62+
Returns
63+
-------
64+
str
65+
Backend name.
66+
"""
67+
return self._GUI_FRAMEWORK_TO_BACKEND_MAPPING.get(framework)
68+
69+
def list_builtin(self, filter_=None):
70+
"""
71+
Return list of backends that are built into Matplotlib.
72+
73+
Parameters
74+
----------
75+
filter_ : `~.BackendFilter`, optional
76+
Filter to apply to returned backends. For example, to return only
77+
non-interactive backends use `.BackendFilter.NON_INTERACTIVE`.
78+
79+
Returns
80+
-------
81+
list of str
82+
Backend names.
83+
"""
84+
if filter_ == BackendFilter.INTERACTIVE:
85+
return self._BUILTIN_INTERACTIVE
86+
elif filter_ == BackendFilter.NON_INTERACTIVE:
87+
return self._BUILTIN_NOT_INTERACTIVE
88+
89+
return self._BUILTIN_INTERACTIVE + self._BUILTIN_NOT_INTERACTIVE
90+
91+
92+
# Singleton
93+
backend_registry = BackendRegistry()

lib/matplotlib/backends/registry.pyi

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from enum import Enum
2+
3+
4+
class BackendFilter(Enum):
5+
INTERACTIVE: int
6+
NON_INTERACTIVE: int
7+
8+
9+
class BackendRegistry:
10+
def backend_for_gui_framework(self, interactive_framework: str) -> str | None: ...
11+
def list_builtin(self, filter_: BackendFilter | None) -> list[str]: ...
12+
13+
14+
backend_registry: BackendRegistry

lib/matplotlib/pyplot.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
from matplotlib.artist import Artist
7070
from matplotlib.axes import Axes
7171
from matplotlib.axes import Subplot # noqa: F401
72+
from matplotlib.backends import BackendFilter, backend_registry
7273
from matplotlib.projections import PolarAxes
7374
from matplotlib import mlab # for detrend_none, window_hanning
7475
from matplotlib.scale import get_scale_names # noqa: F401
@@ -301,16 +302,11 @@ def switch_backend(newbackend: str) -> None:
301302

302303
if newbackend is rcsetup._auto_backend_sentinel:
303304
current_framework = cbook._get_running_interactive_framework()
304-
mapping = {'qt': 'qtagg',
305-
'gtk3': 'gtk3agg',
306-
'gtk4': 'gtk4agg',
307-
'wx': 'wxagg',
308-
'tk': 'tkagg',
309-
'macosx': 'macosx',
310-
'headless': 'agg'}
311-
312-
if current_framework in mapping:
313-
candidates = [mapping[current_framework]]
305+
306+
if (current_framework and
307+
(backend := backend_registry.backend_for_gui_framework(
308+
current_framework))):
309+
candidates = [backend]
314310
else:
315311
candidates = []
316312
candidates += [
@@ -2510,7 +2506,8 @@ def polar(*args, **kwargs) -> list[Line2D]:
25102506
# is compatible with the current running interactive framework.
25112507
if (rcParams["backend_fallback"]
25122508
and rcParams._get_backend_or_none() in ( # type: ignore[attr-defined]
2513-
set(rcsetup.interactive_bk) - {'WebAgg', 'nbAgg'})
2509+
set(backend_registry.list_builtin(BackendFilter.INTERACTIVE)) -
2510+
{'WebAgg', 'nbAgg'})
25142511
and cbook._get_running_interactive_framework()):
25152512
rcParams._set("backend", rcsetup._auto_backend_sentinel)
25162513

0 commit comments

Comments
 (0)