Skip to content

Plugin system for backends #19482

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

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 1 addition & 1 deletion lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -2084,7 +2084,7 @@ def _get_output_canvas(self, backend, fmt):
if backend is not None:
# Return a specific canvas class, if requested.
canvas_class = (
importlib.import_module(cbook._backend_module_name(backend))
importlib.import_module(cbook.backends[backend]['module'])
.FigureCanvas)
if not hasattr(canvas_class, f"print_{fmt}"):
raise ValueError(
Expand Down
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/GTK3Agg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "GTK3Agg",
"module": "matplotlib.backends.backend_gtk3agg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/GTK3Cairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "GTK3Cairo",
"module": "matplotlib.backends.backend_gtk3cairo",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/MacOSX.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "MacOSX",
"module": "matplotlib.backends.backend_macosx",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/Qt4Agg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Qt4Agg",
"module": "matplotlib.backends.backend_qt4agg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/Qt4Cairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Qt4Cairo",
"module": "matplotlib.backends.backend_qt4cairo",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/Qt5Agg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Qt5Agg",
"module": "matplotlib.backends.backend_qt5agg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/Qt5Cairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "Qt5Cairo",
"module": "matplotlib.backends.backend_qt5cairo",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/TkAgg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "TkAgg",
"module": "matplotlib.backends.backend_tkagg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/TkCairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "TkCairo",
"module": "matplotlib.backends.backend_tkcairo",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/WX.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "WX",
"module": "matplotlib.backends.backend_wx",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/WXAgg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "WXAgg",
"module": "matplotlib.backends.backend_wxagg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/WXCairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "WXCairo",
"module": "matplotlib.backends.backend_wxcairo",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/WebAgg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "WebAgg",
"module": "matplotlib.backends.backend_webagg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/agg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "agg",
"module": "matplotlib.backends.backend_agg",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/cairo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "cairo",
"module": "matplotlib.backends.backend_cairo",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/gtk3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "gtk3",
"module": "matplotlib.backends.backend_gtk3",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/mixed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "mixed",
"module": "matplotlib.backends.backend_mixed",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/nbAgg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "nbAgg",
"module": "matplotlib.backends.backend_nbagg",
"interactive": true
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/pdf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "pdf",
"module": "matplotlib.backends.backend_pdf",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/pgf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "pgf",
"module": "matplotlib.backends.backend_pgf",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/ps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "ps",
"module": "matplotlib.backends.backend_ps",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/qt4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "qt4",
"module": "matplotlib.backends.backend_qt4",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/qt5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "qt5",
"module": "matplotlib.backends.backend_qt5",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/svg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "svg",
"module": "matplotlib.backends.backend_svg",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/template.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "template",
"module": "matplotlib.backends.backend_template",
"interactive": false
}
5 changes: 5 additions & 0 deletions lib/matplotlib/backends/manifests/webagg_core.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "webagg_core",
"module": "matplotlib.backends.backend_webagg_core",
"interactive": false
}
32 changes: 25 additions & 7 deletions lib/matplotlib/cbook/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import functools
import gzip
import itertools
import json
import logging
import operator
import os
from pathlib import Path
Expand All @@ -33,6 +35,8 @@
MatplotlibDeprecationWarning, mplDeprecation)


_log = logging.getLogger(__name__)

@_api.deprecated("3.4")
def deprecated(*args, **kwargs):
return _api.deprecated(*args, **kwargs)
Expand Down Expand Up @@ -2245,13 +2249,27 @@ def _check_and_log_subprocess(command, logger, **kwargs):
return proc.stdout


def _backend_module_name(name):
"""
Convert a backend name (either a standard backend -- "Agg", "TkAgg", ... --
or a custom backend -- "module://...") to the corresponding module name).
"""
return (name[9:] if name.startswith("module://")
else "matplotlib.backends.backend_{}".format(name.lower()))
def load_backends():
backends_dir = Path(sys.prefix) / 'share' / 'matplotlib' / 'backends'
manifests = [str(manifest) for manifest in backends_dir.glob('*.json')]

backends = {}
for manifest in manifests:
try:
with open(manifest, 'r') as fobj:
backend = json.load(fobj)
backends[backend['name']] = {
'module': backend['module'],
'interactive': backend['interactive']
}
except:
_log.warning('Error while loading {}'.format(manifest))

return backends


# Load backends
backends = load_backends()


def _setup_new_guiapp():
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def switch_backend(newbackend):
# 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)
backend_name = cbook.backends[newbackend]['module']

class backend_mod(matplotlib.backend_bases._Backend):
locals().update(vars(importlib.import_module(backend_name)))
Expand Down
16 changes: 4 additions & 12 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import numpy as np

from matplotlib import _api, animation, cbook
from matplotlib.cbook import ls_mapper
from matplotlib.cbook import ls_mapper, backends
from matplotlib.colors import Colormap, is_color_like
from matplotlib.fontconfig_pattern import parse_fontconfig_pattern
from matplotlib._enums import JoinStyle, CapStyle
Expand All @@ -33,17 +33,9 @@


_log = logging.getLogger(__name__)
# The capitalized forms are needed for ipython at present; this may
# change for later versions.
interactive_bk = ['GTK3Agg', 'GTK3Cairo',
'MacOSX',
'nbAgg',
'Qt4Agg', 'Qt4Cairo', 'Qt5Agg', 'Qt5Cairo',
'TkAgg', 'TkCairo',
'WebAgg',
'WX', 'WXAgg', 'WXCairo']
non_interactive_bk = ['agg', 'cairo',
'pdf', 'pgf', 'ps', 'svg', 'template']

interactive_bk = [name for name, backend in backends.items() if backend['interactive']]
non_interactive_bk = [name for name, backend in backends.items() if not backend['interactive']]
all_backends = interactive_bk + non_interactive_bk


Expand Down
11 changes: 11 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ def build_extensions(self):
with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd:
fd.write(''.join(template_lines))


share_prefix = str(Path('share') / 'matplotlib' / 'backends')
backends_manifests_dir = Path(__file__).parent / 'lib' / 'matplotlib' / 'backends' / 'manifests'
data_files = [
(
str(Path('share') / 'matplotlib' / 'backends'),
[str(manifest) for manifest in backends_manifests_dir.glob('*.json')]
),
]

setup( # Finally, pass this all along to distutils to do the heavy lifting.
name="matplotlib",
version=__version__,
Expand Down Expand Up @@ -296,6 +306,7 @@ def build_extensions(self):
# real extensions that can depend on numpy for the build.
ext_modules=[Extension("", [])],
package_data=package_data,
data_files=data_files,

python_requires='>={}'.format('.'.join(str(n) for n in py_min_version)),
setup_requires=[
Expand Down