From 954e84de336cd5c831982f8595fb077df46b87b8 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 28 Aug 2018 19:49:40 +0200 Subject: [PATCH] Propagate changes to backend loading to setup/setupext. 1) Restore the ability to set the default backend from setup.cfg. 2) Remove the backend fallback code. Note that not including `mpl-data/*.glade` in the package data is fine: that file had been removed in 962388f anyways. --- .flake8 | 2 +- setup.cfg.template | 9 +- setup.py | 32 ++----- setupext.py | 230 --------------------------------------------- 4 files changed, 12 insertions(+), 261 deletions(-) diff --git a/.flake8 b/.flake8 index 2c48ca999316..f06b760e386a 100644 --- a/.flake8 +++ b/.flake8 @@ -23,7 +23,7 @@ exclude = per-file-ignores = setup.py: E402 - setupext.py: E302, E501 + setupext.py: E501 tools/compare_backend_driver_results.py: E501 tools/subset.py: E221, E231, E251, E261, E302, E501, E701 diff --git a/setup.cfg.template b/setup.cfg.template index 7ccfb7edcbe4..4fed27d31302 100644 --- a/setup.cfg.template +++ b/setup.cfg.template @@ -63,15 +63,9 @@ # behavior # #agg = auto -#cairo = auto -#gtk3agg = auto -#gtk3cairo = auto #macosx = auto -#pyside = auto -#qt4agg = auto #tkagg = auto #windowing = auto -#wxagg = auto [rc_options] # User-configurable options @@ -81,10 +75,9 @@ # # The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do # not choose MacOSX, or TkAgg if you have disabled the relevant extension -# modules. Agg will be used by default. +# modules. The default is determined by fallback. # #backend = Agg -# [package_data] # Package additional files found in the lib/matplotlib directories. diff --git a/setup.py b/setup.py index 6eab9b6b88fd..7e82e94c29c8 100644 --- a/setup.py +++ b/setup.py @@ -70,18 +70,9 @@ setupext.Tests(), setupext.Toolkits_Tests(), 'Optional backend extensions', - # These backends are listed in order of preference, the first - # being the most preferred. The first one that looks like it will - # work will be selected as the default backend. - setupext.BackendMacOSX(), - setupext.BackendQt5(), - setupext.BackendQt4(), - setupext.BackendGtk3Agg(), - setupext.BackendGtk3Cairo(), - setupext.BackendTkAgg(), - setupext.BackendWxAgg(), setupext.BackendAgg(), - setupext.BackendCairo(), + setupext.BackendTkAgg(), + setupext.BackendMacOSX(), setupext.Windowing(), 'Optional package data', setupext.Dlls(), @@ -133,7 +124,6 @@ def run(self): package_dir = {'': 'lib'} install_requires = [] setup_requires = [] - default_backend = None # If the user just queries for information, don't bother figuring out which # packages to build or install. @@ -169,10 +159,6 @@ def run(self): required_failed.append(package) else: good_packages.append(package) - if (isinstance(package, setupext.OptionalBackendPackage) - and package.runtime_check() - and default_backend is None): - default_backend = package.name print_raw('') # Abort if any of the required packages can not be built. @@ -203,14 +189,16 @@ def run(self): setup_requires.extend(package.get_setup_requires()) # Write the default matplotlibrc file - if default_backend is None: - default_backend = 'svg' - if setupext.options['backend']: - default_backend = setupext.options['backend'] with open('matplotlibrc.template') as fd: - template = fd.read() + template_lines = fd.read().splitlines(True) + backend_line_idx, = [ # Also asserts that there is a single such line. + idx for idx, line in enumerate(template_lines) + if line.startswith('#backend ')] + if setupext.options['backend']: + template_lines[backend_line_idx] = ( + 'backend: {}'.format(setupext.options['backend'])) with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd: - fd.write(template) + fd.write(''.join(template_lines)) # Finalize the extension modules so they can get the Numpy include # dirs diff --git a/setupext.py b/setupext.py index 5644bae9dab5..582c7d89c9da 100644 --- a/setupext.py +++ b/setupext.py @@ -384,13 +384,6 @@ def check(self): """ pass - def runtime_check(self): - """ - True if the runtime dependencies of the backend are met. Assumes that - the build-time dependencies are met. - """ - return True - def get_packages(self): """ Get a list of package names to add to the configuration. @@ -1368,10 +1361,6 @@ class BackendTkAgg(OptionalBackendPackage): def check(self): return "installing; run-time loading from Python Tcl / Tk" - def runtime_check(self): - """Checks whether TkAgg runtime dependencies are met.""" - return importlib.util.find_spec("tkinter") is not None - def get_extension(self): sources = [ 'src/_tkagg.cpp' @@ -1391,57 +1380,6 @@ def add_flags(self, ext): ext.libraries.extend(['dl']) -class BackendGtk3Agg(OptionalBackendPackage): - name = "gtk3agg" - - def check_requirements(self): - if not any(map(importlib.util.find_spec, ["cairocffi", "cairo"])): - raise CheckFailed("Requires cairocffi or pycairo to be installed.") - - try: - import gi - except ImportError: - raise CheckFailed("Requires pygobject to be installed.") - - try: - gi.require_version("Gtk", "3.0") - except ValueError: - raise CheckFailed( - "Requires gtk3 development files to be installed.") - except AttributeError: - raise CheckFailed("pygobject version too old.") - - try: - from gi.repository import Gtk, Gdk, GObject - except (ImportError, RuntimeError): - raise CheckFailed("Requires pygobject to be installed.") - - return "version {}.{}.{}".format( - Gtk.get_major_version(), - Gtk.get_minor_version(), - Gtk.get_micro_version()) - - def get_package_data(self): - return {'matplotlib': ['mpl-data/*.glade']} - - -class BackendGtk3Cairo(BackendGtk3Agg): - name = "gtk3cairo" - - -class BackendWxAgg(OptionalBackendPackage): - name = "wxagg" - - def check_requirements(self): - try: - import wx - backend_version = wx.VERSION_STRING - except ImportError: - raise CheckFailed("requires wxPython") - - return "version %s" % backend_version - - class BackendMacOSX(OptionalBackendPackage): name = 'macosx' @@ -1487,174 +1425,6 @@ def get_extension(self): return ext -class BackendQtBase(OptionalBackendPackage): - - def convert_qt_version(self, version): - version = '%x' % version - temp = [] - while len(version) > 0: - version, chunk = version[:-2], version[-2:] - temp.insert(0, str(int(chunk, 16))) - return '.'.join(temp) - - def check_requirements(self): - """ - If PyQt4/PyQt5 is already imported, importing PyQt5/PyQt4 will fail - so we need to test in a subprocess (as for Gtk3). - """ - try: - p = multiprocessing.Pool() - - except: - # Can't do multiprocessing, fall back to normal approach - # (this will fail if importing both PyQt4 and PyQt5). - try: - # Try in-process - msg = self.callback(self) - except RuntimeError: - raise CheckFailed( - "Could not import: are PyQt4 & PyQt5 both installed?") - - else: - # Multiprocessing OK - try: - res = p.map_async(self.callback, [self]) - msg = res.get(timeout=10)[0] - except multiprocessing.TimeoutError: - p.terminate() - # No result returned. Probably hanging, terminate the process. - raise CheckFailed("Check timed out") - except: - # Some other error. - p.close() - raise - else: - # Clean exit - p.close() - finally: - # Tidy up multiprocessing - p.join() - - return msg - - -def backend_pyside_internal_check(self): - try: - from PySide import __version__ - from PySide import QtCore - except ImportError: - raise CheckFailed("PySide not found") - else: - return ("Qt: %s, PySide: %s" % - (QtCore.__version__, __version__)) - - -def backend_pyqt4_internal_check(self): - try: - from PyQt4 import QtCore - except ImportError: - raise CheckFailed("PyQt4 not found") - - try: - qt_version = QtCore.QT_VERSION - pyqt_version_str = QtCore.PYQT_VERSION_STR - except AttributeError: - raise CheckFailed('PyQt4 not correctly imported') - else: - return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) - - -def backend_qt4_internal_check(self): - successes = [] - failures = [] - try: - successes.append(backend_pyside_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - try: - successes.append(backend_pyqt4_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - if len(successes) == 0: - raise CheckFailed('; '.join(failures)) - return '; '.join(successes + failures) - - -class BackendQt4(BackendQtBase): - name = "qt4agg" - - def __init__(self, *args, **kwargs): - BackendQtBase.__init__(self, *args, **kwargs) - self.callback = backend_qt4_internal_check - -def backend_pyside2_internal_check(self): - try: - from PySide2 import __version__ - from PySide2 import QtCore - except ImportError: - raise CheckFailed("PySide2 not found") - else: - return ("Qt: %s, PySide2: %s" % - (QtCore.__version__, __version__)) - -def backend_pyqt5_internal_check(self): - try: - from PyQt5 import QtCore - except ImportError: - raise CheckFailed("PyQt5 not found") - - try: - qt_version = QtCore.QT_VERSION - pyqt_version_str = QtCore.PYQT_VERSION_STR - except AttributeError: - raise CheckFailed('PyQt5 not correctly imported') - else: - return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str)) - -def backend_qt5_internal_check(self): - successes = [] - failures = [] - try: - successes.append(backend_pyside2_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - try: - successes.append(backend_pyqt5_internal_check(self)) - except CheckFailed as e: - failures.append(str(e)) - - if len(successes) == 0: - raise CheckFailed('; '.join(failures)) - return '; '.join(successes + failures) - -class BackendQt5(BackendQtBase): - name = "qt5agg" - - def __init__(self, *args, **kwargs): - BackendQtBase.__init__(self, *args, **kwargs) - self.callback = backend_qt5_internal_check - - -class BackendCairo(OptionalBackendPackage): - name = "cairo" - - def check_requirements(self): - try: - import cairocffi - except ImportError: - try: - import cairo - except ImportError: - raise CheckFailed("cairocffi or pycairo not found") - else: - return "pycairo version %s" % cairo.version - else: - return "cairocffi version %s" % cairocffi.version - - class OptionalPackageData(OptionalPackage): config_category = "package_data"