Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Validate ExpiredDeprecationErrors #857

Merged
merged 3 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions nibabel/pkg_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@ def _cmp(a, b):


def cmp_pkg_version(version_str, pkg_version_str=__version__):
""" Compare `version_str` to current package version
""" Compare ``version_str`` to current package version

To be valid, a version must have a numerical major version followed by a
dot, followed by a numerical minor version. It may optionally be followed
by a dot and a numerical micro version, and / or by an "extra" string.
*Any* extra string labels the version as pre-release, so `1.2.0somestring`
compares as prior to (pre-release for) `1.2.0`, where `somestring` can be
any string.
The extra string may further contain a "+". Any value to the left of a "+"
labels the version as pre-release, while values to the right indicate a
post-release relative to the values to the left. That is,
``1.2.0+1`` is post-release for ``1.2.0``, while ``1.2.0rc1+1`` is
post-release for ``1.2.0rc1`` and pre-release for ``1.2.0``.

This is an approximation of `PEP-440`_, and future versions will fully
implement PEP-440.

Parameters
----------
Expand All @@ -50,15 +55,38 @@ def cmp_pkg_version(version_str, pkg_version_str=__version__):
1
>>> cmp_pkg_version('1.2.0dev', '1.2.0')
-1
>>> cmp_pkg_version('1.2.0dev', '1.2.0rc1')
-1
>>> cmp_pkg_version('1.2.0rc1', '1.2.0')
-1
>>> cmp_pkg_version('1.2.0rc1+1', '1.2.0rc1')
1
>>> cmp_pkg_version('1.2.0rc1+1', '1.2.0')
-1

