Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2ba8545

Browse files
authored
Merge pull request #27948 from ianthomas23/entry_points
Move IPython backend mapping to Matplotlib and support entry points
2 parents 378a77a + e458aa6 commit 2ba8545

24 files changed

+803
-98
lines changed

.github/workflows/tests.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ jobs:
5959
delete-font-cache: true
6060
- os: ubuntu-20.04
6161
python-version: 3.9
62-
extra-requirements: '-r requirements/testing/extra.txt'
62+
# One CI run tests ipython/matplotlib-inline before backend mapping moved to mpl
63+
extra-requirements: '-r requirements/testing/extra.txt "ipython<8.24" "matplotlib-inline<0.1.7"'
6364
CFLAGS: "-fno-lto" # Ensure that disabling LTO works.
6465
# https://github.com/matplotlib/matplotlib/pull/26052#issuecomment-1574595954
6566
# https://www.riverbankcomputing.com/pipermail/pyqt/2023-November/045606.html

doc/users/next_whats_new/backend_registry.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,15 @@ BackendRegistry
33

44
New :class:`~matplotlib.backends.registry.BackendRegistry` class is the single
55
source of truth for available backends. The singleton instance is
6-
``matplotlib.backends.backend_registry``.
6+
``matplotlib.backends.backend_registry``. It is used internally by Matplotlib,
7+
and also IPython (and therefore Jupyter) starting with IPython 8.24.0.
8+
9+
There are three sources of backends: built-in (source code is within the
10+
Matplotlib repository), explicit ``module://some.backend`` syntax (backend is
11+
obtained by loading the module), or via an entry point (self-registering
12+
backend in an external package).
13+
14+
To obtain a list of all registered backends use:
15+
16+
>>> from matplotlib.backends import backend_registry
17+
>>> backend_registry.list_all()

