Skip to content

Commit 557a7f2

Browse files
authored
Merge pull request #1159 from effigies/mnt/5.0.0-deprecations
MNT: 5.0.0 deprecations
2 parents 943e5e8 + bc45f47 commit 557a7f2

11 files changed

+77
-56
lines changed

nibabel/deprecator.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,18 @@ def deprecated_func(*args, **kwargs):
182182
warnings.warn(message, warn_class, stacklevel=2)
183183
return func(*args, **kwargs)
184184

185-
deprecated_func.__doc__ = _add_dep_doc(deprecated_func.__doc__,
186-
message, TESTSETUP, TESTCLEANUP)
185+
keep_doc = deprecated_func.__doc__
186+
setup = TESTSETUP
187+
cleanup = TESTCLEANUP
188+
# After expiration, remove all but the first paragraph.
189+
# The details are no longer relevant, but any code will likely
190+
# raise exceptions we don't need.
191+
if keep_doc and until and self.is_bad_version(until):
192+
lines = '\n'.join(line.rstrip() for line in keep_doc.splitlines())
193+
keep_doc = lines.split('\n\n', 1)[0]
194+
setup = ''
195+
cleanup = ''
196+
deprecated_func.__doc__ = _add_dep_doc(keep_doc, message, setup, cleanup)
187197
return deprecated_func
188198

189199
return deprecator

nibabel/testing/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import unittest
1818

19+
import pytest
1920
import numpy as np
2021
from numpy.testing import assert_array_equal
2122

@@ -223,3 +224,15 @@ def setUp(self):
223224
if self.__class__.__name__.startswith('_'):
224225
raise unittest.SkipTest("Base test case - subclass to run")
225226
super().setUp()
227+
228+
229+
def expires(version):
230+
"Decorator to mark a test as xfail with ExpiredDeprecationError after version"
231+
from packaging.version import Version
232+
from nibabel import __version__ as nbver
233+
from nibabel.deprecator import ExpiredDeprecationError
234+
235+
if Version(nbver) < Version(version):
236+
return lambda x: x
237+
238+
return pytest.mark.xfail(raises=ExpiredDeprecationError)

nibabel/tests/test_analyze.py

+1-9
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ def test_default_header(self):
716716

717717
def test_data_hdr_cache(self):
718718
# test the API for loaded images, such that the data returned
719-
# from np.asanyarray(img.dataobj) and img,get_fdata() are not
719+
# from np.asanyarray(img.dataobj) and img.get_fdata() are not
720720
# affected by subsequent changes to the header.
721721
IC = self.image_class
722722
# save an image to a file map
@@ -740,14 +740,6 @@ def test_data_hdr_cache(self):
740740
assert hdr.get_data_dtype() == np.dtype(np.uint8)
741741
assert_array_equal(img2.get_fdata(), data)
742742
assert_array_equal(np.asanyarray(img2.dataobj), data)
743-
# now check read_img_data function - here we do see the changed
744-
# header
745-
with pytest.deprecated_call(match="from version: 3.2"):
746-
sc_data = read_img_data(img2)
747-
assert sc_data.shape == (3, 2, 2)
748-
with pytest.deprecated_call(match="from version: 3.2"):
749-
us_data = read_img_data(img2, prefer='unscaled')
750-
assert us_data.shape == (3, 2, 2)
751743

752744
def test_affine_44(self):
753745
IC = self.image_class

nibabel/tests/test_api_validators.py

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ def meth(self):
2020
validator(self, imaker, params)
2121
meth.__name__ = 'test_' + name[len('validate_'):]
2222
meth.__doc__ = f'autogenerated test from {klass.__name__}.{name}'
23+
if hasattr(validator, 'pytestmark'):
24+
meth.pytestmark = validator.pytestmark
2325
return meth
2426
for name in dir(klass):
2527
if not name.startswith('validate_'):

nibabel/tests/test_deprecator.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,14 @@ def test_dep_func(self):
111111
'foo\n\n* deprecated from version: 1.2\n* Raises '
112112
f'{ExpiredDeprecationError} as of version: 1.8\n')
113113
func = dec('foo', '1.2', '1.8')(func_doc_long)
114-
assert (func.__doc__ ==
115-
'A docstring\n \n foo\n \n * deprecated from version: 1.2\n '
116-
f'* Raises {ExpiredDeprecationError} as of version: 1.8\n \n'
117-
f'{indent(TESTSETUP, " ", lambda x: True)}'
118-
f' Some text\n{indent(TESTCLEANUP, " ", lambda x: True)}')
114+
assert func.__doc__ == f"""\
115+
A docstring
116+
117+
foo
118+
119+
* deprecated from version: 1.2
120+
* Raises {ExpiredDeprecationError} as of version: 1.8
121+
"""
119122
with pytest.raises(ExpiredDeprecationError):
120123
func()
121124