.. _`PEP-440`: https://www.python.org/dev/peps/pep-0440/
"""
version, extra = _parse_version(version_str)
pkg_version, pkg_extra = _parse_version(pkg_version_str)
if version != pkg_version:
return _cmp(StrictVersion(version), StrictVersion(pkg_version))
return (0 if extra == pkg_extra
else 1 if extra == ''
else -1 if pkg_extra == ''
else _cmp(extra, pkg_extra))

# Normalize versions
quick_check = _cmp(StrictVersion(version), StrictVersion(pkg_version))
# Nothing further to check
if quick_check != 0 or extra == pkg_extra == '':
return quick_check

# Before + is pre-release, after + is additional increment
pre, _, post = extra.partition('+')
pkg_pre, _, pkg_post = pkg_extra.partition('+')
quick_check = _cmp(pre, pkg_pre)
if quick_check != 0: # Excludes case where pre and pkg_pre == ''
# Pre-releases are ordered but strictly less than non-pre
return (1 if pre == ''
else -1 if pkg_pre == ''
else quick_check)

# All else being equal, compare additional information lexically
return _cmp(post, pkg_post)


def pkg_commit_hash(pkg_path=None):
Expand Down
7 changes: 0 additions & 7 deletions nibabel/tests/test_arrayproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ def test_tuplespec():
):
assert_array_equal(getattr(ap_header, method)(*args),
getattr(ap_tuple, method)(*args))
# Tuple-defined ArrayProxies have no header to store
with warnings.catch_warnings():
assert_true(ap_tuple.header is None)
# Partial tuples of length 2-4 are also valid
for n in range(2, 5):
ArrayProxy(bio, tuple_spec[:n])
Expand All @@ -141,10 +138,6 @@ def test_nifti1_init():
ap = ArrayProxy(bio, hdr)
assert_true(ap.file_like == bio)
assert_equal(ap.shape, shape)
# Check there has been a copy of the header
with warnings.catch_warnings():
warnings.simplefilter("ignore")
assert_false(ap.header is hdr)
# Get the data
assert_array_equal(np.asarray(ap), arr * 2.0 + 10)
with InTemporaryDirectory():
Expand Down
7 changes: 3 additions & 4 deletions nibabel/tests/test_image_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from numpy.testing import assert_almost_equal, assert_array_equal, assert_warns, assert_allclose
from ..testing import clear_and_catch_warnings
from ..tmpdirs import InTemporaryDirectory
from ..deprecator import ExpiredDeprecationError

from .test_api_validators import ValidateAPI
from .test_helpers import (bytesio_round_trip, bytesio_filemap,
Expand Down Expand Up @@ -422,10 +423,8 @@ def validate_ndim(self, imaker, params):
def validate_shape_deprecated(self, imaker, params):
# Check deprecated get_shape API
img = imaker()
with clear_and_catch_warnings() as w:
warnings.simplefilter('always', DeprecationWarning)
assert_equal(img.get_shape(), params['shape'])
assert_equal(len(w), 1)
with assert_raises(ExpiredDeprecationError):
img.get_shape()

def validate_mmap_parameter(self, imaker, params):
img = imaker()
Expand Down
26 changes: 9 additions & 17 deletions nibabel/tests/test_minc1.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from ..tmpdirs import InTemporaryDirectory
from ..testing import (assert_true, assert_equal, assert_false, assert_raises, assert_warns,
assert_array_equal, data_path, clear_and_catch_warnings)
from ..deprecator import ExpiredDeprecationError

from . import test_spatialimages as tsi
from .test_fileslice import slicer_samples
Expand Down Expand Up @@ -105,25 +106,16 @@ def test_old_namespace():
arr = np.arange(24).reshape((2, 3, 4))
aff = np.diag([2, 3, 4, 1])

with clear_and_catch_warnings() as warns:
from .. import Minc1Image, MincImage
assert_equal(warns, [])
# But the old named import, imported from new, is not the same
assert_false(Minc1Image is MincImage)
assert_equal(warns, [])
# Create object using old name
mimg = MincImage(arr, aff)
# Call to create object created warning
assert_equal(warns.pop(0).category, FutureWarning)
assert_array_equal(mimg.get_fdata(), arr)
# Another old name
from ..minc1 import MincFile, Minc1File
assert_false(MincFile is Minc1File)
from .. import Minc1Image, MincImage
assert_false(Minc1Image is MincImage)
with assert_raises(ExpiredDeprecationError):
MincImage(arr, aff)
assert_equal(warns, [])
# Another old name
from ..minc1 import MincFile, Minc1File
assert_false(MincFile is Minc1File)
with assert_raises(ExpiredDeprecationError):
mf = MincFile(netcdf_file(EG_FNAME))
# Call to create object created warning
assert_equal(warns.pop(0).category, FutureWarning)
assert_equal(mf.get_data_shape(), (10, 20, 20))


class _TestMincFile(object):
Expand Down
8 changes: 8 additions & 0 deletions nibabel/tests/test_pkg_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ def test_cmp_pkg_version():
('1.2.1rc', '1.2.1rc1', -1),
('1.2.1b', '1.2.1a', 1),
('1.2.1a', '1.2.1b', -1),
('1.2.0+1', '1.2', 1),
('1.2', '1.2.0+1', -1),
('1.2.1+1', '1.2.1', 1),
('1.2.1', '1.2.1+1', -1),
('1.2.1rc1+1', '1.2.1', -1),
('1.2.1', '1.2.1rc1+1', 1),
('1.2.1rc1+1', '1.2.1+1', -1),
('1.2.1+1', '1.2.1rc1+1', 1),
):
assert_equal(cmp_pkg_version(test_ver, pkg_ver), exp_out)
assert_raises(ValueError, cmp_pkg_version, 'foo.2')
Expand Down
9 changes: 3 additions & 6 deletions nibabel/tests/test_proxy_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from numpy.testing import assert_almost_equal, assert_array_equal, assert_allclose

from ..testing import data_path as DATA_PATH, assert_dt_equal, clear_and_catch_warnings
from ..deprecator import ExpiredDeprecationError

from ..tmpdirs import InTemporaryDirectory

Expand Down Expand Up @@ -324,12 +325,8 @@ def validate_slope_inter_offset(self, pmaker, params):

def validate_deprecated_header(self, pmaker, params):
prox, fio, hdr = pmaker()
with warnings.catch_warnings(record=True) as warns:
warnings.simplefilter("always")
# Header is a copy of original
assert_false(prox.header is hdr)
assert_equal(prox.header, hdr)
assert_equal(warns.pop(0).category, DeprecationWarning)
with assert_raises(ExpiredDeprecationError):
prox.header


class TestSpm99AnalyzeProxyAPI(TestAnalyzeProxyAPI):
Expand Down
81 changes: 6 additions & 75 deletions nibabel/tests/test_scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
import numpy as np

from io import BytesIO
from ..volumeutils import (calculate_scale, scale_min_max, finite_range,
apply_read_scaling, array_to_file, array_from_file)
from ..volumeutils import finite_range, apply_read_scaling, array_to_file, array_from_file
from ..casting import type_info
from ..testing import suppress_warnings

from .test_volumeutils import _calculate_scale

from numpy.testing import (assert_array_almost_equal, assert_array_equal)

from nose.tools import (assert_true, assert_equal, assert_raises,
Expand All @@ -26,56 +27,6 @@
DEBUG = True


def test_scale_min_max():
mx_dt = np.maximum_sctype(np.float)
for tp in np.sctypes['uint'] + np.sctypes['int']:
info = np.iinfo(tp)
# Need to pump up to max fp type to contain python longs
imin = np.array(info.min, dtype=mx_dt)
imax = np.array(info.max, dtype=mx_dt)
value_pairs = (
(0, imax),
(imin, 0),
(imin, imax),
(1, 10),
(-1, -1),
(1, 1),
(-10, -1),
(-100, 10))
for mn, mx in value_pairs:
# with intercept
scale, inter = scale_min_max(mn, mx, tp, True)
if mx - mn:
assert_array_almost_equal, (mx - inter) / scale, imax
assert_array_almost_equal, (mn - inter) / scale, imin
else:
assert_equal, (scale, inter), (1.0, mn)
# without intercept
if imin == 0 and mn < 0 and mx > 0:
(assert_raises, ValueError,
scale_min_max, mn, mx, tp, False)
continue
scale, inter = scale_min_max(mn, mx, tp, False)
assert_equal, inter, 0.0
if mn == 0 and mx == 0:
assert_equal, scale, 1.0
continue
sc_mn = mn / scale
sc_mx = mx / scale
assert_true, sc_mn >= imin
assert_true, sc_mx <= imax
if imin == 0:
if mx > 0: # numbers all +ve
assert_array_almost_equal, mx / scale, imax
else: # numbers all -ve
assert_array_almost_equal, mn / scale, imax
continue
if abs(mx) >= abs(mn):
assert_array_almost_equal, mx / scale, imax
else:
assert_array_almost_equal, mn / scale, imin


def test_finite_range():
# Finite range utility function
for in_arr, res in (
Expand Down Expand Up @@ -122,26 +73,6 @@ def test_finite_range():
assert_raises(TypeError, finite_range, a)


def test_calculate_scale():
# Test for special cases in scale calculation
npa = np.array
# Here the offset handles it
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, True)
assert_equal(res, (1.0, -2.0, None, None))
# Not having offset not a problem obviously
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, 0)
assert_equal(res, (-1.0, 0.0, None, None))
# Case where offset handles scaling
res = calculate_scale(npa([-1, 1], dtype=np.int8), np.uint8, 1)
assert_equal(res, (1.0, -1.0, None, None))
# Can't work for no offset case
assert_raises(ValueError,
calculate_scale, npa([-1, 1], dtype=np.int8), np.uint8, 0)
# Offset trick can't work when max is out of range
res = calculate_scale(npa([-1, 255], dtype=np.int16), np.uint8, 1)
assert_not_equal(res, (1.0, -1.0, None, None))


def test_a2f_mn_mx():
# Test array to file mn, mx handling
str_io = BytesIO()
Expand Down Expand Up @@ -213,9 +144,9 @@ def test_array_file_scales():
info = type_info(in_type)
arr[0], arr[1] = info['min'], info['max']
if not err is None:
assert_raises(err, calculate_scale, arr, out_dtype, True)
assert_raises(err, _calculate_scale, arr, out_dtype, True)
continue
slope, inter, mn, mx = calculate_scale(arr, out_dtype, True)
slope, inter, mn, mx = _calculate_scale(arr, out_dtype, True)
array_to_file(arr, bio, out_type, 0, inter, slope, mn, mx)
bio.seek(0)
arr2 = array_from_file(arr.shape, out_dtype, bio)
Expand Down Expand Up @@ -266,7 +197,7 @@ def check_int_a2f(in_type, out_type):
data[1] = this_max + 0j
str_io = BytesIO()
try:
scale, inter, mn, mx = calculate_scale(data, out_type, True)
scale, inter, mn, mx = _calculate_scale(data, out_type, True)
except ValueError as e:
if DEBUG:
print(in_type, out_type, e)
Expand Down
33 changes: 12 additions & 21 deletions nibabel/tests/test_spatialimages.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from ..testing import (clear_and_catch_warnings, suppress_warnings,
memmap_after_ufunc)
from ..tmpdirs import InTemporaryDirectory
from ..deprecator import ExpiredDeprecationError
from .. import load as top_load


Expand Down Expand Up @@ -284,8 +285,8 @@ def test_data_shape(self):
img = img_klass(arr, np.eye(4))
# Shape may be promoted to higher dimension, but may not reorder or
# change size
assert_equal(img.get_shape()[:1], (4,))
assert_equal(np.prod(img.get_shape()), 4)
assert_equal(img.shape[:1], (4,))
assert_equal(np.prod(img.shape), 4)
img = img_klass(np.zeros((2, 3, 4), dtype=np.float32), np.eye(4))
assert_equal(img.shape, (2, 3, 4))

Expand All @@ -305,19 +306,13 @@ def test_str(self):
assert_true(len(str(img)) > 0)

def test_get_shape(self):
# Check there is a get_shape method
# (it is deprecated)
# Check that get_shape raises an ExpiredDeprecationError
img_klass = self.image_class
# Assumes all possible images support int16
# See https://github.com/nipy/nibabel/issues/58
img = img_klass(np.arange(1, dtype=np.int16), np.eye(4))
with suppress_warnings():
# Shape may be promoted to higher dimension, but may not reorder or
# change size
assert_equal(img.get_shape()[:1], (1,))
assert_equal(np.prod(img.get_shape()), 1)
img = img_klass(np.zeros((2, 3, 4), np.int16), np.eye(4))
assert_equal(img.get_shape(), (2, 3, 4))
with assert_raises(ExpiredDeprecationError):
img.get_shape()

def test_get_fdata(self):
# Test array image and proxy image interface for floating point data
Expand Down Expand Up @@ -568,18 +563,14 @@ def from_file_map(self, file_map=None):
bio = BytesIO()
file_map = FakeImage.make_file_map({'image': bio})

with clear_and_catch_warnings() as w:
warnings.simplefilter('always', DeprecationWarning)
with assert_raises(ExpiredDeprecationError):
img.to_files(file_map)
assert_equal(len(w), 1)
with assert_raises(ExpiredDeprecationError):
img.to_filespec('an_image')
assert_equal(len(w), 2)
img = FakeImage.from_files(file_map)
assert_equal(len(w), 3)
file_map = FakeImage.filespec_to_files('an_image')
assert_equal(list(file_map), ['image'])
assert_equal(file_map['image'].filename, 'an_image.foo')
assert_equal(len(w), 4)
with assert_raises(ExpiredDeprecationError):
FakeImage.from_files(file_map)
with assert_raises(ExpiredDeprecationError):
FakeImage.filespec_to_files('an_image')


class MmapImageMixin(object):
Expand Down
Loading