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

Skip to content

Commit e0e1cad

Browse files
committed
Move Matplotlib backend resolution to Matplotlib
1 parent 86d864e commit e0e1cad

6 files changed

Lines changed: 77 additions & 36 deletions

File tree

IPython/core/interactiveshell.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3657,7 +3657,7 @@ def enable_matplotlib(self, gui=None):
36573657
from IPython.core import pylabtools as pt
36583658
gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select)
36593659

3660-
if gui != 'inline':
3660+
if gui != None:
36613661
# If we have our first gui selection, store it
36623662
if self.pylab_gui_select is None:
36633663
self.pylab_gui_select = gui

IPython/core/magics/pylab.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,18 @@
1818
from IPython.core.magic import Magics, magics_class, line_magic
1919
from IPython.testing.skipdoctest import skip_doctest
2020
from warnings import warn
21-
from IPython.core.pylabtools import backends
2221

2322
#-----------------------------------------------------------------------------
2423
# Magic implementation classes
2524
#-----------------------------------------------------------------------------
2625

2726
magic_gui_arg = magic_arguments.argument(
2827
'gui', nargs='?',
29-
help="""Name of the matplotlib backend to use %s.
28+
help="""Name of the matplotlib backend to use such as 'qt' or 'widget'.
3029
If given, the corresponding matplotlib backend is used,
3130
otherwise it will be matplotlib's default
3231
(which you can set in your matplotlib config file).
33-
""" % str(tuple(sorted(backends.keys())))
32+
"""
3433
)
3534

3635

@@ -93,7 +92,13 @@ def matplotlib(self, line=''):
9392
"""
9493
args = magic_arguments.parse_argstring(self.matplotlib, line)
9594
if args.list:
96-
backends_list = list(backends.keys())
95+
from IPython.core.pylabtools import _matplotlib_manages_backends
96+
if _matplotlib_manages_backends():
97+
from matplotlib.backends.registry import backend_registry
98+
backends_list = backend_registry.list_all()
99+
else:
100+
from IPython.core.pylabtools import backends
101+
backends_list = list(backends.keys())
97102
print("Available matplotlib backends: %s" % backends_list)
98103
else:
99104
gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui)

IPython/core/pylabtools.py

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
from IPython.core.display import _pngxy
1313
from IPython.utils.decorators import flag_calls
1414

15-
# If user specifies a GUI, that dictates the backend, otherwise we read the
16-
# user's mpl default from the mpl rc structure
17-
backends = {
15+
16+
# Matplotlib backend resolution functionality moved from IPython to Matplotlib
17+
# in IPython 8.23 and Matplotlib 3.9. Need to keep `backends` and `backend2gui`
18+
# here for earlier Matplotlib and for external backend libraries such as
19+
# mplcairo that might rely upon it.
20+
_deprecated_backends = {
1821
"tk": "TkAgg",
1922
"gtk": "GTKAgg",
2023
"gtk3": "GTK3Agg",
@@ -41,29 +44,38 @@
4144
# GUI support to activate based on the desired matplotlib backend. For the
4245
# most part it's just a reverse of the above dict, but we also need to add a
4346
# few others that map to the same GUI manually:
44-
backend2gui = dict(zip(backends.values(), backends.keys()))
47+
_deprecated_backend2gui = dict(zip(_deprecated_backends.values(), _deprecated_backends.keys()))
4548
# In the reverse mapping, there are a few extra valid matplotlib backends that
4649
# map to the same GUI support
47-
backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk"
48-
backend2gui["GTK3Cairo"] = "gtk3"
49-
backend2gui["GTK4Cairo"] = "gtk4"
50-
backend2gui["WX"] = "wx"
51-
backend2gui["CocoaAgg"] = "osx"
50+
_deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
51+
_deprecated_backend2gui["GTK3Cairo"] = "gtk3"
52+
_deprecated_backend2gui["GTK4Cairo"] = "gtk4"
53+
_deprecated_backend2gui["WX"] = "wx"
54+
_deprecated_backend2gui["CocoaAgg"] = "osx"
5255
# There needs to be a hysteresis here as the new QtAgg Matplotlib backend
5356
# supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
5457
# and Qt6.
55-
backend2gui["QtAgg"] = "qt"
56-
backend2gui["Qt4Agg"] = "qt4"
57-
backend2gui["Qt5Agg"] = "qt5"
58+
_deprecated_backend2gui["QtAgg"] = "qt"
59+
_deprecated_backend2gui["Qt4Agg"] = "qt4"
60+
_deprecated_backend2gui["Qt5Agg"] = "qt5"
5861

5962
# And some backends that don't need GUI integration
60-
del backend2gui["nbAgg"]
61-
del backend2gui["agg"]
62-
del backend2gui["svg"]
63-
del backend2gui["pdf"]
64-
del backend2gui["ps"]
65-
del backend2gui["module://matplotlib_inline.backend_inline"]
66-
del backend2gui["module://ipympl.backend_nbagg"]
63+
del _deprecated_backend2gui["nbAgg"]
64+
del _deprecated_backend2gui["agg"]
65+
del _deprecated_backend2gui["svg"]
66+
del _deprecated_backend2gui["pdf"]
67+
del _deprecated_backend2gui["ps"]
68+
del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
69+
del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
70+
71+
72+
# Deprecated attributes backends and backend2gui mostly following PEP 562.
73+
def __getattr__(name):
74+
if name in ("backends", "backend2gui"):
75+
warnings.warn(f"{name} is deprecated", DeprecationWarning)
76+
return globals()[f"_deprecated_{name}"]
77+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
78+
6779

6880
#-----------------------------------------------------------------------------
6981
# Matplotlib utilities
@@ -267,7 +279,7 @@ def select_figure_formats(shell, formats, **kwargs):
267279

268280
[ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
269281
mplbackend = matplotlib.get_backend().lower()
270-
if mplbackend == 'nbagg' or mplbackend == 'module://ipympl.backend_nbagg':
282+
if mplbackend in ('nbagg', 'ipympl', 'widget', 'module://ipympl.backend_nbagg'):
271283
formatter = shell.display_formatter.ipython_display_formatter
272284
formatter.for_type(Figure, _reshow_nbagg_figure)
273285

@@ -318,9 +330,23 @@ def find_gui_and_backend(gui=None, gui_select=None):
318330
"""
319331

