Chris@87: """Utility to compare (Numpy) version strings. Chris@87: Chris@87: The NumpyVersion class allows properly comparing numpy version strings. Chris@87: The LooseVersion and StrictVersion classes that distutils provides don't Chris@87: work; they don't recognize anything like alpha/beta/rc/dev versions. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import re Chris@87: Chris@87: from numpy.compat import basestring Chris@87: Chris@87: Chris@87: __all__ = ['NumpyVersion'] Chris@87: Chris@87: Chris@87: class NumpyVersion(): Chris@87: """Parse and compare numpy version strings. Chris@87: Chris@87: Numpy has the following versioning scheme (numbers given are examples; they Chris@87: can be > 9) in principle): Chris@87: Chris@87: - Released version: '1.8.0', '1.8.1', etc. Chris@87: - Alpha: '1.8.0a1', '1.8.0a2', etc. Chris@87: - Beta: '1.8.0b1', '1.8.0b2', etc. Chris@87: - Release candidates: '1.8.0rc1', '1.8.0rc2', etc. Chris@87: - Development versions: '1.8.0.dev-f1234afa' (git commit hash appended) Chris@87: - Development versions after a1: '1.8.0a1.dev-f1234afa', Chris@87: '1.8.0b2.dev-f1234afa', Chris@87: '1.8.1rc1.dev-f1234afa', etc. Chris@87: - Development versions (no git hash available): '1.8.0.dev-Unknown' Chris@87: Chris@87: Comparing needs to be done against a valid version string or other Chris@87: `NumpyVersion` instance. Note that all development versions of the same Chris@87: (pre-)release compare equal. Chris@87: Chris@87: .. versionadded:: 1.9.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: vstring : str Chris@87: Numpy version string (``np.__version__``). Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> from numpy.lib import NumpyVersion Chris@87: >>> if NumpyVersion(np.__version__) < '1.7.0'): Chris@87: ... print('skip') Chris@87: skip Chris@87: Chris@87: >>> NumpyVersion('1.7') # raises ValueError, add ".0" Chris@87: Chris@87: """ Chris@87: Chris@87: def __init__(self, vstring): Chris@87: self.vstring = vstring Chris@87: ver_main = re.match(r'\d[.]\d+[.]\d+', vstring) Chris@87: if not ver_main: Chris@87: raise ValueError("Not a valid numpy version string") Chris@87: Chris@87: self.version = ver_main.group() Chris@87: self.major, self.minor, self.bugfix = [int(x) for x in Chris@87: self.version.split('.')] Chris@87: if len(vstring) == ver_main.end(): Chris@87: self.pre_release = 'final' Chris@87: else: Chris@87: alpha = re.match(r'a\d', vstring[ver_main.end():]) Chris@87: beta = re.match(r'b\d', vstring[ver_main.end():]) Chris@87: rc = re.match(r'rc\d', vstring[ver_main.end():]) Chris@87: pre_rel = [m for m in [alpha, beta, rc] if m is not None] Chris@87: if pre_rel: Chris@87: self.pre_release = pre_rel[0].group() Chris@87: else: Chris@87: self.pre_release = '' Chris@87: Chris@87: self.is_devversion = bool(re.search(r'.dev', vstring)) Chris@87: Chris@87: def _compare_version(self, other): Chris@87: """Compare major.minor.bugfix""" Chris@87: if self.major == other.major: Chris@87: if self.minor == other.minor: Chris@87: if self.bugfix == other.bugfix: Chris@87: vercmp = 0 Chris@87: elif self.bugfix > other.bugfix: Chris@87: vercmp = 1 Chris@87: else: Chris@87: vercmp = -1 Chris@87: elif self.minor > other.minor: Chris@87: vercmp = 1 Chris@87: else: Chris@87: vercmp = -1 Chris@87: elif self.major > other.major: Chris@87: vercmp = 1 Chris@87: else: Chris@87: vercmp = -1 Chris@87: Chris@87: return vercmp Chris@87: Chris@87: def _compare_pre_release(self, other): Chris@87: """Compare alpha/beta/rc/final.""" Chris@87: if self.pre_release == other.pre_release: Chris@87: vercmp = 0 Chris@87: elif self.pre_release == 'final': Chris@87: vercmp = 1 Chris@87: elif other.pre_release == 'final': Chris@87: vercmp = -1 Chris@87: elif self.pre_release > other.pre_release: Chris@87: vercmp = 1 Chris@87: else: Chris@87: vercmp = -1 Chris@87: Chris@87: return vercmp Chris@87: Chris@87: def _compare(self, other): Chris@87: if not isinstance(other, (basestring, NumpyVersion)): Chris@87: raise ValueError("Invalid object to compare with NumpyVersion.") Chris@87: Chris@87: if isinstance(other, basestring): Chris@87: other = NumpyVersion(other) Chris@87: Chris@87: vercmp = self._compare_version(other) Chris@87: if vercmp == 0: Chris@87: # Same x.y.z version, check for alpha/beta/rc Chris@87: vercmp = self._compare_pre_release(other) Chris@87: if vercmp == 0: Chris@87: # Same version and same pre-release, check if dev version Chris@87: if self.is_devversion is other.is_devversion: Chris@87: vercmp = 0 Chris@87: elif self.is_devversion: Chris@87: vercmp = -1 Chris@87: else: Chris@87: vercmp = 1 Chris@87: Chris@87: return vercmp Chris@87: Chris@87: def __lt__(self, other): Chris@87: return self._compare(other) < 0 Chris@87: Chris@87: def __le__(self, other): Chris@87: return self._compare(other) <= 0 Chris@87: Chris@87: def __eq__(self, other): Chris@87: return self._compare(other) == 0 Chris@87: Chris@87: def __ne__(self, other): Chris@87: return self._compare(other) != 0 Chris@87: Chris@87: def __gt__(self, other): Chris@87: return self._compare(other) > 0 Chris@87: Chris@87: def __ge__(self, other): Chris@87: return self._compare(other) >= 0 Chris@87: Chris@87: def __repr(self): Chris@87: return "NumpyVersion(%s)" % self.vstring