##// END OF EJS Templates
Merge pull request #10210 from ipython/auto-backport-of-pr-10148...
Matthias Bussonnier -
r23250:8aeb83eb merge
parent child Browse files
Show More
@@ -33,10 +33,10 b' import sys'
33
33
34 from IPython.utils.version import check_version
34 from IPython.utils.version import check_version
35 from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE,
35 from IPython.external.qt_loaders import (load_qt, loaded_api, QT_API_PYSIDE,
36 QT_API_PYQT, QT_API_PYQT5,
36 QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5,
37 QT_API_PYQTv1, QT_API_PYQT_DEFAULT)
37 QT_API_PYQTv1, QT_API_PYQT_DEFAULT)
38
38
39 _qt_apis = (QT_API_PYSIDE, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1,
39 _qt_apis = (QT_API_PYSIDE, QT_API_PYSIDE2, QT_API_PYQT, QT_API_PYQT5, QT_API_PYQTv1,
40 QT_API_PYQT_DEFAULT)
40 QT_API_PYQT_DEFAULT)
41
41
42 #Constraints placed on an imported matplotlib
42 #Constraints placed on an imported matplotlib
@@ -83,7 +83,8 b' def get_options():'
83 qt_api = os.environ.get('QT_API', None)
83 qt_api = os.environ.get('QT_API', None)
84 if qt_api is None:
84 if qt_api is None:
85 #no ETS variable. Ask mpl, then use default fallback path
85 #no ETS variable. Ask mpl, then use default fallback path
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE, QT_API_PYQT5]
86 return matplotlib_options(mpl) or [QT_API_PYQT_DEFAULT, QT_API_PYSIDE,
87 QT_API_PYQT5, QT_API_PYSIDE2]
87 elif qt_api not in _qt_apis:
88 elif qt_api not in _qt_apis:
88 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
89 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
89 (qt_api, ', '.join(_qt_apis)))
90 (qt_api, ', '.join(_qt_apis)))
@@ -20,6 +20,15 b" QT_API_PYQT5 = 'pyqt5'"
20 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
20 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
21 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
21 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
22 QT_API_PYSIDE = 'pyside'
22 QT_API_PYSIDE = 'pyside'
23 QT_API_PYSIDE2 = 'pyside2'
24
25 api_to_module = {QT_API_PYSIDE2: 'PySide2',
26 QT_API_PYSIDE: 'PySide',
27 QT_API_PYQT: 'PyQt4',
28 QT_API_PYQTv1: 'PyQt4',
29 QT_API_PYQT5: 'PyQt5',
30 QT_API_PYQT_DEFAULT: 'PyQt4',
31 }
23
32
24
33
25 class ImportDenier(object):
34 class ImportDenier(object):
@@ -54,14 +63,21 b' def commit_api(api):'
54 """Commit to a particular API, and trigger ImportErrors on subsequent
63 """Commit to a particular API, and trigger ImportErrors on subsequent
55 dangerous imports"""
64 dangerous imports"""
56
65
66 if api == QT_API_PYSIDE2:
67 ID.forbid('PySide')
68 ID.forbid('PyQt4')
69 ID.forbid('PyQt5')
57 if api == QT_API_PYSIDE:
70 if api == QT_API_PYSIDE:
71 ID.forbid('PySide2')
58 ID.forbid('PyQt4')
72 ID.forbid('PyQt4')
59 ID.forbid('PyQt5')
73 ID.forbid('PyQt5')
60 elif api == QT_API_PYQT5:
74 elif api == QT_API_PYQT5:
75 ID.forbid('PySide2')
61 ID.forbid('PySide')
76 ID.forbid('PySide')
62 ID.forbid('PyQt4')
77 ID.forbid('PyQt4')
63 else: # There are three other possibilities, all representing PyQt4
78 else: # There are three other possibilities, all representing PyQt4
64 ID.forbid('PyQt5')
79 ID.forbid('PyQt5')
80 ID.forbid('PySide2')
65 ID.forbid('PySide')
81 ID.forbid('PySide')
66
82
67
83
@@ -73,7 +89,7 b' def loaded_api():'
73
89
74 Returns
90 Returns
75 -------
91 -------
76 None, 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
92 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
77 """
93 """
78 if 'PyQt4.QtCore' in sys.modules:
94 if 'PyQt4.QtCore' in sys.modules:
79 if qtapi_version() == 2:
95 if qtapi_version() == 2:
@@ -82,18 +98,21 b' def loaded_api():'
82 return QT_API_PYQTv1
98 return QT_API_PYQTv1
83 elif 'PySide.QtCore' in sys.modules:
99 elif 'PySide.QtCore' in sys.modules:
84 return QT_API_PYSIDE
100 return QT_API_PYSIDE
101 elif 'PySide2.QtCore' in sys.modules:
102 return QT_API_PYSIDE2
85 elif 'PyQt5.QtCore' in sys.modules:
103 elif 'PyQt5.QtCore' in sys.modules:
86 return QT_API_PYQT5
104 return QT_API_PYQT5
87 return None
105 return None
88
106
89
107
90 def has_binding(api):
108 def has_binding(api):
91 """Safely check for PyQt4/5 or PySide, without importing
109 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
92 submodules
110
111 Supports Python <= 3.3
93
112
94 Parameters
113 Parameters
95 ----------
114 ----------
96 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
115 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
97 Which module to check for
116 Which module to check for
98
117
99 Returns
118 Returns
@@ -103,12 +122,7 b' def has_binding(api):'
103 # we can't import an incomplete pyside and pyqt4
122 # we can't import an incomplete pyside and pyqt4
104 # this will cause a crash in sip (#1431)
123 # this will cause a crash in sip (#1431)
105 # check for complete presence before importing
124 # check for complete presence before importing
106 module_name = {QT_API_PYSIDE: 'PySide',
125 module_name = api_to_module[api]
107 QT_API_PYQT: 'PyQt4',
108 QT_API_PYQTv1: 'PyQt4',
109 QT_API_PYQT5: 'PyQt5',
110 QT_API_PYQT_DEFAULT: 'PyQt4'}
111 module_name = module_name[api]
112
126
113 import imp
127 import imp
114 try:
128 try:
@@ -118,7 +132,7 b' def has_binding(api):'
118 imp.find_module('QtCore', mod.__path__)
132 imp.find_module('QtCore', mod.__path__)
119 imp.find_module('QtGui', mod.__path__)
133 imp.find_module('QtGui', mod.__path__)
120 imp.find_module('QtSvg', mod.__path__)
134 imp.find_module('QtSvg', mod.__path__)
121 if api == QT_API_PYQT5:
135 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
122 # QT5 requires QtWidgets too
136 # QT5 requires QtWidgets too
123 imp.find_module('QtWidgets', mod.__path__)
137 imp.find_module('QtWidgets', mod.__path__)
124
138
@@ -130,6 +144,48 b' def has_binding(api):'
130 except ImportError:
144 except ImportError:
131 return False
145 return False
132
146
147 def has_binding_new(api):
148 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
149
150 Supports Python >= 3.4
151
152 Parameters
153 ----------
154 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
155 Which module to check for
156
157 Returns
158 -------
159 True if the relevant module appears to be importable
160 """
161 module_name = api_to_module[api]
162 from importlib.util import find_spec
163
164 required = ['QtCore', 'QtGui', 'QtSvg']
165 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
166 # QT5 requires QtWidgets too
167 required.append('QtWidgets')
168
169 for submod in required:
170 try:
171 spec = find_spec('%s.%s' % (module_name, submod))
172 except ImportError:
173 # Package (e.g. PyQt5) not found
174 return False
175 else:
176 if spec is None:
177 # Submodule (e.g. PyQt5.QtCore) not found
178 return False
179
180 if api == QT_API_PYSIDE:
181 # We can also safely check PySide version
182 import PySide
183 return check_version(PySide.__version__, '1.0.3')
184
185 return True
186
187 if sys.version_info >= (3, 4):
188 has_binding = has_binding_new
133
189
134 def qtapi_version():
190 def qtapi_version():
135 """Return which QString API has been set, if any
191 """Return which QString API has been set, if any
@@ -229,6 +285,22 b' def import_pyside():'
229 from PySide import QtGui, QtCore, QtSvg
285 from PySide import QtGui, QtCore, QtSvg
230 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
286 return QtCore, QtGui, QtSvg, QT_API_PYSIDE
231
287
288 def import_pyside2():
289 """
290 Import PySide2
291
292 ImportErrors raised within this function are non-recoverable
293 """
294 from PySide2 import QtGui, QtCore, QtSvg, QtWidgets, QtPrintSupport
295
296 # Join QtGui and QtWidgets for Qt4 compatibility.
297 QtGuiCompat = types.ModuleType('QtGuiCompat')
298 QtGuiCompat.__dict__.update(QtGui.__dict__)
299 QtGuiCompat.__dict__.update(QtWidgets.__dict__)
300 QtGuiCompat.__dict__.update(QtPrintSupport.__dict__)
301
302 return QtCore, QtGuiCompat, QtSvg, QT_API_PYSIDE2
303
232
304
233 def load_qt(api_options):
305 def load_qt(api_options):
234 """
306 """
@@ -240,7 +312,7 b' def load_qt(api_options):'
240 Parameters
312 Parameters
241 ----------
313 ----------
242 api_options: List of strings
314 api_options: List of strings
243 The order of APIs to try. Valid items are 'pyside',
315 The order of APIs to try. Valid items are 'pyside', 'pyside2',
244 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
316 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
245
317
246 Returns
318 Returns
@@ -256,12 +328,14 b' def load_qt(api_options):'
256 bindings (either becaues they aren't installed, or because
328 bindings (either becaues they aren't installed, or because
257 an incompatible library has already been installed)
329 an incompatible library has already been installed)
258 """
330 """
259 loaders = {QT_API_PYSIDE: import_pyside,
331 loaders = {
332 QT_API_PYSIDE2: import_pyside2,
333 QT_API_PYSIDE: import_pyside,
260 QT_API_PYQT: import_pyqt4,
334 QT_API_PYQT: import_pyqt4,
261 QT_API_PYQT5: import_pyqt5,
335 QT_API_PYQT5: import_pyqt5,
262 QT_API_PYQTv1: partial(import_pyqt4, version=1),
336 QT_API_PYQTv1: partial(import_pyqt4, version=1),
263 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
337 QT_API_PYQT_DEFAULT: partial(import_pyqt4, version=None)
264 }
338 }
265
339
266 for api in api_options:
340 for api in api_options:
267
341
@@ -281,16 +355,18 b' def load_qt(api_options):'
281 else:
355 else:
282 raise ImportError("""
356 raise ImportError("""
283 Could not load requested Qt binding. Please ensure that
357 Could not load requested Qt binding. Please ensure that
284 PyQt4 >= 4.7, PyQt5 or PySide >= 1.0.3 is available,
358 PyQt4 >= 4.7, PyQt5, PySide >= 1.0.3 or PySide2 is available,
285 and only one is imported per session.
359 and only one is imported per session.
286
360
287 Currently-imported Qt library: %r
361 Currently-imported Qt library: %r
288 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
362 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
289 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
363 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
290 PySide >= 1.0.3 installed: %s
364 PySide >= 1.0.3 installed: %s
365 PySide2 installed: %s
291 Tried to load: %r
366 Tried to load: %r
292 """ % (loaded_api(),
367 """ % (loaded_api(),
293 has_binding(QT_API_PYQT),
368 has_binding(QT_API_PYQT),
294 has_binding(QT_API_PYQT5),
369 has_binding(QT_API_PYQT5),
295 has_binding(QT_API_PYSIDE),
370 has_binding(QT_API_PYSIDE),
371 has_binding(QT_API_PYSIDE2),
296 api_options))
372 api_options))
General Comments 0
You need to be logged in to leave comments. Login now