Skip to content

MNT: 5.0.0 deprecations #1159

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

Merged
merged 8 commits into from
Dec 27, 2022
14 changes: 12 additions & 2 deletions nibabel/deprecator.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,18 @@ def deprecated_func(*args, **kwargs):
warnings.warn(message, warn_class, stacklevel=2)
return func(*args, **kwargs)

deprecated_func.__doc__ = _add_dep_doc(deprecated_func.__doc__,
message, TESTSETUP, TESTCLEANUP)
keep_doc = deprecated_func.__doc__
setup = TESTSETUP
cleanup = TESTCLEANUP
# After expiration, remove all but the first paragraph.
# The details are no longer relevant, but any code will likely
# raise exceptions we don't need.
if keep_doc and until and self.is_bad_version(until):
lines = '\n'.join(line.rstrip() for line in keep_doc.splitlines())
keep_doc = lines.split('\n\n', 1)[0]
setup = ''
cleanup = ''
deprecated_func.__doc__ = _add_dep_doc(keep_doc, message, setup, cleanup)
return deprecated_func

return deprecator
13 changes: 13 additions & 0 deletions nibabel/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import unittest

import pytest
import numpy as np
from numpy.testing import assert_array_equal

Expand Down Expand Up @@ -223,3 +224,15 @@ def setUp(self):
if self.__class__.__name__.startswith('_'):
raise unittest.SkipTest("Base test case - subclass to run")
super().setUp()


def expires(version):
"Decorator to mark a test as xfail with ExpiredDeprecationError after version"
from packaging.version import Version
from nibabel import __version__ as nbver
from nibabel.deprecator import ExpiredDeprecationError

if Version(nbver) < Version(version):
return lambda x: x

return pytest.mark.xfail(raises=ExpiredDeprecationError)
10 changes: 1 addition & 9 deletions nibabel/tests/test_analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ def test_default_header(self):

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

def test_affine_44(self):
IC = self.image_class
Expand Down
2 changes: 2 additions & 0 deletions nibabel/tests/test_api_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ def meth(self):
validator(self, imaker, params)
meth.__name__ = 'test_' + name[len('validate_'):]
meth.__doc__ = f'autogenerated test from {klass.__name__}.{name}'
if hasattr(validator, 'pytestmark'):
meth.pytestmark = validator.pytestmark
return meth
for name in dir(klass):
if not name.startswith('validate_'):
Expand Down
13 changes: 8 additions & 5 deletions nibabel/tests/test_deprecator.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,14 @@ def test_dep_func(self):
'foo\n\n* deprecated from version: 1.2\n* Raises '
f'{ExpiredDeprecationError} as of version: 1.8\n')
func = dec('foo', '1.2', '1.8')(func_doc_long)
assert (func.__doc__ ==
'A docstring\n \n foo\n \n * deprecated from version: 1.2\n '
f'* Raises {ExpiredDeprecationError} as of version: 1.8\n \n'
f'{indent(TESTSETUP, " ", lambda x: True)}'
f' Some text\n{indent(TESTCLEANUP, " ", lambda x: True)}')
assert func.__doc__ == f"""\
A docstring

foo

* deprecated from version: 1.2
* Raises {ExpiredDeprecationError} as of version: 1.8
"""
with pytest.raises(ExpiredDeprecationError):
func()

Expand Down
31 changes: 5 additions & 26 deletions nibabel/tests/test_image_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

from numpy.testing import assert_almost_equal, assert_array_equal, assert_warns, assert_allclose
from nibabel.testing import (bytesio_round_trip, bytesio_filemap, assert_data_similar,
clear_and_catch_warnings, nullcontext)
clear_and_catch_warnings, nullcontext, expires)
from ..tmpdirs import InTemporaryDirectory

from .test_api_validators import ValidateAPI
Expand Down Expand Up @@ -170,8 +170,8 @@ def validate_no_slicing(self, imaker, params):
with pytest.raises(TypeError):
img[:]