320332
import matplotlib
333+
if _matplotlib_manages_backends():
334+
backend_registry = matplotlib.backends.registry.backend_registry
335+
336+
# gui argument may be a gui event loop or may be a backend name.
337+
if gui in ("auto", None):
338+
backend = matplotlib.rcParamsOrig['backend']
339+
backend, gui = backend_registry.resolve_backend(backend)
340+
else:
341+
backend, gui = backend_registry.resolve_gui_or_backend(gui)
321342

322-
has_unified_qt_backend = getattr(matplotlib, "__version_info__", (0, 0)) >= (3, 5)
343+
return gui, backend
323344

345+
# Fallback to previous behaviour (Matplotlib < 3.9)
346+
mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
347+
has_unified_qt_backend = mpl_version_info >= (3, 5)
348+
349+
from IPython.core.pylabtools import backends
324350
backends_ = dict(backends)
325351
if not has_unified_qt_backend:
326352
backends_["qt"] = "qt5agg"
@@ -338,6 +364,7 @@ def find_gui_and_backend(gui=None, gui_select=None):
338364
backend = matplotlib.rcParamsOrig['backend']
339365
# In this case, we need to find what the appropriate gui selection call
340366
# should be for IPython, so we can activate inputhook accordingly
367+
from IPython.core.pylabtools import backend2gui
341368
gui = backend2gui.get(backend, None)
342369

343370
# If we have already had a gui active, we need it and inline are the
@@ -346,6 +373,10 @@ def find_gui_and_backend(gui=None, gui_select=None):
346373
gui = gui_select
347374
backend = backends_[gui]
348375

376+
# Since IPython 8.23.0 use None for no gui event loop rather than "inline".
377+
if gui == "inline":
378+
gui = None
379+
349380
return gui, backend
350381

351382

@@ -431,3 +462,9 @@ def configure_inline_support(shell, backend):
431462
)
432463

433464
configure_inline_support_orig(shell, backend)
465+
466+
467+
def _matplotlib_manages_backends():
468+
import matplotlib
469+
mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
470+
return mpl_version_info >= (3, 9)

IPython/core/shellapp.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131

3232
gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))
3333

34-
backend_keys = sorted(pylabtools.backends.keys())
35-
backend_keys.insert(0, 'auto')
34+
backend_keys = []
3635

3736
shell_flags = {}
3837

IPython/core/tests/test_pylabtools.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,15 +199,15 @@ def test_qt(self):
199199
assert s.pylab_gui_select == "qt"
200200

201201
gui, backend = s.enable_matplotlib("inline")
202-
assert gui == "inline"
202+
assert gui is None
203203
assert s.pylab_gui_select == "qt"
204204

205205
gui, backend = s.enable_matplotlib("qt")
206206
assert gui == "qt"
207207
assert s.pylab_gui_select == "qt"
208208

209209
gui, backend = s.enable_matplotlib("inline")
210-
assert gui == "inline"
210+
assert gui is None
211211
assert s.pylab_gui_select == "qt"
212212

213213
gui, backend = s.enable_matplotlib()
@@ -217,11 +217,11 @@ def test_qt(self):
217217
def test_inline(self):
218218
s = self.Shell()
219219
gui, backend = s.enable_matplotlib("inline")
220-
assert gui == "inline"
220+
assert gui is None
221221
assert s.pylab_gui_select == None
222222

223223
gui, backend = s.enable_matplotlib("inline")
224-
assert gui == "inline"
224+
assert gui is None
225225
assert s.pylab_gui_select == None
226226

227227
gui, backend = s.enable_matplotlib("qt")
@@ -233,14 +233,14 @@ def test_inline_twice(self):
233233

234234
ip = self.Shell()
235235
gui, backend = ip.enable_matplotlib("inline")
236-
assert gui == "inline"
236+
assert gui is None
237237

238238
fmts = {'png'}
239239
active_mimes = {_fmt_mime_map[fmt] for fmt in fmts}
240240
pt.select_figure_formats(ip, fmts)
241241

242242
gui, backend = ip.enable_matplotlib("inline")
243-
assert gui == "inline"
243+
assert gui is None
244244

245245
for mime, f in ip.display_formatter.formatters.items():
246246
if mime in active_mimes:
@@ -254,7 +254,7 @@ def test_qt_gtk(self):
254254
assert gui == "qt"
255255
assert s.pylab_gui_select == "qt"
256256

257-
gui, backend = s.enable_matplotlib("gtk")
257+
gui, backend = s.enable_matplotlib("gtk3")
258258
assert gui == "qt"
259259
assert s.pylab_gui_select == "qt"
260260

IPython/terminal/interactiveshell.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -966,7 +966,7 @@ def enable_gui(self, gui: Optional[str] = None) -> None:
966966
if self._inputhook is not None and gui is None:
967967
self.active_eventloop = self._inputhook = None
968968

969-
if gui and (gui not in {"inline", "webagg"}):
969+
if gui and (gui not in {None, "webagg"}):
970970
# This hook runs with each cycle of the `prompt_toolkit`'s event loop.
971971
self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui)
972972
else:

0 commit comments

Comments
 (0)