galleries/users_explain/figure/backends.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,8 @@ QtAgg Agg rendering in a Qt_ canvas (requires PyQt_ or `Qt for Python`_,
175175
more details.
176176
ipympl Agg rendering embedded in a Jupyter widget (requires ipympl_).
177177
This backend can be enabled in a Jupyter notebook with
178-
``%matplotlib ipympl``.
178+
``%matplotlib ipympl`` or ``%matplotlib widget``. Works with
179+
Jupyter ``lab`` and ``notebook>=7``.
179180
GTK3Agg Agg rendering to a GTK_ 3.x canvas (requires PyGObject_ and
180181
pycairo_). This backend can be activated in IPython with
181182
``%matplotlib gtk3``.
@@ -188,7 +189,8 @@ TkAgg Agg rendering to a Tk_ canvas (requires TkInter_). This
188189
backend can be activated in IPython with ``%matplotlib tk``.
189190
nbAgg Embed an interactive figure in a Jupyter classic notebook. This
190191
backend can be enabled in Jupyter notebooks via
191-
``%matplotlib notebook``.
192+
``%matplotlib notebook`` or ``%matplotlib nbagg``. Works with
193+
Jupyter ``notebook<7`` and ``nbclassic``.
192194
WebAgg On ``show()`` will start a tornado server with an interactive
193195
figure.
194196
GTK3Cairo Cairo rendering to a GTK_ 3.x canvas (requires PyGObject_ and
@@ -200,7 +202,7 @@ wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4).
200202
========= ================================================================
201203

202204
.. note::
203-
The names of builtin backends case-insensitive; e.g., 'QtAgg' and
205+
The names of builtin backends are case-insensitive; e.g., 'QtAgg' and
204206
'qtagg' are equivalent.
205207

206208
.. _`Anti-Grain Geometry`: http://agg.sourceforge.net/antigrain.com/
@@ -222,11 +224,13 @@ wxAgg Agg rendering to a wxWidgets_ canvas (requires wxPython_ 4).
222224
.. _wxWidgets: https://www.wxwidgets.org/
223225
.. _ipympl: https://www.matplotlib.org/ipympl
224226

227+
.. _ipympl_install:
228+
225229
ipympl
226230
^^^^^^
227231

228-
The Jupyter widget ecosystem is moving too fast to support directly in
229-
Matplotlib. To install ipympl:
232+
The ipympl backend is in a separate package that must be explicitly installed
233+
if you wish to use it, for example:
230234

231235
.. code-block:: bash
232236

galleries/users_explain/figure/figure_intro.rst

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,20 @@ Notebooks and IDEs
5252

5353
If you are using a Notebook (e.g. `Jupyter <https://jupyter.org>`_) or an IDE
5454
that renders Notebooks (PyCharm, VSCode, etc), then they have a backend that
55-
will render the Matplotlib Figure when a code cell is executed. One thing to
56-
be aware of is that the default Jupyter backend (``%matplotlib inline``) will
55+
will render the Matplotlib Figure when a code cell is executed. The default
56+
Jupyter backend (``%matplotlib inline``) creates static plots that
5757
by default trim or expand the figure size to have a tight box around Artists
58-
added to the Figure (see :ref:`saving_figures`, below). If you use a backend
59-
other than the default "inline" backend, you will likely need to use an ipython
60-
"magic" like ``%matplotlib notebook`` for the Matplotlib :ref:`notebook
61-
<jupyter_notebooks_jupyterlab>` or ``%matplotlib widget`` for the `ipympl
62-
<https://matplotlib.org/ipympl/>`_ backend.
58+
added to the Figure (see :ref:`saving_figures`, below). For interactive plots
59+
in Jupyter you will need to use an ipython "magic" like ``%matplotlib widget``
60+
for the `ipympl <https://matplotlib.org/ipympl/>`_ backend in ``jupyter lab``
61+
or ``notebook>=7``, or ``%matplotlib notebook`` for the Matplotlib
62+
:ref:`notebook <jupyter_notebooks_jupyterlab>` in ``notebook<7`` or
63+
``nbclassic``.
64+
65+
.. note::
66+
67+
The `ipympl <https://matplotlib.org/ipympl/>`_ backend is in a separate
68+
package, see :ref:`Installing ipympl <ipympl_install>`.
6369

6470
.. figure:: /_static/FigureNotebook.png
6571
:alt: Image of figure generated in Jupyter Notebook with notebook
@@ -75,15 +81,6 @@ other than the default "inline" backend, you will likely need to use an ipython
7581
.. seealso::
7682
:ref:`interactive_figures`.
7783

78-
.. note::
79-
80-
If you only need to use the classic notebook (i.e. ``notebook<7``),
81-
you can use:
82-
83-
.. sourcecode:: ipython
84-
85-
%matplotlib notebook
86-
8784
.. _standalone-scripts-and-interactive-use:
8885

8986
Standalone scripts and interactive use
@@ -104,7 +101,7 @@ backend. These are typically chosen either in the user's :ref:`matplotlibrc
104101
QtAgg backend.
105102

106103
When run from a script, or interactively (e.g. from an
107-
`iPython shell <https://ipython.readthedocs.io/en/stable/>`_) the Figure
104+
`IPython shell <https://ipython.readthedocs.io/en/stable/>`_) the Figure
108105
will not be shown until we call ``plt.show()``. The Figure will appear in
109106
a new GUI window, and usually will have a toolbar with Zoom, Pan, and other tools
110107
for interacting with the Figure. By default, ``plt.show()`` blocks

