##// 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 34 from IPython.utils.version import check_version
35 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 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 40 QT_API_PYQT_DEFAULT)
41 41
42 42 #Constraints placed on an imported matplotlib
@@ -83,7 +83,8 b' def get_options():'
83 83 qt_api = os.environ.get('QT_API', None)
84 84 if qt_api is None:
85 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 88 elif qt_api not in _qt_apis:
88 89 raise RuntimeError("Invalid Qt API %r, valid values are: %r" %
89 90 (qt_api, ', '.join(_qt_apis)))
@@ -20,6 +20,15 b" QT_API_PYQT5 = 'pyqt5'"
20 20 QT_API_PYQTv1 = 'pyqtv1' # Force version 2
21 21 QT_API_PYQT_DEFAULT = 'pyqtdefault' # use system default for version 1 vs. 2
22 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 34 class ImportDenier(object):
@@ -54,14 +63,21 b' def commit_api(api):'
54 63 """Commit to a particular API, and trigger ImportErrors on subsequent
55 64 dangerous imports"""
56 65
66 if api == QT_API_PYSIDE2:
67 ID.forbid('PySide')
68 ID.forbid('PyQt4')
69 ID.forbid('PyQt5')
57 70 if api == QT_API_PYSIDE:
71 ID.forbid('PySide2')
58 72 ID.forbid('PyQt4')
59 73 ID.forbid('PyQt5')
60 74 elif api == QT_API_PYQT5:
75 ID.forbid('PySide2')
61 76 ID.forbid('PySide')
62 77 ID.forbid('PyQt4')
63 78 else: # There are three other possibilities, all representing PyQt4
64 79 ID.forbid('PyQt5')
80 ID.forbid('PySide2')
65 81 ID.forbid('PySide')
66 82
67 83
@@ -73,7 +89,7 b' def loaded_api():'
73 89
74 90 Returns
75 91 -------
76 None, 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
92 None, 'pyside2', 'pyside', 'pyqt', 'pyqt5', or 'pyqtv1'
77 93 """
78 94 if 'PyQt4.QtCore' in sys.modules:
79 95 if qtapi_version() == 2:
@@ -82,18 +98,21 b' def loaded_api():'
82 98 return QT_API_PYQTv1
83 99 elif 'PySide.QtCore' in sys.modules:
84 100 return QT_API_PYSIDE
101 elif 'PySide2.QtCore' in sys.modules:
102 return QT_API_PYSIDE2
85 103 elif 'PyQt5.QtCore' in sys.modules:
86 104 return QT_API_PYQT5
87 105 return None
88 106
89 107
90 108 def has_binding(api):
91 """Safely check for PyQt4/5 or PySide, without importing
92 submodules
109 """Safely check for PyQt4/5, PySide or PySide2, without importing submodules
110
111 Supports Python <= 3.3
93 112
94 113 Parameters
95 114 ----------
96 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyqtdefault']
115 api : str [ 'pyqtv1' | 'pyqt' | 'pyqt5' | 'pyside' | 'pyside2' | 'pyqtdefault']
97 116 Which module to check for
98 117
99 118 Returns
@@ -103,12 +122,7 b' def has_binding(api):'
103 122 # we can't import an incomplete pyside and pyqt4
104 123 # this will cause a crash in sip (#1431)
105 124 # check for complete presence before importing
106 module_name = {QT_API_PYSIDE: 'PySide',
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]
125 module_name = api_to_module[api]
112 126
113 127 import imp
114 128 try:
@@ -118,7 +132,7 b' def has_binding(api):'
118 132 imp.find_module('QtCore', mod.__path__)
119 133 imp.find_module('QtGui', mod.__path__)
120 134 imp.find_module('QtSvg', mod.__path__)
121 if api == QT_API_PYQT5:
135 if api in (QT_API_PYQT5, QT_API_PYSIDE2):
122 136 # QT5 requires QtWidgets too
123 137 imp.find_module('QtWidgets', mod.__path__)
124 138
@@ -130,6 +144,48 b' def has_binding(api):'
130 144 except ImportError:
131 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 190 def qtapi_version():
135 191 """Return which QString API has been set, if any
@@ -229,6 +285,22 b' def import_pyside():'
229 285 from PySide import QtGui, QtCore, QtSvg
230 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 305 def load_qt(api_options):
234 306 """
@@ -240,7 +312,7 b' def load_qt(api_options):'
240 312 Parameters
241 313 ----------
242 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 316 'pyqt', 'pyqt5', 'pyqtv1' and 'pyqtdefault'
245 317
246 318 Returns
@@ -256,7 +328,9 b' def load_qt(api_options):'
256 328 bindings (either becaues they aren't installed, or because
257 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 334 QT_API_PYQT: import_pyqt4,
261 335 QT_API_PYQT5: import_pyqt5,
262 336 QT_API_PYQTv1: partial(import_pyqt4, version=1),
@@ -281,16 +355,18 b' def load_qt(api_options):'
281 355 else:
282 356 raise ImportError("""
283 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 359 and only one is imported per session.
286 360
287 361 Currently-imported Qt library: %r
288 362 PyQt4 available (requires QtCore, QtGui, QtSvg): %s
289 363 PyQt5 available (requires QtCore, QtGui, QtSvg, QtWidgets): %s
290 364 PySide >= 1.0.3 installed: %s
365 PySide2 installed: %s
291 366 Tried to load: %r
292 367 """ % (loaded_api(),
293 368 has_binding(QT_API_PYQT),
294 369 has_binding(QT_API_PYQT5),
295 370 has_binding(QT_API_PYSIDE),
371 has_binding(QT_API_PYSIDE2),
296 372 api_options))
General Comments 0
You need to be logged in to leave comments. Login now