Skip to content

Commit dadb8d0

Browse files
authored
Merge pull request matplotlib#11962 from anntzer/setupextbackend
BLD: Propagate changes to backend loading to setup/setupext.
2 parents 2188358 + 954e84d commit dadb8d0

File tree

4 files changed

+12
-261
lines changed

4 files changed

+12
-261
lines changed

.flake8

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ exclude =
2323

2424
per-file-ignores =
2525
setup.py: E402
26-
setupext.py: E302, E501
26+
setupext.py: E501
2727

2828
tools/compare_backend_driver_results.py: E501
2929
tools/subset.py: E221, E251, E261, E302, E501

setup.cfg.template

+1-8
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,9 @@
6363
# behavior
6464
#
6565
#agg = auto
66-
#cairo = auto
67-
#gtk3agg = auto
68-
#gtk3cairo = auto
6966
#macosx = auto
70-
#pyside = auto
71-
#qt4agg = auto
7267
#tkagg = auto
7368
#windowing = auto
74-
#wxagg = auto
7569

7670
[rc_options]
7771
# User-configurable options
@@ -81,10 +75,9 @@
8175
#
8276
# The Agg, Ps, Pdf and SVG backends do not require external dependencies. Do
8377
# not choose MacOSX, or TkAgg if you have disabled the relevant extension
84-
# modules. Agg will be used by default.
78+
# modules. The default is determined by fallback.
8579
#
8680
#backend = Agg
87-
#
8881

8982
[package_data]
9083
# Package additional files found in the lib/matplotlib directories.

setup.py

+10-22
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,9 @@
6969
setupext.Tests(),
7070
setupext.Toolkits_Tests(),
7171
'Optional backend extensions',
72-
# These backends are listed in order of preference, the first
73-
# being the most preferred. The first one that looks like it will
74-
# work will be selected as the default backend.
75-
setupext.BackendMacOSX(),
76-
setupext.BackendQt5(),
77-
setupext.BackendQt4(),
78-
setupext.BackendGtk3Agg(),
79-
setupext.BackendGtk3Cairo(),
80-
setupext.BackendTkAgg(),
81-
setupext.BackendWxAgg(),
8272
setupext.BackendAgg(),
83-
setupext.BackendCairo(),
73+
setupext.BackendTkAgg(),
74+
setupext.BackendMacOSX(),
8475
setupext.Windowing(),
8576
'Optional package data',
8677
setupext.Dlls(),
@@ -137,7 +128,6 @@ def build_extensions(self):
137128
package_dir = {'': 'lib'}
138129
install_requires = []
139130
setup_requires = []
140-
default_backend = None
141131

142132
# If the user just queries for information, don't bother figuring out which
143133
# packages to build or install.
@@ -173,10 +163,6 @@ def build_extensions(self):
173163
required_failed.append(package)
174164
else:
175165
good_packages.append(package)
176-
if (isinstance(package, setupext.OptionalBackendPackage)
177-
and package.runtime_check()
178-
and default_backend is None):
179-
default_backend = package.name
180166
print_raw('')
181167

182168
# Abort if any of the required packages can not be built.
@@ -207,14 +193,16 @@ def build_extensions(self):
207193
setup_requires.extend(package.get_setup_requires())
208194

209195
# Write the default matplotlibrc file
210-
if default_backend is None:
211-
default_backend = 'svg'
212-
if setupext.options['backend']:
213-
default_backend = setupext.options['backend']
214196
with open('matplotlibrc.template') as fd:
215-
template = fd.read()
197+
template_lines = fd.read().splitlines(True)
198+
backend_line_idx, = [ # Also asserts that there is a single such line.
199+
idx for idx, line in enumerate(template_lines)
200+
if line.startswith('#backend ')]
201+
if setupext.options['backend']:
202+
template_lines[backend_line_idx] = (
203+
'backend: {}'.format(setupext.options['backend']))
216204
with open('lib/matplotlib/mpl-data/matplotlibrc', 'w') as fd:
217-
fd.write(template)
205+
fd.write(''.join(template_lines))
218206

219207
# Finalize the extension modules so they can get the Numpy include
220208
# dirs

setupext.py