galleries/users_explain/figure/writing_a_backend_pyplot_interface.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,3 +84,47 @@ Function-based API
8484
2. **Showing figures**: `.pyplot.show()` calls a module-level ``show()``
8585
function, which is typically generated via the ``ShowBase`` class and its
8686
``mainloop`` method.
87+
88+
Registering a backend
89+
---------------------
90+
91+
For a new backend to be usable via ``matplotlib.use()`` or IPython
92+
``%matplotlib`` magic command, it must be compatible with one of the three ways
93+
supported by the :class:`~matplotlib.backends.registry.BackendRegistry`:
94+
95+
Built-in
96+
^^^^^^^^
97+
98+
A backend built into Matplotlib must have its name and
99+
``FigureCanvas.required_interactive_framework`` hard-coded in the
100+
:class:`~matplotlib.backends.registry.BackendRegistry`. If the backend module
101+
is not ``f"matplotlib.backends.backend_{backend_name.lower()}"`` then there
102+
must also be an entry in the ``BackendRegistry._name_to_module``.
103+
104+
module:// syntax
105+
^^^^^^^^^^^^^^^^
106+
107+
Any backend in a separate module (not built into Matplotlib) can be used by
108+
specifying the path to the module in the form ``module://some.backend.module``.
109+
An example is ``module://mplcairo.qt`` for
110+
`mplcairo <https:////github.com/matplotlib/mplcairo>`_. The backend's
111+
interactive framework will be taken from its
112+
``FigureCanvas.required_interactive_framework``.
113+
114+
Entry point
115+
^^^^^^^^^^^
116+
117+
An external backend module can self-register as a backend using an
118+
``entry point`` in its ``pyproject.toml`` such as the one used by
119+
``matplotlib-inline``:
120+
121+
.. code-block:: toml
122+
123+
[project.entry-points."matplotlib.backend"]
124+
inline = "matplotlib_inline.backend_inline"
125+
126+
The backend's interactive framework will be taken from its
127+
``FigureCanvas.required_interactive_framework``. All entry points are loaded
128+
together but only when first needed, such as when a backend name is not
129+
recognised as a built-in backend, or when
130+
:meth:`~matplotlib.backends.registry.BackendRegistry.list_all` is first called.

lib/matplotlib/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1208,14 +1208,16 @@ def use(backend, *, force=True):
12081208
backend names, which are case-insensitive:
12091209
12101210
- interactive backends:
1211-
GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, QtAgg,
1211+
GTK3Agg, GTK3Cairo, GTK4Agg, GTK4Cairo, MacOSX, nbAgg, notebook, QtAgg,
12121212
QtCairo, TkAgg, TkCairo, WebAgg, WX, WXAgg, WXCairo, Qt5Agg, Qt5Cairo
12131213
12141214
- non-interactive backends:
12151215
agg, cairo, pdf, pgf, ps, svg, template
12161216
12171217
or a string of the form: ``module://my.module.name``.
12181218
1219+
notebook is a synonym for nbAgg.
1220+
12191221
Switching to an interactive backend is not possible if an unrelated
12201222
event loop has already been started (e.g., switching to GTK3Agg if a
12211223
TkAgg window has already been opened). Switching to a non-interactive

lib/matplotlib/backend_bases.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,8 +1766,16 @@ def _fix_ipython_backend2gui(cls):
17661766
# `ipython --auto`). This cannot be done at import time due to
17671767
# ordering issues, so we do it when creating a canvas, and should only
17681768
# be done once per class (hence the `cache`).
1769-
if sys.modules.get("IPython") is None:
1769+
1770+
# This function will not be needed when Python 3.12, the latest version
1771+
# supported by IPython < 8.24, reaches end-of-life in late 2028.
1772+
# At that time this function can be made a no-op and deprecated.
1773+
mod_ipython = sys.modules.get("IPython")
1774+
if mod_ipython is None or mod_ipython.version_info[:2] >= (8, 24):
1775+
# Use of backend2gui is not needed for IPython >= 8.24 as the
1776+
# functionality has been moved to Matplotlib.
17701777
return
1778+
17711779
import IPython
17721780
ip = IPython.get_ipython()
17731781
if not ip:
@@ -2030,9 +2038,8 @@ def _switch_canvas_and_return_print_method(self, fmt, backend=None):
20302038
canvas = None
20312039
if backend is not None:
20322040
# Return a specific canvas class, if requested.
2033-
canvas_class = (
2034-
importlib.import_module(cbook._backend_module_name(backend))
2035-
.FigureCanvas)
2041+
from .backends.registry import backend_registry
2042+
canvas_class = backend_registry.load_backend_module(backend).FigureCanvas
20362043
if not hasattr(canvas_class, f"print_{fmt}"):
20372044
raise ValueError(
20382045
f"The {backend!r} backend does not support {fmt} output")

0 commit comments

Comments
 (0)