From 6a04d7d1ba57a323badd211641c7c835dad3ae1c Mon Sep 17 00:00:00 2001 From: martinRenou Date: Mon, 8 Feb 2021 18:14:48 +0100 Subject: [PATCH] Plugging system for backends --- lib/matplotlib/backend_bases.py | 2 +- .../backends/manifests/GTK3Agg.json | 5 +++ .../backends/manifests/GTK3Cairo.json | 5 +++ lib/matplotlib/backends/manifests/MacOSX.json | 5 +++ lib/matplotlib/backends/manifests/Qt4Agg.json | 5 +++ .../backends/manifests/Qt4Cairo.json | 5 +++ lib/matplotlib/backends/manifests/Qt5Agg.json | 5 +++ .../backends/manifests/Qt5Cairo.json | 5 +++ lib/matplotlib/backends/manifests/TkAgg.json | 5 +++ .../backends/manifests/TkCairo.json | 5 +++ lib/matplotlib/backends/manifests/WX.json | 5 +++ lib/matplotlib/backends/manifests/WXAgg.json | 5 +++ .../backends/manifests/WXCairo.json | 5 +++ lib/matplotlib/backends/manifests/WebAgg.json | 5 +++ lib/matplotlib/backends/manifests/agg.json | 5 +++ lib/matplotlib/backends/manifests/cairo.json | 5 +++ lib/matplotlib/backends/manifests/gtk3.json | 5 +++ lib/matplotlib/backends/manifests/mixed.json | 5 +++ lib/matplotlib/backends/manifests/nbAgg.json | 5 +++ lib/matplotlib/backends/manifests/pdf.json | 5 +++ lib/matplotlib/backends/manifests/pgf.json | 5 +++ lib/matplotlib/backends/manifests/ps.json | 5 +++ lib/matplotlib/backends/manifests/qt4.json | 5 +++ lib/matplotlib/backends/manifests/qt5.json | 5 +++ lib/matplotlib/backends/manifests/svg.json | 5 +++ .../backends/manifests/template.json | 5 +++ .../backends/manifests/webagg_core.json | 5 +++ lib/matplotlib/cbook/__init__.py | 32 +++++++++++++++---- lib/matplotlib/pyplot.py | 2 +- lib/matplotlib/rcsetup.py | 16 +++------- setup.py | 11 +++++++ 31 files changed, 172 insertions(+), 21 deletions(-) create mode 100644 lib/matplotlib/backends/manifests/GTK3Agg.json create mode 100644 lib/matplotlib/backends/manifests/GTK3Cairo.json create mode 100644 lib/matplotlib/backends/manifests/MacOSX.json create mode 100644 lib/matplotlib/backends/manifests/Qt4Agg.json create mode 100644 lib/matplotlib/backends/manifests/Qt4Cairo.json create mode 100644 lib/matplotlib/backends/manifests/Qt5Agg.json create mode 100644 lib/matplotlib/backends/manifests/Qt5Cairo.json create mode 100644 lib/matplotlib/backends/manifests/TkAgg.json create mode 100644 lib/matplotlib/backends/manifests/TkCairo.json create mode 100644 lib/matplotlib/backends/manifests/WX.json create mode 100644 lib/matplotlib/backends/manifests/WXAgg.json create mode 100644 lib/matplotlib/backends/manifests/WXCairo.json create mode 100644 lib/matplotlib/backends/manifests/WebAgg.json create mode 100644 lib/matplotlib/backends/manifests/agg.json create mode 100644 lib/matplotlib/backends/manifests/cairo.json create mode 100644 lib/matplotlib/backends/manifests/gtk3.json create mode 100644 lib/matplotlib/backends/manifests/mixed.json create mode 100644 lib/matplotlib/backends/manifests/nbAgg.json create mode 100644 lib/matplotlib/backends/manifests/pdf.json create mode 100644 lib/matplotlib/backends/manifests/pgf.json create mode 100644 lib/matplotlib/backends/manifests/ps.json create mode 100644 lib/matplotlib/backends/manifests/qt4.json create mode 100644 lib/matplotlib/backends/manifests/qt5.json create mode 100644 lib/matplotlib/backends/manifests/svg.json create mode 100644 lib/matplotlib/backends/manifests/template.json create mode 100644 lib/matplotlib/backends/manifests/webagg_core.json diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index ac041495a030..6b4837293f48 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -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( diff --git a/lib/matplotlib/backends/manifests/GTK3Agg.json b/lib/matplotlib/backends/manifests/GTK3Agg.json new file mode 100644 index 000000000000..17d28e5f8744 --- /dev/null +++ b/lib/matplotlib/backends/manifests/GTK3Agg.json @@ -0,0 +1,5 @@ +{ + "name": "GTK3Agg", + "module": "matplotlib.backends.backend_gtk3agg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/GTK3Cairo.json b/lib/matplotlib/backends/manifests/GTK3Cairo.json new file mode 100644 index 000000000000..a85630ee3fd0 --- /dev/null +++ b/lib/matplotlib/backends/manifests/GTK3Cairo.json @@ -0,0 +1,5 @@ +{ + "name": "GTK3Cairo", + "module": "matplotlib.backends.backend_gtk3cairo", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/MacOSX.json b/lib/matplotlib/backends/manifests/MacOSX.json new file mode 100644 index 000000000000..708ecbb39dbb --- /dev/null +++ b/lib/matplotlib/backends/manifests/MacOSX.json @@ -0,0 +1,5 @@ +{ + "name": "MacOSX", + "module": "matplotlib.backends.backend_macosx", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/Qt4Agg.json b/lib/matplotlib/backends/manifests/Qt4Agg.json new file mode 100644 index 000000000000..1f920fea7972 --- /dev/null +++ b/lib/matplotlib/backends/manifests/Qt4Agg.json @@ -0,0 +1,5 @@ +{ + "name": "Qt4Agg", + "module": "matplotlib.backends.backend_qt4agg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/Qt4Cairo.json b/lib/matplotlib/backends/manifests/Qt4Cairo.json new file mode 100644 index 000000000000..cad78c4f8abc --- /dev/null +++ b/lib/matplotlib/backends/manifests/Qt4Cairo.json @@ -0,0 +1,5 @@ +{ + "name": "Qt4Cairo", + "module": "matplotlib.backends.backend_qt4cairo", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/Qt5Agg.json b/lib/matplotlib/backends/manifests/Qt5Agg.json new file mode 100644 index 000000000000..6886cd652f71 --- /dev/null +++ b/lib/matplotlib/backends/manifests/Qt5Agg.json @@ -0,0 +1,5 @@ +{ + "name": "Qt5Agg", + "module": "matplotlib.backends.backend_qt5agg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/Qt5Cairo.json b/lib/matplotlib/backends/manifests/Qt5Cairo.json new file mode 100644 index 000000000000..8c8a5081db15 --- /dev/null +++ b/lib/matplotlib/backends/manifests/Qt5Cairo.json @@ -0,0 +1,5 @@ +{ + "name": "Qt5Cairo", + "module": "matplotlib.backends.backend_qt5cairo", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/TkAgg.json b/lib/matplotlib/backends/manifests/TkAgg.json new file mode 100644 index 000000000000..a48d6ced46b3 --- /dev/null +++ b/lib/matplotlib/backends/manifests/TkAgg.json @@ -0,0 +1,5 @@ +{ + "name": "TkAgg", + "module": "matplotlib.backends.backend_tkagg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/TkCairo.json b/lib/matplotlib/backends/manifests/TkCairo.json new file mode 100644 index 000000000000..52254a3ccd16 --- /dev/null +++ b/lib/matplotlib/backends/manifests/TkCairo.json @@ -0,0 +1,5 @@ +{ + "name": "TkCairo", + "module": "matplotlib.backends.backend_tkcairo", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/WX.json b/lib/matplotlib/backends/manifests/WX.json new file mode 100644 index 000000000000..addef1d363bc --- /dev/null +++ b/lib/matplotlib/backends/manifests/WX.json @@ -0,0 +1,5 @@ +{ + "name": "WX", + "module": "matplotlib.backends.backend_wx", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/WXAgg.json b/lib/matplotlib/backends/manifests/WXAgg.json new file mode 100644 index 000000000000..684700737942 --- /dev/null +++ b/lib/matplotlib/backends/manifests/WXAgg.json @@ -0,0 +1,5 @@ +{ + "name": "WXAgg", + "module": "matplotlib.backends.backend_wxagg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/WXCairo.json b/lib/matplotlib/backends/manifests/WXCairo.json new file mode 100644 index 000000000000..ff5c86ad2238 --- /dev/null +++ b/lib/matplotlib/backends/manifests/WXCairo.json @@ -0,0 +1,5 @@ +{ + "name": "WXCairo", + "module": "matplotlib.backends.backend_wxcairo", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/WebAgg.json b/lib/matplotlib/backends/manifests/WebAgg.json new file mode 100644 index 000000000000..c02e94f27804 --- /dev/null +++ b/lib/matplotlib/backends/manifests/WebAgg.json @@ -0,0 +1,5 @@ +{ + "name": "WebAgg", + "module": "matplotlib.backends.backend_webagg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/agg.json b/lib/matplotlib/backends/manifests/agg.json new file mode 100644 index 000000000000..e58f287f80bf --- /dev/null +++ b/lib/matplotlib/backends/manifests/agg.json @@ -0,0 +1,5 @@ +{ + "name": "agg", + "module": "matplotlib.backends.backend_agg", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/cairo.json b/lib/matplotlib/backends/manifests/cairo.json new file mode 100644 index 000000000000..316b97b6ecdc --- /dev/null +++ b/lib/matplotlib/backends/manifests/cairo.json @@ -0,0 +1,5 @@ +{ + "name": "cairo", + "module": "matplotlib.backends.backend_cairo", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/gtk3.json b/lib/matplotlib/backends/manifests/gtk3.json new file mode 100644 index 000000000000..58016dd89052 --- /dev/null +++ b/lib/matplotlib/backends/manifests/gtk3.json @@ -0,0 +1,5 @@ +{ + "name": "gtk3", + "module": "matplotlib.backends.backend_gtk3", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/mixed.json b/lib/matplotlib/backends/manifests/mixed.json new file mode 100644 index 000000000000..7db3a6459eb0 --- /dev/null +++ b/lib/matplotlib/backends/manifests/mixed.json @@ -0,0 +1,5 @@ +{ + "name": "mixed", + "module": "matplotlib.backends.backend_mixed", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/nbAgg.json b/lib/matplotlib/backends/manifests/nbAgg.json new file mode 100644 index 000000000000..169df405ccd9 --- /dev/null +++ b/lib/matplotlib/backends/manifests/nbAgg.json @@ -0,0 +1,5 @@ +{ + "name": "nbAgg", + "module": "matplotlib.backends.backend_nbagg", + "interactive": true +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/pdf.json b/lib/matplotlib/backends/manifests/pdf.json new file mode 100644 index 000000000000..6643eb582ca0 --- /dev/null +++ b/lib/matplotlib/backends/manifests/pdf.json @@ -0,0 +1,5 @@ +{ + "name": "pdf", + "module": "matplotlib.backends.backend_pdf", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/pgf.json b/lib/matplotlib/backends/manifests/pgf.json new file mode 100644 index 000000000000..abe3cd91aca3 --- /dev/null +++ b/lib/matplotlib/backends/manifests/pgf.json @@ -0,0 +1,5 @@ +{ + "name": "pgf", + "module": "matplotlib.backends.backend_pgf", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/ps.json b/lib/matplotlib/backends/manifests/ps.json new file mode 100644 index 000000000000..88ec5f979491 --- /dev/null +++ b/lib/matplotlib/backends/manifests/ps.json @@ -0,0 +1,5 @@ +{ + "name": "ps", + "module": "matplotlib.backends.backend_ps", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/qt4.json b/lib/matplotlib/backends/manifests/qt4.json new file mode 100644 index 000000000000..7c2d53d251b8 --- /dev/null +++ b/lib/matplotlib/backends/manifests/qt4.json @@ -0,0 +1,5 @@ +{ + "name": "qt4", + "module": "matplotlib.backends.backend_qt4", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/qt5.json b/lib/matplotlib/backends/manifests/qt5.json new file mode 100644 index 000000000000..73d92775e3ed --- /dev/null +++ b/lib/matplotlib/backends/manifests/qt5.json @@ -0,0 +1,5 @@ +{ + "name": "qt5", + "module": "matplotlib.backends.backend_qt5", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/svg.json b/lib/matplotlib/backends/manifests/svg.json new file mode 100644 index 000000000000..8ae035bfd82d --- /dev/null +++ b/lib/matplotlib/backends/manifests/svg.json @@ -0,0 +1,5 @@ +{ + "name": "svg", + "module": "matplotlib.backends.backend_svg", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/template.json b/lib/matplotlib/backends/manifests/template.json new file mode 100644 index 000000000000..3a932e440217 --- /dev/null +++ b/lib/matplotlib/backends/manifests/template.json @@ -0,0 +1,5 @@ +{ + "name": "template", + "module": "matplotlib.backends.backend_template", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/backends/manifests/webagg_core.json b/lib/matplotlib/backends/manifests/webagg_core.json new file mode 100644 index 000000000000..a415f9b2c3ab --- /dev/null +++ b/lib/matplotlib/backends/manifests/webagg_core.json @@ -0,0 +1,5 @@ +{ + "name": "webagg_core", + "module": "matplotlib.backends.backend_webagg_core", + "interactive": false +} \ No newline at end of file diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index 31055dcd7990..e7070e0728ca 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -12,6 +12,8 @@ import functools import gzip import itertools +import json +import logging import operator import os from pathlib import Path @@ -33,6 +35,8 @@ MatplotlibDeprecationWarning, mplDeprecation) +_log = logging.getLogger(__name__) + @_api.deprecated("3.4") def deprecated(*args, **kwargs): return _api.deprecated(*args, **kwargs) @@ -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(): diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index 39b28ab11346..aa081786f33b 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -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))) diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index 535649b03f9f..0090be6ecf04 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -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 @@ -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 diff --git a/setup.py b/setup.py index b51ae806c2fb..1b35cea1d92a 100644 --- a/setup.py +++ b/setup.py @@ -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__, @@ -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=[