-230
Original file line numberDiff line numberDiff line change
@@ -355,13 +355,6 @@ def check(self):
355355
"""
356356
pass
357357

358-
def runtime_check(self):
359-
"""
360-
True if the runtime dependencies of the backend are met. Assumes that
361-
the build-time dependencies are met.
362-
"""
363-
return True
364-
365358
def get_packages(self):
366359
"""
367360
Get a list of package names to add to the configuration.
@@ -1325,10 +1318,6 @@ class BackendTkAgg(OptionalBackendPackage):
13251318
def check(self):
13261319
return "installing; run-time loading from Python Tcl / Tk"
13271320

1328-
def runtime_check(self):
1329-
"""Checks whether TkAgg runtime dependencies are met."""
1330-
return importlib.util.find_spec("tkinter") is not None
1331-
13321321
def get_extension(self):
13331322
sources = [
13341323
'src/_tkagg.cpp'
@@ -1348,57 +1337,6 @@ def add_flags(self, ext):
13481337
ext.libraries.extend(['dl'])
13491338

13501339

1351-
class BackendGtk3Agg(OptionalBackendPackage):
1352-
name = "gtk3agg"
1353-
1354-
def check_requirements(self):
1355-
if not any(map(importlib.util.find_spec, ["cairocffi", "cairo"])):
1356-
raise CheckFailed("Requires cairocffi or pycairo to be installed.")
1357-
1358-
try:
1359-
import gi
1360-
except ImportError:
1361-
raise CheckFailed("Requires pygobject to be installed.")
1362-
1363-
try:
1364-
gi.require_version("Gtk", "3.0")
1365-
except ValueError:
1366-
raise CheckFailed(
1367-
"Requires gtk3 development files to be installed.")
1368-
except AttributeError:
1369-
raise CheckFailed("pygobject version too old.")
1370-
1371-
try:
1372-
from gi.repository import Gtk, Gdk, GObject
1373-
except (ImportError, RuntimeError):
1374-
raise CheckFailed("Requires pygobject to be installed.")
1375-
1376-
return "version {}.{}.{}".format(
1377-
Gtk.get_major_version(),
1378-
Gtk.get_minor_version(),
1379-
Gtk.get_micro_version())
1380-
1381-
def get_package_data(self):
1382-
return {'matplotlib': ['mpl-data/*.glade']}
1383-
1384-
1385-
class BackendGtk3Cairo(BackendGtk3Agg):
1386-
name = "gtk3cairo"
1387-
1388-
1389-
class BackendWxAgg(OptionalBackendPackage):
1390-
name = "wxagg"
1391-
1392-
def check_requirements(self):
1393-
try:
1394-
import wx
1395-
backend_version = wx.VERSION_STRING
1396-
except ImportError:
1397-
raise CheckFailed("requires wxPython")
1398-
1399-
return "version %s" % backend_version
1400-
1401-
14021340
class BackendMacOSX(OptionalBackendPackage):
14031341
name = 'macosx'
14041342

@@ -1444,174 +1382,6 @@ def get_extension(self):
14441382
return ext
14451383

14461384

1447-
class BackendQtBase(OptionalBackendPackage):
1448-
1449-
def convert_qt_version(self, version):
1450-
version = '%x' % version
1451-
temp = []
1452-
while len(version) > 0:
1453-
version, chunk = version[:-2], version[-2:]
1454-
temp.insert(0, str(int(chunk, 16)))
1455-
return '.'.join(temp)
1456-
1457-
def check_requirements(self):
1458-
"""
1459-
If PyQt4/PyQt5 is already imported, importing PyQt5/PyQt4 will fail
1460-
so we need to test in a subprocess (as for Gtk3).
1461-
"""
1462-
try:
1463-
p = multiprocessing.Pool()
1464-
1465-
except:
1466-
# Can't do multiprocessing, fall back to normal approach
1467-
# (this will fail if importing both PyQt4 and PyQt5).
1468-
try:
1469-
# Try in-process
1470-
msg = self.callback(self)
1471-
except RuntimeError:
1472-
raise CheckFailed(
1473-
"Could not import: are PyQt4 & PyQt5 both installed?")
1474-
1475-
else:
1476-
# Multiprocessing OK
1477-
try:
1478-
res = p.map_async(self.callback, [self])
1479-
msg = res.get(timeout=10)[0]
1480-
except multiprocessing.TimeoutError:
1481-
p.terminate()
1482-
# No result returned. Probably hanging, terminate the process.
1483-
raise CheckFailed("Check timed out")
1484-
except:
1485-
# Some other error.
1486-
p.close()
1487-
raise
1488-
else:
1489-
# Clean exit
1490-
p.close()
1491-
finally:
1492-
# Tidy up multiprocessing
1493-
p.join()
1494-
1495-
return msg
1496-
1497-
1498-
def backend_pyside_internal_check(self):
1499-
try:
1500-
from PySide import __version__
1501-
from PySide import QtCore
1502-
except ImportError:
1503-
raise CheckFailed("PySide not found")
1504-
else:
1505-
return ("Qt: %s, PySide: %s" %
1506-
(QtCore.__version__, __version__))
1507-
1508-
1509-
def backend_pyqt4_internal_check(self):
1510-
try:
1511-
from PyQt4 import QtCore
1512-
except ImportError:
1513-
raise CheckFailed("PyQt4 not found")
1514-
1515-
try:
1516-
qt_version = QtCore.QT_VERSION
1517-
pyqt_version_str = QtCore.PYQT_VERSION_STR
1518-
except AttributeError:
1519-
raise CheckFailed('PyQt4 not correctly imported')
1520-
else:
1521-
return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str))
1522-
1523-
1524-
def backend_qt4_internal_check(self):
1525-
successes = []
1526-
failures = []
1527-
try:
1528-
successes.append(backend_pyside_internal_check(self))
1529-
except CheckFailed as e:
1530-
failures.append(str(e))
1531-
1532-
try:
1533-
successes.append(backend_pyqt4_internal_check(self))
1534-
except CheckFailed as e:
1535-
failures.append(str(e))
1536-
1537-
if len(successes) == 0:
1538-
raise CheckFailed('; '.join(failures))
1539-
return '; '.join(successes + failures)
1540-
1541-
1542-
class BackendQt4(BackendQtBase):
1543-
name = "qt4agg"
1544-
1545-
def __init__(self, *args, **kwargs):
1546-
BackendQtBase.__init__(self, *args, **kwargs)
1547-
self.callback = backend_qt4_internal_check
1548-
1549-
def backend_pyside2_internal_check(self):
1550-
try:
1551-
from PySide2 import __version__
1552-
from PySide2 import QtCore
1553-
except ImportError:
1554-
raise CheckFailed("PySide2 not found")
1555-
else:
1556-
return ("Qt: %s, PySide2: %s" %
1557-
(QtCore.__version__, __version__))
1558-
1559-
def backend_pyqt5_internal_check(self):
1560-
try:
1561-
from PyQt5 import QtCore
1562-
except ImportError:
1563-
raise CheckFailed("PyQt5 not found")
1564-
1565-
try:
1566-
qt_version = QtCore.QT_VERSION
1567-
pyqt_version_str = QtCore.PYQT_VERSION_STR
1568-
except AttributeError:
1569-
raise CheckFailed('PyQt5 not correctly imported')
1570-
else:
1571-
return ("Qt: %s, PyQt: %s" % (self.convert_qt_version(qt_version), pyqt_version_str))
1572-
1573-
def backend_qt5_internal_check(self):
1574-
successes = []
1575-
failures = []
1576-
try:
1577-
successes.append(backend_pyside2_internal_check(self))
1578-
except CheckFailed as e:
1579-
failures.append(str(e))
1580-
1581-
try:
1582-
successes.append(backend_pyqt5_internal_check(self))
1583-
except CheckFailed as e:
1584-
failures.append(str(e))
1585-
1586-
if len(successes) == 0:
1587-
raise CheckFailed('; '.join(failures))
1588-
return '; '.join(successes + failures)
1589-
1590-
class BackendQt5(BackendQtBase):
1591-
name = "qt5agg"
1592-
1593-
def __init__(self, *args, **kwargs):
1594-
BackendQtBase.__init__(self, *args, **kwargs)
1595-
self.callback = backend_qt5_internal_check
1596-
1597-
1598-
class BackendCairo(OptionalBackendPackage):
1599-
name = "cairo"
1600-
1601-
def check_requirements(self):
1602-
try:
1603-
import cairocffi
1604-
except ImportError:
1605-
try:
1606-
import cairo
1607-
except ImportError:
1608-
raise CheckFailed("cairocffi or pycairo not found")
1609-
else:
1610-
return "pycairo version %s" % cairo.version
1611-
else:
1612-
return "cairocffi version %s" % cairocffi.version
1613-
1614-
16151385
class OptionalPackageData(OptionalPackage):
16161386
config_category = "package_data"
16171387

0 commit comments

Comments
 (0)