Skip to content

Commit a80cb3c

Browse files
authored
Merge pull request #857 from effigies/fix/expired_deprecations
FIX: Validate ExpiredDeprecationErrors
2 parents 5d3e817 + cabfc81 commit a80cb3c

10 files changed

+161
-187
lines changed

nibabel/pkg_info.py

+38-10
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,19 @@ def _cmp(a, b):
2222

2323

2424
def cmp_pkg_version(version_str, pkg_version_str=__version__):
25-
""" Compare `version_str` to current package version
25+
""" Compare ``version_str`` to current package version
2626
2727
To be valid, a version must have a numerical major version followed by a
2828
dot, followed by a numerical minor version. It may optionally be followed
2929
by a dot and a numerical micro version, and / or by an "extra" string.
30-
*Any* extra string labels the version as pre-release, so `1.2.0somestring`
31-
compares as prior to (pre-release for) `1.2.0`, where `somestring` can be
32-
any string.
30+
The extra string may further contain a "+". Any value to the left of a "+"
31+
labels the version as pre-release, while values to the right indicate a
32+
post-release relative to the values to the left. That is,
33+
``1.2.0+1`` is post-release for ``1.2.0``, while ``1.2.0rc1+1`` is
34+
post-release for ``1.2.0rc1`` and pre-release for ``1.2.0``.
35+
36+
This is an approximation of `PEP-440`_, and future versions will fully
37+
implement PEP-440.
3338
3439
Parameters
3540
----------
@@ -50,15 +55,38 @@ def cmp_pkg_version(version_str, pkg_version_str=__version__):
5055
1
5156
>>> cmp_pkg_version('1.2.0dev', '1.2.0')
5257
-1
58+
>>> cmp_pkg_version('1.2.0dev', '1.2.0rc1')
59+
-1
60+
>>> cmp_pkg_version('1.2.0rc1', '1.2.0')
61+
-1
62+
>>> cmp_pkg_version('1.2.0rc1+1', '1.2.0rc1')
63+
1
64+
>>> cmp_pkg_version('1.2.0rc1+1', '1.2.0')
65+
-1
66+
67+
.. _`PEP-440`: https://www.python.org/dev/peps/pep-0440/
5368
"""
5469
version, extra = _parse_version(version_str)
5570
pkg_version, pkg_extra = _parse_version(pkg_version_str)
56-
if version != pkg_version:
57-
return _cmp(StrictVersion(version), StrictVersion(pkg_version))
58-
return (0 if extra == pkg_extra
59-
else 1 if extra == ''
60-
else -1 if pkg_extra == ''
61-
else _cmp(extra, pkg_extra))
71+
72+
# Normalize versions
73+
quick_check = _cmp(StrictVersion(version), StrictVersion(pkg_version))
74+
# Nothing further to check
75+
if quick_check != 0 or extra == pkg_extra == '':
76+
return quick_check
77+
78+
# Before + is pre-release, after + is additional increment
79+
pre, _, post = extra.partition('+')
80+
pkg_pre, _, pkg_post = pkg_extra.partition('+')
81+
quick_check = _cmp(pre, pkg_pre)
82+
if quick_check != 0: # Excludes case where pre and pkg_pre == ''
83+
# Pre-releases are ordered but strictly less than non-pre
84+
return (1 if pre == ''
85+
else -1 if pkg_pre == ''
86+
else quick_check)
87+
88+
# All else being equal, compare additional information lexically
89+
return _cmp(post, pkg_post)
6290

6391

6492
def pkg_commit_hash(pkg_path=None):

nibabel/tests/test_arrayproxy.py

-7
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@ def test_tuplespec():
112112
):
113113
assert_array_equal(getattr(ap_header, method)(*args),
114114
getattr(ap_tuple, method)(*args))
115-
# Tuple-defined ArrayProxies have no header to store
116-
with warnings.catch_warnings():
117-
assert_true(ap_tuple.header is None)
118115
# Partial tuples of length 2-4 are also valid
119116
for n in range(2, 5):
120117
ArrayProxy(bio, tuple_spec[:n])
@@ -141,10 +138,6 @@ def test_nifti1_init():
141138
ap = ArrayProxy(bio, hdr)
142139
assert_true(ap.file_like == bio)
143140
assert_equal(ap.shape, shape)
144-
# Check there has been a copy of the header
145-
with warnings.catch_warnings():
146-
warnings.simplefilter("ignore")
147-
assert_false(ap.header is hdr)
148141
# Get the data
149142
assert_array_equal(np.asarray(ap), arr * 2.0 + 10)
150143
with InTemporaryDirectory():

nibabel/tests/test_image_api.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from numpy.testing import assert_almost_equal, assert_array_equal, assert_warns, assert_allclose
4848
from ..testing import clear_and_catch_warnings
4949
from ..tmpdirs import InTemporaryDirectory
50+
from ..deprecator import ExpiredDeprecationError
5051

5152
from .test_api_validators import ValidateAPI
5253
from .test_helpers import (bytesio_round_trip, bytesio_filemap,
@@ -422,10 +423,8 @@ def validate_ndim(self, imaker, params):
422423
def validate_shape_deprecated(self, imaker, params):
423424
# Check deprecated get_shape API
424425
img = imaker()
425-
with clear_and_catch_warnings() as w:
426-
warnings.simplefilter('always', DeprecationWarning)
427-
assert_equal(img.get_shape(), params['shape'])
428-
assert_equal(len(w), 1)
426+
with assert_raises(ExpiredDeprecationError):
427+
img.get_shape()
429428

430429
def validate_mmap_parameter(self, imaker, params):
431430
img = imaker()

nibabel/tests/test_minc1.py

+9-17
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from ..tmpdirs import InTemporaryDirectory
2727
from ..testing import (assert_true, assert_equal, assert_false, assert_raises, assert_warns,
2828
assert_array_equal, data_path, clear_and_catch_warnings)
29+
from ..deprecator import ExpiredDeprecationError
2930

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

108-
with clear_and_catch_warnings() as warns:
109-
from .. import Minc1Image, MincImage
110-
assert_equal(warns, [])
111-
# But the old named import, imported from new, is not the same
112-
assert_false(Minc1Image is MincImage)
113-
assert_equal(warns, [])
114-
# Create object using old name
115-
mimg = MincImage(arr, aff)
116-
# Call to create object created warning
117-
assert_equal(warns.pop(0).category, FutureWarning)
118-
assert_array_equal(mimg.get_fdata(), arr)
119-
# Another old name
120-
from ..minc1 import MincFile, Minc1File
121-
assert_false(MincFile is Minc1File)
109+
from .. import Minc1Image, MincImage
110+
assert_false(Minc1Image is MincImage)
111+
with assert_raises(ExpiredDeprecationError):
112+
MincImage(arr, aff)
122113
assert_equal(warns, [])
114+
# Another old name
115+
from ..minc1 import MincFile, Minc1File
116+
assert_false(MincFile is Minc1File)
117+
with assert_raises(ExpiredDeprecationError):
123118
mf = MincFile(netcdf_file(EG_FNAME))
124-
# Call to create object created warning
125-
assert_equal(warns.pop(0).category, FutureWarning)
126-
assert_equal(mf.get_data_shape(), (10, 20, 20))
127119

128120

129121
class _TestMincFile(object):

nibabel/tests/test_pkg_info.py

+8
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ def test_cmp_pkg_version():
6363
('1.2.1rc', '1.2.1rc1', -1),
6464
('1.2.1b', '1.2.1a', 1),
6565
('1.2.1a', '1.2.1b', -1),
66+
('1.2.0+1', '1.2', 1),
67+
('1.2', '1.2.0+1', -1),
68+
('1.2.1+1', '1.2.1', 1),
69+
('1.2.1', '1.2.1+1', -1),
70+
('1.2.1rc1+1', '1.2.1', -1),
71+
('1.2.1', '1.2.1rc1+1', 1),
72+
('1.2.1rc1+1', '1.2.1+1', -1),
73+
('1.2.1+1', '1.2.1rc1+1', 1),
6674
):
6775
assert_equal(cmp_pkg_version(test_ver, pkg_ver), exp_out)
6876
assert_raises(ValueError, cmp_pkg_version, 'foo.2')

nibabel/tests/test_proxy_api.py

+3-6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
from numpy.testing import assert_almost_equal, assert_array_equal, assert_allclose
5959

6060
from ..testing import data_path as DATA_PATH, assert_dt_equal, clear_and_catch_warnings
61+
from ..deprecator import ExpiredDeprecationError
6162

6263
from ..tmpdirs import InTemporaryDirectory
6364

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

325326
def validate_deprecated_header(self, pmaker, params):
326327
prox, fio, hdr = pmaker()
327-
with warnings.catch_warnings(record=True) as warns:
328-
warnings.simplefilter("always")
329-
# Header is a copy of original
330-
assert_false(prox.header is hdr)
331-
assert_equal(prox.header, hdr)
332-
assert_equal(warns.pop(0).category, DeprecationWarning)
328+
with assert_raises(ExpiredDeprecationError):
329+
prox.header
333330

334331

335332
class TestSpm99AnalyzeProxyAPI(TestAnalyzeProxyAPI):

nibabel/tests/test_scaling.py

+6-75
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111
import numpy as np
1212

1313
from io import BytesIO
14-
from ..volumeutils import (calculate_scale, scale_min_max, finite_range,
15-
apply_read_scaling, array_to_file, array_from_file)
14+
from ..volumeutils import finite_range, apply_read_scaling, array_to_file, array_from_file
1615
from ..casting import type_info
1716
from ..testing import suppress_warnings
1817

18+
from .test_volumeutils import _calculate_scale
19+
1920
from numpy.testing import (assert_array_almost_equal, assert_array_equal)
2021

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

2829

29-
def test_scale_min_max():
30-
mx_dt = np.maximum_sctype(np.float)
31-
for tp in np.sctypes['uint'] + np.sctypes['int']:
32-
info = np.iinfo(tp)
33-
# Need to pump up to max fp type to contain python longs
34-
imin = np.array(info.min, dtype=mx_dt)
35-
imax = np.array(info.max, dtype=mx_dt)
36-
value_pairs = (
37-
(0, imax),
38-
(imin, 0),
39-
(imin, imax),
40-
(1, 10),
41-
(-1, -1),
42-
(1, 1),
43-
(-10, -1),
44-
(-100, 10))
45-
for mn, mx in value_pairs:
46-
# with intercept
47-
scale, inter = scale_min_max(mn, mx, tp, True)
48-
if mx - mn:
49-
assert_array_almost_equal, (mx - inter) / scale, imax
50-
assert_array_almost_equal, (mn - inter) / scale, imin
51-
else:
52-
assert_equal, (scale, inter), (1.0, mn)
53-
# without intercept
54-
if imin == 0 and mn < 0 and mx > 0:
55-
(assert_raises, ValueError,
56-
scale_min_max, mn, mx, tp, False)
57-
continue
58-
scale, inter = scale_min_max(mn, mx, tp, False)
59-
assert_equal, inter, 0.0
60-
if mn == 0 and mx == 0:
61-
assert_equal, scale, 1.0
62-
continue
63-
sc_mn = mn / scale
64-
sc_mx = mx / scale
65-
assert_true, sc_mn >= imin
66-
assert_true, sc_mx <= imax
67-
if imin == 0:
68-
if mx > 0: # numbers all +ve
69-
assert_array_almost_equal, mx / scale, imax
70-
else: # numbers all -ve
71-
assert_array_almost_equal, mn / scale, imax
72-
continue
73-
if abs(mx) >= abs(mn):
74-
assert_array_almost_equal, mx / scale, imax
75-
else:
76-
assert_array_almost_equal, mn / scale, imin
77-
78-
7930
def test_finite_range():
8031
# Finite range utility function
8132
for in_arr, res in (
@@ -122,26 +73,6 @@ def test_finite_range():
12273
assert_raises(TypeError, finite_range, a)
12374

12475

125-
def test_calculate_scale():
126-
# Test for special cases in scale calculation
127-
npa = np.array
128-
# Here the offset handles it
129-
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, True)
130-
assert_equal(res, (1.0, -2.0, None, None))
131-
# Not having offset not a problem obviously
132-
res = calculate_scale(npa([-2, -1], dtype=np.int8), np.uint8, 0)
133-
assert_equal(res, (-1.0, 0.0, None, None))
134-
# Case where offset handles scaling
135-
res = calculate_scale(npa([-1, 1], dtype=np.int8), np.uint8, 1)
136-
assert_equal(res, (1.0, -1.0, None, None))
137-
# Can't work for no offset case
138-
assert_raises(ValueError,
139-
calculate_scale, npa([-1, 1], dtype=np.int8), np.uint8, 0)
140-
# Offset trick can't work when max is out of range
141-
res = calculate_scale(npa([-1, 255], dtype=np.int16), np.uint8, 1)
142-
assert_not_equal(res, (1.0, -1.0, None, None))
143-
144-
14576
def test_a2f_mn_mx():
14677
# Test array to file mn, mx handling
14778
str_io = BytesIO()
@@ -213,9 +144,9 @@ def test_array_file_scales():
213144
info = type_info(in_type)
214145
arr[0], arr[1] = info['min'], info['max']
215146
if not err is None:
216-
assert_raises(err, calculate_scale, arr, out_dtype, True)
147+
assert_raises(err, _calculate_scale, arr, out_dtype, True)
217148
continue
218-
slope, inter, mn, mx = calculate_scale(arr, out_dtype, True)
149+
slope, inter, mn, mx = _calculate_scale(arr, out_dtype, True)
219150
array_to_file(arr, bio, out_type, 0, inter, slope, mn, mx)
220151
bio.seek(0)
221152
arr2 = array_from_file(arr.shape, out_dtype, bio)
@@ -266,7 +197,7 @@ def check_int_a2f(in_type, out_type):
266197
data[1] = this_max + 0j
267198
str_io = BytesIO()
268199
try:
269-
scale, inter, mn, mx = calculate_scale(data, out_type, True)
200+
scale, inter, mn, mx = _calculate_scale(data, out_type, True)
270201
except ValueError as e:
271202
if DEBUG:
272203
print(in_type, out_type, e)

nibabel/tests/test_spatialimages.py

+12-21
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from ..testing import (clear_and_catch_warnings, suppress_warnings,
2929
memmap_after_ufunc)
3030
from ..tmpdirs import InTemporaryDirectory
31+
from ..deprecator import ExpiredDeprecationError
3132
from .. import load as top_load
3233

3334

@@ -284,8 +285,8 @@ def test_data_shape(self):
284285
img = img_klass(arr, np.eye(4))
285286
# Shape may be promoted to higher dimension, but may not reorder or
286287
# change size
287-
assert_equal(img.get_shape()[:1], (4,))
288-
assert_equal(np.prod(img.get_shape()), 4)
288+
assert_equal(img.shape[:1], (4,))
289+
assert_equal(np.prod(img.shape), 4)
289290
img = img_klass(np.zeros((2, 3, 4), dtype=np.float32), np.eye(4))
290291
assert_equal(img.shape, (2, 3, 4))
291292

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

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

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

571-
with clear_and_catch_warnings() as w:
572-
warnings.simplefilter('always', DeprecationWarning)
566+
with assert_raises(ExpiredDeprecationError):
573567
img.to_files(file_map)
574-
assert_equal(len(w), 1)
568+
with assert_raises(ExpiredDeprecationError):
575569
img.to_filespec('an_image')
576-
assert_equal(len(w), 2)
577-
img = FakeImage.from_files(file_map)
578-
assert_equal(len(w), 3)
579-
file_map = FakeImage.filespec_to_files('an_image')
580-
assert_equal(list(file_map), ['image'])
581-
assert_equal(file_map['image'].filename, 'an_image.foo')
582-
assert_equal(len(w), 4)
570+
with assert_raises(ExpiredDeprecationError):
571+
FakeImage.from_files(file_map)
572+
with assert_raises(ExpiredDeprecationError):
573+
FakeImage.filespec_to_files('an_image')
583574

584575

585576
class MmapImageMixin(object):

0 commit comments

Comments
 (0)