1"""Utility to compare (NumPy) version strings.

2

3 The NumpyVersion class allows properly comparing numpy version strings.

4 The LooseVersion and StrictVersion classes that distutils provides don't

5 work; they don't recognize anything like alpha/beta/rc/dev versions.

6

7"""

8 import re

9

10

11__all__ = ['NumpyVersion']

12

13

14 class NumpyVersion():

15 """Parse and compare numpy version strings.

16

17 NumPy has the following versioning scheme (numbers given are examples; they

18 can be > 9 in principle):

19

20 - Released version: '1.8.0', '1.8.1', etc.

21 - Alpha: '1.8.0a1', '1.8.0a2', etc.

22 - Beta: '1.8.0b1', '1.8.0b2', etc.

23 - Release candidates: '1.8.0rc1', '1.8.0rc2', etc.

24 - Development versions: '1.8.0.dev-f1234afa' (git commit hash appended)

25 - Development versions after a1: '1.8.0a1.dev-f1234afa',

26 '1.8.0b2.dev-f1234afa',

27 '1.8.1rc1.dev-f1234afa', etc.

28 - Development versions (no git hash available): '1.8.0.dev-Unknown'

29

30 Comparing needs to be done against a valid version string or other

31 `NumpyVersion` instance. Note that all development versions of the same

32 (pre-)release compare equal.

33

34 .. versionadded:: 1.9.0

35

36 Parameters

37 ----------

38 vstring : str

39 NumPy version string (``np.__version__``).

40

41 Examples

42 --------

43 >>> from numpy.lib import NumpyVersion

44 >>> if NumpyVersion(np.__version__) < '1.7.0':

45 ... print('skip')

46 >>> # skip

47

48 >>> NumpyVersion('1.7') # raises ValueError, add ".0"

49 Traceback (most recent call last):

50 ...

51 ValueError: Not a valid numpy version string

52

53 """

54

55 def __init__(self, vstring):

56 self.vstring = vstring

57 ver_main = re.match(r'\d+\.\d+\.\d+', vstring)

58 if not ver_main:

59 raise ValueError("Not a valid numpy version string")

60

61 self.version = ver_main.group()

62 self.major, self.minor, self.bugfix = [int(x) for x in

63 self.version.split('.')]

64 if len(vstring) == ver_main.end():

65 self.pre_release = 'final'

66 else:

67 alpha = re.match(r'a\d', vstring[ver_main.end():])

68 beta = re.match(r'b\d', vstring[ver_main.end():])

69 rc = re.match(r'rc\d', vstring[ver_main.end():])

70 pre_rel = [m for m in [alpha, beta, rc] if m is not None]

71 if pre_rel:

72 self.pre_release = pre_rel[0].group()

73 else:

74 self.pre_release = ''

75

76 self.is_devversion = bool(re.search(r'.dev', vstring))

77

78 def _compare_version(self, other):

79 """Compare major.minor.bugfix"""

80 if self.major == other.major:

81 if self.minor == other.minor:

82 if self.bugfix == other.bugfix:

83 vercmp = 0

84 elif self.bugfix > other.bugfix:

85 vercmp = 1

86 else:

87 vercmp = -1

88 elif self.minor > other.minor:

89 vercmp = 1

90 else:

91 vercmp = -1

92 elif self.major > other.major:

93 vercmp = 1

94 else:

95 vercmp = -1

96

97 return vercmp

98

99 def _compare_pre_release(self, other):

100 """Compare alpha/beta/rc/final."""

101 if self.pre_release == other.pre_release:

102 vercmp = 0

103 elif self.pre_release == 'final':

104 vercmp = 1

105 elif other.pre_release == 'final':

106 vercmp = -1

107 elif self.pre_release > other.pre_release:

108 vercmp = 1

109 else:

110 vercmp = -1

111

112 return vercmp

113

114 def _compare(self, other):

115 if not isinstance(other, (str, NumpyVersion)):

116 raise ValueError("Invalid object to compare with NumpyVersion.")

117

118 if isinstance(other, str):

119 other = NumpyVersion(other)

120

121 vercmp = self._compare_version(other)

122 if vercmp == 0:

123 # Same x.y.z version, check for alpha/beta/rc

124 vercmp = self._compare_pre_release(other)

125 if vercmp == 0:

126 # Same version and same pre-release, check if dev version

127 if self.is_devversion is other.is_devversion:

128 vercmp = 0

129 elif self.is_devversion:

130 vercmp = -1

131 else:

132 vercmp = 1

133

134 return vercmp

135

136 def __lt__(self, other):

137 return self._compare(other) < 0

138

139 def __le__(self, other):

140 return self._compare(other) <= 0

141

142 def __eq__(self, other):

143 return self._compare(other) == 0

144

145 def __ne__(self, other):

146 return self._compare(other) != 0

147

148 def __gt__(self, other):

149 return self._compare(other) > 0

150

151 def __ge__(self, other):

152 return self._compare(other) >= 0

153

154 def __repr__(self):

155 return "NumpyVersion(%s)" % self.vstring