aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/ipython/py3/IPython/core/pylabtools.py
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-05-11 14:26:05 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-05-11 14:35:40 +0300
commitebf1daa4c46f1c48865e6db613db68751770ccea (patch)
treed58d354f25c8106d5add16f6e2979d0e353e0318 /contrib/python/ipython/py3/IPython/core/pylabtools.py
parent8173b4515355158a5787dcb12aa6036874776101 (diff)
downloadydb-ebf1daa4c46f1c48865e6db613db68751770ccea.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/ipython/py3/IPython/core/pylabtools.py')
-rw-r--r--contrib/python/ipython/py3/IPython/core/pylabtools.py127
1 files changed, 106 insertions, 21 deletions
diff --git a/contrib/python/ipython/py3/IPython/core/pylabtools.py b/contrib/python/ipython/py3/IPython/core/pylabtools.py
index e5715a9497..1f5a11f37e 100644
--- a/contrib/python/ipython/py3/IPython/core/pylabtools.py
+++ b/contrib/python/ipython/py3/IPython/core/pylabtools.py
@@ -12,9 +12,12 @@ import warnings
from IPython.core.display import _pngxy
from IPython.utils.decorators import flag_calls
-# If user specifies a GUI, that dictates the backend, otherwise we read the
-# user's mpl default from the mpl rc structure
-backends = {
+
+# Matplotlib backend resolution functionality moved from IPython to Matplotlib
+# in IPython 8.24 and Matplotlib 3.9.1. Need to keep `backends` and `backend2gui`
+# here for earlier Matplotlib and for external backend libraries such as
+# mplcairo that might rely upon it.
+_deprecated_backends = {
"tk": "TkAgg",
"gtk": "GTKAgg",
"gtk3": "GTK3Agg",
@@ -41,29 +44,44 @@ backends = {
# GUI support to activate based on the desired matplotlib backend. For the
# most part it's just a reverse of the above dict, but we also need to add a
# few others that map to the same GUI manually:
-backend2gui = dict(zip(backends.values(), backends.keys()))
+_deprecated_backend2gui = dict(
+ zip(_deprecated_backends.values(), _deprecated_backends.keys())
+)
# In the reverse mapping, there are a few extra valid matplotlib backends that
# map to the same GUI support
-backend2gui["GTK"] = backend2gui["GTKCairo"] = "gtk"
-backend2gui["GTK3Cairo"] = "gtk3"
-backend2gui["GTK4Cairo"] = "gtk4"
-backend2gui["WX"] = "wx"
-backend2gui["CocoaAgg"] = "osx"
+_deprecated_backend2gui["GTK"] = _deprecated_backend2gui["GTKCairo"] = "gtk"
+_deprecated_backend2gui["GTK3Cairo"] = "gtk3"
+_deprecated_backend2gui["GTK4Cairo"] = "gtk4"
+_deprecated_backend2gui["WX"] = "wx"
+_deprecated_backend2gui["CocoaAgg"] = "osx"
# There needs to be a hysteresis here as the new QtAgg Matplotlib backend
# supports either Qt5 or Qt6 and the IPython qt event loop support Qt4, Qt5,
# and Qt6.
-backend2gui["QtAgg"] = "qt"
-backend2gui["Qt4Agg"] = "qt4"
-backend2gui["Qt5Agg"] = "qt5"
+_deprecated_backend2gui["QtAgg"] = "qt"
+_deprecated_backend2gui["Qt4Agg"] = "qt4"
+_deprecated_backend2gui["Qt5Agg"] = "qt5"
# And some backends that don't need GUI integration
-del backend2gui["nbAgg"]
-del backend2gui["agg"]
-del backend2gui["svg"]
-del backend2gui["pdf"]
-del backend2gui["ps"]
-del backend2gui["module://matplotlib_inline.backend_inline"]
-del backend2gui["module://ipympl.backend_nbagg"]
+del _deprecated_backend2gui["nbAgg"]
+del _deprecated_backend2gui["agg"]
+del _deprecated_backend2gui["svg"]
+del _deprecated_backend2gui["pdf"]
+del _deprecated_backend2gui["ps"]
+del _deprecated_backend2gui["module://matplotlib_inline.backend_inline"]
+del _deprecated_backend2gui["module://ipympl.backend_nbagg"]
+
+
+# Deprecated attributes backends and backend2gui mostly following PEP 562.
+def __getattr__(name):
+ if name in ("backends", "backend2gui"):
+ warnings.warn(
+ f"{name} is deprecated since IPython 8.24, backends are managed "
+ "in matplotlib and can be externally registered.",
+ DeprecationWarning,
+ )
+ return globals()[f"_deprecated_{name}"]
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
#-----------------------------------------------------------------------------
# Matplotlib utilities
@@ -267,7 +285,7 @@ def select_figure_formats(shell, formats, **kwargs):
[ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]
mplbackend = matplotlib.get_backend().lower()
- if mplbackend == 'nbagg' or mplbackend == 'module://ipympl.backend_nbagg':
+ if mplbackend in ("nbagg", "ipympl", "widget", "module://ipympl.backend_nbagg"):
formatter = shell.display_formatter.ipython_display_formatter
formatter.for_type(Figure, _reshow_nbagg_figure)
@@ -319,7 +337,23 @@ def find_gui_and_backend(gui=None, gui_select=None):
import matplotlib
- has_unified_qt_backend = getattr(matplotlib, "__version_info__", (0, 0)) >= (3, 5)
+ if _matplotlib_manages_backends():
+ backend_registry = matplotlib.backends.registry.backend_registry
+
+ # gui argument may be a gui event loop or may be a backend name.
+ if gui in ("auto", None):
+ backend = matplotlib.rcParamsOrig["backend"]
+ backend, gui = backend_registry.resolve_backend(backend)
+ else:
+ backend, gui = backend_registry.resolve_gui_or_backend(gui)
+
+ return gui, backend
+
+ # Fallback to previous behaviour (Matplotlib < 3.9)
+ mpl_version_info = getattr(matplotlib, "__version_info__", (0, 0))
+ has_unified_qt_backend = mpl_version_info >= (3, 5)
+
+ from IPython.core.pylabtools import backends
backends_ = dict(backends)
if not has_unified_qt_backend:
@@ -338,6 +372,7 @@ def find_gui_and_backend(gui=None, gui_select=None):
backend = matplotlib.rcParamsOrig['backend']
# In this case, we need to find what the appropriate gui selection call
# should be for IPython, so we can activate inputhook accordingly
+ from IPython.core.pylabtools import backend2gui
gui = backend2gui.get(backend, None)
# If we have already had a gui active, we need it and inline are the
@@ -346,6 +381,11 @@ def find_gui_and_backend(gui=None, gui_select=None):
gui = gui_select
backend = backends_[gui]
+ # Matplotlib before _matplotlib_manages_backends() can return "inline" for
+ # no gui event loop rather than the None that IPython >= 8.24.0 expects.
+ if gui == "inline":
+ gui = None
+
return gui, backend
@@ -431,3 +471,48 @@ def configure_inline_support(shell, backend):
)
configure_inline_support_orig(shell, backend)
+
+
+# Determine if Matplotlib manages backends only if needed, and cache result.
+# Do not read this directly, instead use _matplotlib_manages_backends().
+_matplotlib_manages_backends_value: bool | None = None
+
+
+def _matplotlib_manages_backends() -> bool:
+ """Return True if Matplotlib manages backends, False otherwise.
+
+ If it returns True, the caller can be sure that
+ matplotlib.backends.registry.backend_registry is available along with
+ member functions resolve_gui_or_backend, resolve_backend, list_all, and
+ list_gui_frameworks.
+ """
+ global _matplotlib_manages_backends_value
+ if _matplotlib_manages_backends_value is None:
+ try:
+ from matplotlib.backends.registry import backend_registry
+
+ _matplotlib_manages_backends_value = hasattr(
+ backend_registry, "resolve_gui_or_backend"
+ )
+ except ImportError:
+ _matplotlib_manages_backends_value = False
+
+ return _matplotlib_manages_backends_value
+
+
+def _list_matplotlib_backends_and_gui_loops() -> list[str]:
+ """Return list of all Matplotlib backends and GUI event loops.
+
+ This is the list returned by
+ %matplotlib --list
+ """
+ if _matplotlib_manages_backends():
+ from matplotlib.backends.registry import backend_registry
+
+ ret = backend_registry.list_all() + backend_registry.list_gui_frameworks()
+ else:
+ from IPython.core import pylabtools
+
+ ret = list(pylabtools.backends.keys())
+
+ return sorted(["auto"] + ret)