Skip to content

Derive new_figure_manager from FigureCanvas.new_manager. #23090

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
May 22, 2022
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
42 changes: 33 additions & 9 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,15 +266,9 @@ def switch_backend(newbackend):
rcParamsOrig["backend"] = "agg"
return

# Backends are implemented as modules, but "inherit" default method
# implementations from backend_bases._Backend. This is achieved by
# creating a "class" that inherits from backend_bases._Backend and whose
# body is filled with the module's globals.

backend_name = cbook._backend_module_name(newbackend)

class backend_mod(matplotlib.backend_bases._Backend):
locals().update(vars(importlib.import_module(backend_name)))
backend_mod = importlib.import_module(
cbook._backend_module_name(newbackend))
canvas_class = backend_mod.FigureCanvas

required_framework = _get_required_interactive_framework(backend_mod)
if required_framework is not None:
Expand All @@ -286,6 +280,36 @@ class backend_mod(matplotlib.backend_bases._Backend):
"framework, as {!r} is currently running".format(
newbackend, required_framework, current_framework))

# Load the new_figure_manager(), draw_if_interactive(), and show()
# functions from the backend.

# Classically, backends can directly export these functions. This should
# keep working for backcompat.
new_figure_manager = getattr(backend_mod, "new_figure_manager", None)
# draw_if_interactive = getattr(backend_mod, "draw_if_interactive", None)
# show = getattr(backend_mod, "show", None)
# In that classical approach, backends are implemented as modules, but
# "inherit" default method implementations from backend_bases._Backend.
# This is achieved by creating a "class" that inherits from
# backend_bases._Backend and whose body is filled with the module globals.
class backend_mod(matplotlib.backend_bases._Backend):
locals().update(vars(backend_mod))

# However, the newer approach for defining new_figure_manager (and, in
# the future, draw_if_interactive and show) is to derive them from canvas
# methods. In that case, also update backend_mod accordingly.
if new_figure_manager is None:
def new_figure_manager_given_figure(num, figure):
return canvas_class.new_manager(figure, num)

def new_figure_manager(num, *args, FigureClass=Figure, **kwargs):
fig = FigureClass(*args, **kwargs)
return new_figure_manager_given_figure(num, fig)

backend_mod.new_figure_manager_given_figure = \
new_figure_manager_given_figure
backend_mod.new_figure_manager = new_figure_manager

_log.debug("Loaded backend %s version %s.",
newbackend, backend_mod.backend_version)

Expand Down
5 changes: 2 additions & 3 deletions lib/matplotlib/testing/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ def mpl_test_settings(request):

backend = None
backend_marker = request.node.get_closest_marker('backend')
prev_backend = matplotlib.get_backend()
if backend_marker is not None:
assert len(backend_marker.args) == 1, \
"Marker 'backend' must specify 1 backend."
backend, = backend_marker.args
skip_on_importerror = backend_marker.kwargs.get(
'skip_on_importerror', False)
prev_backend = matplotlib.get_backend()

# special case Qt backend importing to avoid conflicts
if backend.lower().startswith('qt5'):
Expand Down Expand Up @@ -87,8 +87,7 @@ def mpl_test_settings(request):
try:
yield
finally:
if backend is not None:
plt.switch_backend(prev_backend)
matplotlib.use(prev_backend)


@pytest.fixture
Expand Down
23 changes: 23 additions & 0 deletions lib/matplotlib/tests/test_backend_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Backend-loading machinery tests, using variations on the template backend.
"""

import sys
from types import SimpleNamespace

import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib.backends import backend_template


def test_load_template():
mpl.use("template")
assert type(plt.figure().canvas) == backend_template.FigureCanvasTemplate


def test_new_manager(monkeypatch):
mpl_test_backend = SimpleNamespace(**vars(backend_template))
del mpl_test_backend.new_figure_manager
monkeypatch.setitem(sys.modules, "mpl_test_backend", mpl_test_backend)
mpl.use("module://mpl_test_backend")
assert type(plt.figure().canvas) == backend_template.FigureCanvasTemplate