@expires("5.0.0")
def validate_get_data_deprecated(self, imaker, params):
# Check deprecated header API
img = imaker()
with pytest.deprecated_call():
data = img.get_data()
Expand Down Expand Up @@ -209,7 +209,7 @@ class DataInterfaceMixin(GetSetDtypeMixin):
Use this mixin if your image has a ``dataobj`` property that contains an
array or an array-like thing.
"""
meth_names = ('get_fdata', 'get_data')
meth_names = ('get_fdata',)

def validate_data_interface(self, imaker, params):
# Check get data returns array, and caches
Expand Down Expand Up @@ -304,27 +304,6 @@ def _check_proxy_interface(self, imaker, meth_name):
with maybe_deprecated(meth_name):
data_again = method()
assert data is data_again
# Check the interaction of caching with get_data, get_fdata.
# Caching for `get_data` should have no effect on caching for
# get_fdata, and vice versa.
# Modify the cached data
data[:] = 43
# Load using the other data fetch method
other_name = set(self.meth_names).difference({meth_name}).pop()
other_method = getattr(img, other_name)
with maybe_deprecated(other_name):
other_data = other_method()
# We get the original data, not the modified cache
assert_array_equal(proxy_data, other_data)
assert not np.all(data == other_data)
# We can modify the other cache, without affecting the first
other_data[:] = 44
with maybe_deprecated(other_name):
assert_array_equal(other_method(), 44)
with pytest.deprecated_call():
assert not np.all(method() == other_method())
if meth_name != 'get_fdata':
return
# Check that caching refreshes for new floating point type.
img.uncache()
fdata = img.get_fdata()
Expand Down Expand Up @@ -558,7 +537,7 @@ def validate_to_from_bytes(self, imaker, params):
del img_b

@pytest.fixture(autouse=True)
def setup(self, httpserver, tmp_path):
def setup_method(self, httpserver, tmp_path):
"""Make pytest fixtures available to validate functions"""
self.httpserver = httpserver
self.tmp_path = tmp_path
Expand Down Expand Up @@ -788,7 +767,7 @@ class TestMinc1API(ImageHeaderAPI):

class TestMinc2API(TestMinc1API):

def setup(self):
def setup_method(self):
if not have_h5py:
raise unittest.SkipTest('Need h5py for these tests')

Expand Down
2 changes: 2 additions & 0 deletions nibabel/tests/test_image_load_save.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from ..volumeutils import native_code, swapped_code
from ..optpkg import optional_package
from ..spatialimages import SpatialImage
from ..testing import expires

from numpy.testing import assert_array_equal, assert_array_almost_equal
import pytest
Expand Down Expand Up @@ -270,6 +271,7 @@ def test_filename_save():
shutil.rmtree(pth)


@expires('5.0.0')
def test_guessed_image_type():
# Test whether we can guess the image type from example files
with pytest.deprecated_call():
Expand Down
3 changes: 3 additions & 0 deletions nibabel/tests/test_loadsave.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..filebasedimages import ImageFileError
from ..tmpdirs import InTemporaryDirectory, TemporaryDirectory
from ..openers import Opener
from ..testing import expires

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


@expires("5.0.0")
def test_read_img_data():
fnames_test = [
'example4d.nii.gz',
Expand Down Expand Up @@ -120,6 +122,7 @@ def test_signature_matches_extension(tmp_path):
assert msg == ""


@expires("5.0.0")
def test_read_img_data_nifti():
shape = (2, 3, 4)
data = np.random.normal(size=shape)
Expand Down
16 changes: 16 additions & 0 deletions nibabel/tests/test_onetime.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import pytest
from nibabel.onetime import auto_attr, setattr_on_read
from nibabel.testing import expires


@expires('5.0.0')
def test_setattr_on_read():
with pytest.deprecated_call():
class MagicProp:
Expand All @@ -15,3 +17,17 @@ def a(self):
assert 'a' in x.__dict__
# Each call to object() produces a unique object. Verify we get the same one every time.
assert x.a is obj


def test_auto_attr():
class MagicProp:
@auto_attr
def a(self):
return object()

x = MagicProp()
assert 'a' not in x.__dict__
obj = x.a
assert 'a' in x.__dict__
# Each call to object() produces a unique object. Verify we get the same one every time.
assert x.a is obj
2 changes: 2 additions & 0 deletions nibabel/tests/test_orientations.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ornt2axcodes, axcodes2ornt, aff2axcodes)

from ..affines import from_matvec, to_matvec
from ..testing import expires


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


@expires('5.0.0')
def test_flip_axis_deprecation():
a = np.arange(24).reshape((2, 3, 4))
axis = 1
Expand Down
27 changes: 13 additions & 14 deletions nibabel/tests/test_spatialimages.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
bytesio_round_trip,
clear_and_catch_warnings,
suppress_warnings,
memmap_after_ufunc
memmap_after_ufunc,
expires,
)

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

@expires("5.0.0")
def test_get_data(self):
# Test array image and proxy image interface
img_klass = self.image_class
in_data_template = np.arange(24, dtype=np.int16).reshape((2, 3, 4))
in_data = in_data_template.copy()
img = img_klass(in_data, None)
# Can't slice into the image object:
with pytest.raises(TypeError) as exception_manager:
img[0, 0, 0]
# Make sure the right message gets raised:
assert (str(exception_manager.value) ==
"Cannot slice image objects; consider using "
"`img.slicer[slice]` to generate a sliced image (see "
"documentation for caveats) or slicing image array data "
"with `img.dataobj[slice]` or `img.get_fdata()[slice]`")
assert in_data is img.dataobj
with pytest.deprecated_call():
out_data = img.get_data()
Expand Down Expand Up @@ -411,6 +404,16 @@ def test_slicer(self):
in_data = in_data_template.copy().reshape(dshape)
img = img_klass(in_data, base_affine.copy())

# Can't slice into the image object:
with pytest.raises(TypeError) as exception_manager:
img[0, 0, 0]
# Make sure the right message gets raised:
assert (str(exception_manager.value) ==
"Cannot slice image objects; consider using "
"`img.slicer[slice]` to generate a sliced image (see "
"documentation for caveats) or slicing image array data "
"with `img.dataobj[slice]` or `img.get_fdata()[slice]`")

if not spatial_axes_first(img):
with pytest.raises(ValueError):
img.slicer
Expand Down Expand Up @@ -519,13 +522,9 @@ def test_slicer(self):
pass
else:
sliced_data = in_data[sliceobj]
with pytest.deprecated_call():
assert (sliced_data == sliced_img.get_data()).all()
assert (sliced_data == sliced_img.get_fdata()).all()
assert (sliced_data == sliced_img.dataobj).all()
assert (sliced_data == img.dataobj[sliceobj]).all()
with pytest.deprecated_call():
assert (sliced_data == img.get_data()[sliceobj]).all()
assert (sliced_data == img.get_fdata()[sliceobj]).all()


Expand Down