diff options
author | Charles Harris <charlesr.harris@gmail.com> | 2014-05-14 15:34:53 -0600 |
---|---|---|
committer | Charles Harris <charlesr.harris@gmail.com> | 2014-05-15 01:13:22 -0600 |
commit | 62e4561f20ff681d321765d1083d3fde8db9a9f2 (patch) | |
tree | d20e62d4b7b17a04e298799e01feeb53450751be | |
parent | 13b32e9d157a0ce62c0aa7d1447ad53fbb23d930 (diff) | |
download | numpy-62e4561f20ff681d321765d1083d3fde8db9a9f2.tar.gz |
ENH: Add the scipy NumpyVersion class.
The class is in numpy/lib/_version.py and can be used to compare
numpy versions when the version goes to double digits.
-rw-r--r-- | doc/release/1.9.0-notes.rst | 22 | ||||
-rw-r--r-- | numpy/lib/__init__.py | 1 | ||||
-rw-r--r-- | numpy/lib/_version.py | 155 | ||||
-rw-r--r-- | numpy/lib/tests/test__version.py | 57 |
4 files changed, 231 insertions, 4 deletions
diff --git a/doc/release/1.9.0-notes.rst b/doc/release/1.9.0-notes.rst index 9aa39342a..4ecacaf9e 100644 --- a/doc/release/1.9.0-notes.rst +++ b/doc/release/1.9.0-notes.rst @@ -17,6 +17,7 @@ Dropped Support * The numpy/testing/numpytest.py file has been removed together with the importall function it contained. + Future Changes ============== @@ -25,9 +26,11 @@ Future Changes Numpy 1.10.0. This will certainly break some code that is currently ignoring the warning. * Relaxed stride checking will be the default in 1.10.0 -* String version checks will break because, e.g., '1.9' > '1.10' is True. +* String version checks will break because, e.g., '1.9' > '1.10' is True. A + NumpyVersion class has been added that can be used for such comparisons. * The diagonal and diag functions will return writeable views in 1.10.0 + Compatibility notes =================== @@ -120,6 +123,7 @@ Applications that now fail can be fixed by masking the higher 32 bit values to zero: ``seed = seed & 0xFFFFFFFF``. This is what is done silently in older versions so the random stream remains the same. + New Features ============ @@ -178,11 +182,20 @@ this new boolean argument. The number of times each unique item comes up in the input can now be obtained as an optional return value. -Support for median in nanfunctions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Taking the median of an ``ndarray`` while ignoring the nans in an array +Support for median in nanfunctions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Taking the median of an ``ndarray`` while ignoring the nans in an array was added with the ``np.nanmedian`` function. +NumpyVersion class added +~~~~~~~~~~~~~~~~~~~~~~~~ +The class may be imported from numpy.lib and can be used for version +comparison when the numpy version goes to 1.10.devel. For example:: + + >>> from numpy.lib import NumpyVersion + >>> if NumpyVersion(np.__version__) < '1.10.0'): + ... print('Wow, that is an old NumPy version!') + Improvements ============ @@ -243,6 +256,7 @@ MaskedArray support for more complicated base classes Built-in assumptions that the baseclass behaved like a plain array are being removed. In particalur, ``repr`` and ``str`` should now work more reliably. + Changes ======= diff --git a/numpy/lib/__init__.py b/numpy/lib/__init__.py index 73e4b2306..8c420b0c3 100644 --- a/numpy/lib/__init__.py +++ b/numpy/lib/__init__.py @@ -23,6 +23,7 @@ from .npyio import * from .financial import * from .arrayterator import * from .arraypad import * +from ._version import * __all__ = ['emath', 'math'] __all__ += type_check.__all__ diff --git a/numpy/lib/_version.py b/numpy/lib/_version.py new file mode 100644 index 000000000..4eaabd0ff --- /dev/null +++ b/numpy/lib/_version.py @@ -0,0 +1,155 @@ +"""Utility to compare (Numpy) version strings. + +The NumpyVersion class allows properly comparing numpy version strings. +The LooseVersion and StrictVersion classes that distutils provides don't +work; they don't recognize anything like alpha/beta/rc/dev versions. + +""" +from __future__ import division, absolute_import, print_function + +import re + +from numpy.compat import basestring + + +__all__ = ['NumpyVersion'] + + +class NumpyVersion(): + """Parse and compare numpy version strings. + + Numpy has the following versioning scheme (numbers given are examples; they + can be > 9) in principle): + + - Released version: '1.8.0', '1.8.1', etc. + - Alpha: '1.8.0a1', '1.8.0a2', etc. + - Beta: '1.8.0b1', '1.8.0b2', etc. + - Release candidates: '1.8.0rc1', '1.8.0rc2', etc. + - Development versions: '1.8.0.dev-f1234afa' (git commit hash appended) + - Development versions after a1: '1.8.0a1.dev-f1234afa', + '1.8.0b2.dev-f1234afa', + '1.8.1rc1.dev-f1234afa', etc. + - Development versions (no git hash available): '1.8.0.dev-Unknown' + + Comparing needs to be done against a valid version string or other + `NumpyVersion` instance. Note that all development versions of the same + (pre-)release compare equal. + + .. versionadded:: 1.9.0 + + Parameters + ---------- + vstring : str + Numpy version string (``np.__version__``). + + Examples + -------- + >>> from numpy.lib import NumpyVersion + >>> if NumpyVersion(np.__version__) < '1.7.0'): + ... print('skip') + skip + + >>> NumpyVersion('1.7') # raises ValueError, add ".0" + + """ + def __init__(self, vstring): + self.vstring = vstring + ver_main = re.match(r'\d[.]\d+[.]\d+', vstring) + if not ver_main: + raise ValueError("Not a valid numpy version string") + + self.version = ver_main.group() + self.major, self.minor, self.bugfix = [int(x) for x in + self.version.split('.')] + if len(vstring) == ver_main.end(): + self.pre_release = 'final' + else: + alpha = re.match(r'a\d', vstring[ver_main.end():]) + beta = re.match(r'b\d', vstring[ver_main.end():]) + rc = re.match(r'rc\d', vstring[ver_main.end():]) + pre_rel = [m for m in [alpha, beta, rc] if m is not None] + if pre_rel: + self.pre_release = pre_rel[0].group() + else: + self.pre_release = '' + + self.is_devversion = bool(re.search(r'.dev', vstring)) + + def _compare_version(self, other): + """Compare major.minor.bugfix""" + if self.major == other.major: + if self.minor == other.minor: + if self.bugfix == other.bugfix: + vercmp = 0 + elif self.bugfix > other.bugfix: + vercmp = 1 + else: + vercmp = -1 + elif self.minor > other.minor: + vercmp = 1 + else: + vercmp = -1 + elif self.major > other.major: + vercmp = 1 + else: + vercmp = -1 + + return vercmp + + def _compare_pre_release(self, other): + """Compare alpha/beta/rc/final.""" + if self.pre_release == other.pre_release: + vercmp = 0 + elif self.pre_release == 'final': + vercmp = 1 + elif other.pre_release == 'final': + vercmp = -1 + elif self.pre_release > other.pre_release: + vercmp = 1 + else: + vercmp = -1 + + return vercmp + + def _compare(self, other): + if not isinstance(other, (basestring, NumpyVersion)): + raise ValueError("Invalid object to compare with NumpyVersion.") + + if isinstance(other, basestring): + other = NumpyVersion(other) + + vercmp = self._compare_version(other) + if vercmp == 0: + # Same x.y.z version, check for alpha/beta/rc + vercmp = self._compare_pre_release(other) + if vercmp == 0: + # Same version and same pre-release, check if dev version + if self.is_devversion is other.is_devversion: + vercmp = 0 + elif self.is_devversion: + vercmp = -1 + else: + vercmp = 1 + + return vercmp + + def __lt__(self, other): + return self._compare(other) < 0 + + def __le__(self, other): + return self._compare(other) <= 0 + + def __eq__(self, other): + return self._compare(other) == 0 + + def __ne__(self, other): + return self._compare(other) != 0 + + def __gt__(self, other): + return self._compare(other) > 0 + + def __ge__(self, other): + return self._compare(other) >= 0 + + def __repr(self): + return "NumpyVersion(%s)" % self.vstring diff --git a/numpy/lib/tests/test__version.py b/numpy/lib/tests/test__version.py new file mode 100644 index 000000000..bbafe68eb --- /dev/null +++ b/numpy/lib/tests/test__version.py @@ -0,0 +1,57 @@ +"""Tests for the NumpyVersion class. + +""" +from __future__ import division, absolute_import, print_function + +from numpy.testing import assert_, run_module_suite, assert_raises +from numpy.lib import NumpyVersion + + +def test_main_versions(): + assert_(NumpyVersion('1.8.0') == '1.8.0') + for ver in ['1.9.0', '2.0.0', '1.8.1']: + assert_(NumpyVersion('1.8.0') < ver) + + for ver in ['1.7.0', '1.7.1', '0.9.9']: + assert_(NumpyVersion('1.8.0') > ver) + + +def test_version_1_point_10(): + # regression test for gh-2998. + assert_(NumpyVersion('1.9.0') < '1.10.0') + assert_(NumpyVersion('1.11.0') < '1.11.1') + assert_(NumpyVersion('1.11.0') == '1.11.0') + assert_(NumpyVersion('1.99.11') < '1.99.12') + + +def test_alpha_beta_rc(): + assert_(NumpyVersion('1.8.0rc1') == '1.8.0rc1') + for ver in ['1.8.0', '1.8.0rc2']: + assert_(NumpyVersion('1.8.0rc1') < ver) + + for ver in ['1.8.0a2', '1.8.0b3', '1.7.2rc4']: + assert_(NumpyVersion('1.8.0rc1') > ver) + + assert_(NumpyVersion('1.8.0b1') > '1.8.0a2') + + +def test_dev_version(): + assert_(NumpyVersion('1.9.0.dev-Unknown') < '1.9.0') + for ver in ['1.9.0', '1.9.0a1', '1.9.0b2', '1.9.0b2.dev-ffffffff']: + assert_(NumpyVersion('1.9.0.dev-f16acvda') < ver) + + assert_(NumpyVersion('1.9.0.dev-f16acvda') == '1.9.0.dev-11111111') + + +def test_dev_a_b_rc_mixed(): + assert_(NumpyVersion('1.9.0a2.dev-f16acvda') == '1.9.0a2.dev-11111111') + assert_(NumpyVersion('1.9.0a2.dev-6acvda54') < '1.9.0a2') + + +def test_raises(): + for ver in ['1.9', '1,9.0', '1.7.x']: + assert_raises(ValueError, NumpyVersion, ver) + + +if __name__ == "__main__": + run_module_suite() |