nibabel/tests/test_image_api.py

+5-26
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848

4949
from numpy.testing import assert_almost_equal, assert_array_equal, assert_warns, assert_allclose
5050
from nibabel.testing import (bytesio_round_trip, bytesio_filemap, assert_data_similar,
51-
clear_and_catch_warnings, nullcontext)
51+
clear_and_catch_warnings, nullcontext, expires)
5252
from ..tmpdirs import InTemporaryDirectory
5353

5454
from .test_api_validators import ValidateAPI
@@ -170,8 +170,8 @@ def validate_no_slicing(self, imaker, params):
170170
with pytest.raises(TypeError):
171171
img[:]
172172

173+
@expires("5.0.0")
173174
def validate_get_data_deprecated(self, imaker, params):
174-
# Check deprecated header API
175175
img = imaker()
176176
with pytest.deprecated_call():
177177
data = img.get_data()
@@ -209,7 +209,7 @@ class DataInterfaceMixin(GetSetDtypeMixin):
209209
Use this mixin if your image has a ``dataobj`` property that contains an
210210
array or an array-like thing.
211211
"""
212-
meth_names = ('get_fdata', 'get_data')
212+
meth_names = ('get_fdata',)
213213

214214
def validate_data_interface(self, imaker, params):
215215
# Check get data returns array, and caches
@@ -304,27 +304,6 @@ def _check_proxy_interface(self, imaker, meth_name):
304304
with maybe_deprecated(meth_name):
305305
data_again = method()
306306
assert data is data_again
307-
# Check the interaction of caching with get_data, get_fdata.
308-
# Caching for `get_data` should have no effect on caching for
309-
# get_fdata, and vice versa.
310-
# Modify the cached data
311-
data[:] = 43
312-
# Load using the other data fetch method
313-
other_name = set(self.meth_names).difference({meth_name}).pop()
314-
other_method = getattr(img, other_name)
315-
with maybe_deprecated(other_name):
316-
other_data = other_method()
317-
# We get the original data, not the modified cache
318-
assert_array_equal(proxy_data, other_data)
319-
assert not np.all(data == other_data)
320-
# We can modify the other cache, without affecting the first
321-
other_data[:] = 44
322-
with maybe_deprecated(other_name):
323-
assert_array_equal(other_method(), 44)
324-
with pytest.deprecated_call():
325-
assert not np.all(method() == other_method())
326-
if meth_name != 'get_fdata':
327-
return
328307
# Check that caching refreshes for new floating point type.
329308
img.uncache()
330309
fdata = img.get_fdata()
@@ -558,7 +537,7 @@ def validate_to_from_bytes(self, imaker, params):
558537
del img_b
559538

560539
@pytest.fixture(autouse=True)
561-
def setup(self, httpserver, tmp_path):
540+
def setup_method(self, httpserver, tmp_path):
562541
"""Make pytest fixtures available to validate functions"""
563542
self.httpserver = httpserver
564543
self.tmp_path = tmp_path
@@ -788,7 +767,7 @@ class TestMinc1API(ImageHeaderAPI):
788767

789768
class TestMinc2API(TestMinc1API):
790769

791-
def setup(self):
770+
def setup_method(self):
792771
if not have_h5py:
793772
raise unittest.SkipTest('Need h5py for these tests')
794773

nibabel/tests/test_image_load_save.py

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from ..volumeutils import native_code, swapped_code
3030
from ..optpkg import optional_package
3131
from ..spatialimages import SpatialImage
32+
from ..testing import expires
3233

3334
from numpy.testing import assert_array_equal, assert_array_almost_equal
3435
import pytest
@@ -270,6 +271,7 @@ def test_filename_save():
270271
shutil.rmtree(pth)
271272

272273

274+
@expires('5.0.0')
273275
def test_guessed_image_type():
274276
# Test whether we can guess the image type from example files
275277
with pytest.deprecated_call():

nibabel/tests/test_loadsave.py

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ..filebasedimages import ImageFileError
1515
from ..tmpdirs import InTemporaryDirectory, TemporaryDirectory
1616
from ..openers import Opener
17+
from ..testing import expires
1718

1819
from ..optpkg import optional_package
1920
_, have_scipy, _ = optional_package('scipy')
@@ -27,6 +28,7 @@
2728
data_path = pjoin(dirname(__file__), 'data')
2829

2930

31+
@expires("5.0.0")
3032
def test_read_img_data():
3133
fnames_test = [
3234
'example4d.nii.gz',
@@ -120,6 +122,7 @@ def test_signature_matches_extension(tmp_path):
120122
assert msg == ""
121123

122124

125+
@expires("5.0.0")
123126
def test_read_img_data_nifti():
124127
shape = (2, 3, 4)
125128
data = np.random.normal(size=shape)

nibabel/tests/test_onetime.py

+16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import pytest
22
from nibabel.onetime import auto_attr, setattr_on_read
3+
from nibabel.testing import expires
34

45

6+
@expires('5.0.0')
57
def test_setattr_on_read():
68
with pytest.deprecated_call():
79
class MagicProp:
@@ -15,3 +17,17 @@ def a(self):
1517
assert 'a' in x.__dict__
1618
# Each call to object() produces a unique object. Verify we get the same one every time.
1719
assert x.a is obj
20+
21+
22+
def test_auto_attr():
23+
class MagicProp:
24+
@auto_attr
25+
def a(self):
26+
return object()
27+
28+
x = MagicProp()
29+
assert 'a' not in x.__dict__
30+
obj = x.a
31+
assert 'a' in x.__dict__
32+
# Each call to object() produces a unique object. Verify we get the same one every time.
33+
assert x.a is obj

nibabel/tests/test_orientations.py

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
ornt2axcodes, axcodes2ornt, aff2axcodes)
2121

2222
from ..affines import from_matvec, to_matvec
23+
from ..testing import expires
2324

2425

2526
IN_ARRS = [np.eye(4),
@@ -353,6 +354,7 @@ def test_inv_ornt_aff():
353354
inv_ornt_aff([[0, 1], [1, -1], [np.nan, np.nan]], (3, 4, 5))
354355

355356

357+
@expires('5.0.0')
356358
def test_flip_axis_deprecation():
357359
a = np.arange(24).reshape((2, 3, 4))
358360
axis = 1

nibabel/tests/test_spatialimages.py

+13-14
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
bytesio_round_trip,
2727
clear_and_catch_warnings,
2828
suppress_warnings,
29-
memmap_after_ufunc
29+
memmap_after_ufunc,
30+
expires,
3031
)
3132

3233
from ..tmpdirs import InTemporaryDirectory
@@ -358,21 +359,13 @@ def test_get_fdata(self):
358359
assert rt_img.get_fdata() is not out_data
359360
assert (rt_img.get_fdata() == in_data).all()
360361

362+
@expires("5.0.0")
361363
def test_get_data(self):
362364
# Test array image and proxy image interface
363365
img_klass = self.image_class
364366
in_data_template = np.arange(24, dtype=np.int16).reshape((2, 3, 4))
365367
in_data = in_data_template.copy()
366368
img = img_klass(in_data, None)
367-
# Can't slice into the image object:
368-
with pytest.raises(TypeError) as exception_manager:
369-
img[0, 0, 0]
370-
# Make sure the right message gets raised:
371-
assert (str(exception_manager.value) ==
372-
"Cannot slice image objects; consider using "
373-
"`img.slicer[slice]` to generate a sliced image (see "
374-
"documentation for caveats) or slicing image array data "
375-
"with `img.dataobj[slice]` or `img.get_fdata()[slice]`")
376369
assert in_data is img.dataobj
377370
with pytest.deprecated_call():
378371
out_data = img.get_data()
@@ -411,6 +404,16 @@ def test_slicer(self):
411404
in_data = in_data_template.copy().reshape(dshape)
412405
img = img_klass(in_data, base_affine.copy())
413406

407+
# Can't slice into the image object:
408+
with pytest.raises(TypeError) as exception_manager:
409+
img[0, 0, 0]
410+
# Make sure the right message gets raised:
411+
assert (str(exception_manager.value) ==
412+
"Cannot slice image objects; consider using "
413+
"`img.slicer[slice]` to generate a sliced image (see "
414+
"documentation for caveats) or slicing image array data "
415+
"with `img.dataobj[slice]` or `img.get_fdata()[slice]`")
416+
414417
if not spatial_axes_first(img):
415418
with pytest.raises(ValueError):
416419
img.slicer
@@ -519,13 +522,9 @@ def test_slicer(self):
519522
pass
520523
else:
521524
sliced_data = in_data[sliceobj]
522-
with pytest.deprecated_call():
523-
assert (sliced_data == sliced_img.get_data()).all()
524525
assert (sliced_data == sliced_img.get_fdata()).all()
525526
assert (sliced_data == sliced_img.dataobj).all()
526527
assert (sliced_data == img.dataobj[sliceobj]).all()
527-
with pytest.deprecated_call():
528-
assert (sliced_data == img.get_data()[sliceobj]).all()
529528
assert (sliced_data == img.get_fdata()[sliceobj]).all()
530529

531530

0 commit comments

Comments
 (0)