summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2014-05-14 15:34:53 -0600
committerCharles Harris <charlesr.harris@gmail.com>2014-05-15 01:13:22 -0600
commit62e4561f20ff681d321765d1083d3fde8db9a9f2 (patch)
treed20e62d4b7b17a04e298799e01feeb53450751be
parent13b32e9d157a0ce62c0aa7d1447ad53fbb23d930 (diff)
downloadnumpy-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.rst22
-rw-r--r--numpy/lib/__init__.py1
-rw-r--r--numpy/lib/_version.py155
-rw-r--r--numpy/lib/tests/test__version.py57
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()