##// END OF EJS Templates
add check_version utility...
MinRK -
Show More
@@ -1,81 +1,81 b''
1 """ A Qt API selector that can be used to switch between PyQt and PySide.
1 """ A Qt API selector that can be used to switch between PyQt and PySide.
2
2
3 This uses the ETS 4.0 selection pattern of:
3 This uses the ETS 4.0 selection pattern of:
4 PySide first, PyQt with API v2. second.
4 PySide first, PyQt with API v2. second.
5
5
6 Do not use this if you need PyQt with the old QString/QVariant API.
6 Do not use this if you need PyQt with the old QString/QVariant API.
7 """
7 """
8
8
9 import os
9 import os
10 from IPython.utils.version import NumericalVersion as V
10 from IPython.utils.version import check_version
11 # Available APIs.
11 # Available APIs.
12 QT_API_PYQT = 'pyqt'
12 QT_API_PYQT = 'pyqt'
13 QT_API_PYSIDE = 'pyside'
13 QT_API_PYSIDE = 'pyside'
14
14
15 def prepare_pyqt4():
15 def prepare_pyqt4():
16 # For PySide compatibility, use the new-style string API that automatically
16 # For PySide compatibility, use the new-style string API that automatically
17 # converts QStrings to Unicode Python strings. Also, automatically unpack
17 # converts QStrings to Unicode Python strings. Also, automatically unpack
18 # QVariants to their underlying objects.
18 # QVariants to their underlying objects.
19 import sip
19 import sip
20 sip.setapi('QString', 2)
20 sip.setapi('QString', 2)
21 sip.setapi('QVariant', 2)
21 sip.setapi('QVariant', 2)
22
22
23 # Select Qt binding, using the QT_API environment variable if available.
23 # Select Qt binding, using the QT_API environment variable if available.
24 QT_API = os.environ.get('QT_API')
24 QT_API = os.environ.get('QT_API')
25 if QT_API is None:
25 if QT_API is None:
26 pyside_found = False
26 pyside_found = False
27 try:
27 try:
28 import PySide
28 import PySide
29 if V(PySide.__version__) < V('1.0.3'):
29 if not check_version(PySide.__version__, '1.0.3'):
30 # old PySide, fallback on PyQt
30 # old PySide, fallback on PyQt
31 raise ImportError
31 raise ImportError
32 # we can't import an incomplete pyside and pyqt4
32 # we can't import an incomplete pyside and pyqt4
33 # this will cause a crash in sip (#1431)
33 # this will cause a crash in sip (#1431)
34 # check for complete presence before importing
34 # check for complete presence before importing
35 import imp
35 import imp
36 imp.find_module("QtCore", PySide.__path__)
36 imp.find_module("QtCore", PySide.__path__)
37 imp.find_module("QtGui", PySide.__path__)
37 imp.find_module("QtGui", PySide.__path__)
38 imp.find_module("QtSvg", PySide.__path__)
38 imp.find_module("QtSvg", PySide.__path__)
39 pyside_found = True
39 pyside_found = True
40 from PySide import QtCore, QtGui, QtSvg
40 from PySide import QtCore, QtGui, QtSvg
41 QT_API = QT_API_PYSIDE
41 QT_API = QT_API_PYSIDE
42 except ImportError:
42 except ImportError:
43 try:
43 try:
44 prepare_pyqt4()
44 prepare_pyqt4()
45 import PyQt4
45 import PyQt4
46 from PyQt4 import QtCore, QtGui, QtSvg
46 from PyQt4 import QtCore, QtGui, QtSvg
47 if pyside_found:
47 if pyside_found:
48 print "WARNING: PySide installation incomplete and PyQt4 " \
48 print "WARNING: PySide installation incomplete and PyQt4 " \
49 "present.\nThis will likely crash, please install " \
49 "present.\nThis will likely crash, please install " \
50 "PySide completely, remove PySide or PyQt4 or set " \
50 "PySide completely, remove PySide or PyQt4 or set " \
51 "the QT_API environment variable to pyqt or pyside"
51 "the QT_API environment variable to pyqt or pyside"
52 if V(QtCore.PYQT_VERSION_STR) < V('4.7'):
52 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
53 # PyQt 4.6 has issues with null strings returning as None
53 # PyQt 4.6 has issues with null strings returning as None
54 raise ImportError
54 raise ImportError
55 QT_API = QT_API_PYQT
55 QT_API = QT_API_PYQT
56 except ImportError:
56 except ImportError:
57 raise ImportError('Cannot import PySide >= 1.0.3 or PyQt4 >= 4.7')
57 raise ImportError('Cannot import PySide >= 1.0.3 or PyQt4 >= 4.7')
58
58
59 elif QT_API == QT_API_PYQT:
59 elif QT_API == QT_API_PYQT:
60 # Note: This must be called *before* PyQt4 is imported.
60 # Note: This must be called *before* PyQt4 is imported.
61 prepare_pyqt4()
61 prepare_pyqt4()
62
62
63 # Now peform the imports.
63 # Now peform the imports.
64 if QT_API == QT_API_PYQT:
64 if QT_API == QT_API_PYQT:
65 from PyQt4 import QtCore, QtGui, QtSvg
65 from PyQt4 import QtCore, QtGui, QtSvg
66 if V(QtCore.PYQT_VERSION_STR) < V('4.7'):
66 if not check_version(QtCore.PYQT_VERSION_STR, '4.7'):
67 raise ImportError("IPython requires PyQt4 >= 4.7, found %s"%QtCore.PYQT_VERSION_STR)
67 raise ImportError("IPython requires PyQt4 >= 4.7, found %s"%QtCore.PYQT_VERSION_STR)
68
68
69 # Alias PyQt-specific functions for PySide compatibility.
69 # Alias PyQt-specific functions for PySide compatibility.
70 QtCore.Signal = QtCore.pyqtSignal
70 QtCore.Signal = QtCore.pyqtSignal
71 QtCore.Slot = QtCore.pyqtSlot
71 QtCore.Slot = QtCore.pyqtSlot
72
72
73 elif QT_API == QT_API_PYSIDE:
73 elif QT_API == QT_API_PYSIDE:
74 import PySide
74 import PySide
75 if V(PySide.__version__) < V('1.0.3'):
75 if not check_version(PySide.__version__, '1.0.3'):
76 raise ImportError("IPython requires PySide >= 1.0.3, found %s"%PySide.__version__)
76 raise ImportError("IPython requires PySide >= 1.0.3, found %s"%PySide.__version__)
77 from PySide import QtCore, QtGui, QtSvg
77 from PySide import QtCore, QtGui, QtSvg
78
78
79 else:
79 else:
80 raise RuntimeError('Invalid Qt API %r, valid values are: %r or %r' %
80 raise RuntimeError('Invalid Qt API %r, valid values are: %r or %r' %
81 (QT_API, QT_API_PYQT, QT_API_PYSIDE))
81 (QT_API, QT_API_PYQT, QT_API_PYSIDE))
@@ -1,88 +1,88 b''
1 """ Import Qt in a manner suitable for an IPython kernel.
1 """ Import Qt in a manner suitable for an IPython kernel.
2
2
3 This is the import used for the `gui=qt` or `pylab=qt` initialization.
3 This is the import used for the `gui=qt` or `pylab=qt` initialization.
4
4
5 Import Priority:
5 Import Priority:
6
6
7 if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
7 if matplotlib has been imported and doesn't support v2 (<= 1.0.1):
8 use PyQt4 @v1
8 use PyQt4 @v1
9
9
10 Next, ask ETS' QT_API env variable
10 Next, ask ETS' QT_API env variable
11
11
12 if QT_API not set:
12 if QT_API not set:
13 ask matplotlib via rcParams['backend.qt4']
13 ask matplotlib via rcParams['backend.qt4']
14 if it said PyQt:
14 if it said PyQt:
15 use PyQt4 @v1
15 use PyQt4 @v1
16 elif it said PySide:
16 elif it said PySide:
17 use PySide
17 use PySide
18
18
19 else: (matplotlib said nothing)
19 else: (matplotlib said nothing)
20 # this is the default path - nobody told us anything
20 # this is the default path - nobody told us anything
21 try:
21 try:
22 PyQt @v1
22 PyQt @v1
23 except:
23 except:
24 fallback on PySide
24 fallback on PySide
25 else:
25 else:
26 use PyQt @v2 or PySide, depending on QT_API
26 use PyQt @v2 or PySide, depending on QT_API
27 because ETS doesn't work with PyQt @v1.
27 because ETS doesn't work with PyQt @v1.
28
28
29 """
29 """
30
30
31 import os
31 import os
32 import sys
32 import sys
33
33
34 from IPython.utils.warn import warn
34 from IPython.utils.warn import warn
35 from IPython.utils.version import NumericalVersion as V
35 from IPython.utils.version import check_version
36
36
37 matplotlib = sys.modules.get('matplotlib')
37 matplotlib = sys.modules.get('matplotlib')
38 if matplotlib and V(matplotlib.__version__) <= V('1.0.1'):
38 if matplotlib and not check_version(matplotlib.__version__, '1.0.2'):
39 # 1.0.1 doesn't support pyside or v2, so stick with PyQt @v1,
39 # 1.0.1 doesn't support pyside or v2, so stick with PyQt @v1,
40 # and ignore everything else
40 # and ignore everything else
41 from PyQt4 import QtCore, QtGui
41 from PyQt4 import QtCore, QtGui
42 else:
42 else:
43 # ask QT_API ETS variable *first*
43 # ask QT_API ETS variable *first*
44 QT_API = os.environ.get('QT_API', None)
44 QT_API = os.environ.get('QT_API', None)
45 if QT_API is None:
45 if QT_API is None:
46 # QT_API not set, ask matplotlib if it was imported (e.g. `pylab=qt`)
46 # QT_API not set, ask matplotlib if it was imported (e.g. `pylab=qt`)
47 if matplotlib:
47 if matplotlib:
48 mpqt = matplotlib.rcParams.get('backend.qt4', None)
48 mpqt = matplotlib.rcParams.get('backend.qt4', None)
49 else:
49 else:
50 mpqt = None
50 mpqt = None
51 if mpqt is None:
51 if mpqt is None:
52 # matplotlib not imported or had nothing to say.
52 # matplotlib not imported or had nothing to say.
53 try:
53 try:
54 # default to unconfigured PyQt4
54 # default to unconfigured PyQt4
55 from PyQt4 import QtCore, QtGui
55 from PyQt4 import QtCore, QtGui
56 except ImportError:
56 except ImportError:
57 # fallback on PySide
57 # fallback on PySide
58 try:
58 try:
59 from PySide import QtCore, QtGui
59 from PySide import QtCore, QtGui
60 except ImportError:
60 except ImportError:
61 raise ImportError('Cannot import PySide or PyQt4')
61 raise ImportError('Cannot import PySide or PyQt4')
62 elif mpqt.lower() == 'pyqt4':
62 elif mpqt.lower() == 'pyqt4':
63 # import PyQt4 unconfigured
63 # import PyQt4 unconfigured
64 from PyQt4 import QtCore, QtGui
64 from PyQt4 import QtCore, QtGui
65 elif mpqt.lower() == 'pyside':
65 elif mpqt.lower() == 'pyside':
66 from PySide import QtCore, QtGui
66 from PySide import QtCore, QtGui
67 else:
67 else:
68 raise ImportError("unhandled value for backend.qt4 from matplotlib: %r"%mpqt)
68 raise ImportError("unhandled value for backend.qt4 from matplotlib: %r"%mpqt)
69 else:
69 else:
70 # QT_API specified, use PySide or PyQt+v2 API from external.qt
70 # QT_API specified, use PySide or PyQt+v2 API from external.qt
71 # this means ETS is likely to be used, which requires v2
71 # this means ETS is likely to be used, which requires v2
72 try:
72 try:
73 from IPython.external.qt import QtCore, QtGui
73 from IPython.external.qt import QtCore, QtGui
74 except ValueError as e:
74 except ValueError as e:
75 if 'API' in str(e):
75 if 'API' in str(e):
76 # PyQt4 already imported, and APIv2 couldn't be set
76 # PyQt4 already imported, and APIv2 couldn't be set
77 # Give more meaningful message, and warn instead of raising
77 # Give more meaningful message, and warn instead of raising
78 warn("""
78 warn("""
79 Assigning the ETS variable `QT_API=pyqt` implies PyQt's v2 API for
79 Assigning the ETS variable `QT_API=pyqt` implies PyQt's v2 API for
80 QString and QVariant, but PyQt has already been imported
80 QString and QVariant, but PyQt has already been imported
81 with v1 APIs. You should unset QT_API to work with PyQt4
81 with v1 APIs. You should unset QT_API to work with PyQt4
82 in its default mode.
82 in its default mode.
83 """)
83 """)
84 # allow it to still work
84 # allow it to still work
85 from PyQt4 import QtCore, QtGui
85 from PyQt4 import QtCore, QtGui
86 else:
86 else:
87 raise
87 raise
88
88
@@ -1,56 +1,69 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Utilities for version comparison
3 Utilities for version comparison
4
4
5 It is a bit ridiculous that we need these.
5 It is a bit ridiculous that we need these.
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
9 # Copyright (C) 2013 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 from distutils.version import LooseVersion
19 from distutils.version import LooseVersion
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Code
22 # Code
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class NumericalVersion(LooseVersion):
25 class NumericalVersion(LooseVersion):
26 """A version of LooseVersion that is *always* comparable
26 """A version of LooseVersion that is *always* comparable
27
27
28 String elements (of any kind!) are interpreted as infinite.
28 String elements (of any kind!) are interpreted as infinite.
29 Since these are generally only on development branches,
29 Since these are generally only on development branches,
30 that is fairly safe, even though technically 1.0a1 should be less than 1.0.
30 that is fairly safe, even though technically 1.0a1 should be less than 1.0.
31 """
31 """
32 def parse (self, vstring):
32 def parse (self, vstring):
33 # I've given up on thinking I can reconstruct the version string
33 # I've given up on thinking I can reconstruct the version string
34 # from the parsed tuple -- so I just store the string here for
34 # from the parsed tuple -- so I just store the string here for
35 # use by __str__
35 # use by __str__
36 self.vstring = vstring
36 self.vstring = vstring
37 components = filter(lambda x: x and x != '.',
37 components = filter(lambda x: x and x != '.',
38 self.component_re.split(vstring))
38 self.component_re.split(vstring))
39 for i in range(len(components)):
39 for i in range(len(components)):
40 try:
40 try:
41 components[i] = int(components[i])
41 components[i] = int(components[i])
42 except ValueError:
42 except ValueError:
43 # this is the only change
43 # this is the only change
44 components[i] = float('inf')
44 components[i] = float('inf')
45
45
46 self.version = components
46 self.version = components
47
47
48 def version_tuple(vs):
48 def version_tuple(vs):
49 """Return a tuple of numbers from a version string.
49 """Return a tuple of numbers from a version string.
50
50
51 'dev' 'a', 'etc' are transformed to float('inf'),
51 'dev' 'a', 'etc' are transformed to float('inf'),
52 so they will always compare positively to any
52 so they will always compare positively to any
53 regular integer element.
53 regular integer element.
54 """
54 """
55 return tuple(NumericalVersion(vs).version)
55 return tuple(NumericalVersion(vs).version)
56
56
57 #
58 def check_version(v, check):
59 """check version string v >= check
60
61 If dev/prerelease tags result in TypeError for string-number comparison,
62 it is assumed that the dependency is satisfied.
63 Users on dev branches are responsible for keeping their own packages up to date.
64 """
65 try:
66 return LooseVersion(v) >= LooseVersion(check)
67 except TypeError:
68 return True
69 No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now