Skip to content

Commit 825e047

Browse files
committed
RF: refactor image API tests
There was too much indentation, making the code harder to follow. Break the code up into helper methods. Remembering (after forgetting) that method names beginning with `validate` will get called automatically by the validator post-processing.
1 parent dfd113c commit 825e047

File tree

1 file changed

+156
-144
lines changed

1 file changed

+156
-144
lines changed

nibabel/tests/test_image_api.py

+156-144
Original file line numberDiff line numberDiff line change
@@ -195,156 +195,20 @@ class DataInterfaceMixin(GetSetDtypeMixin):
195195
Use this mixin if your image has a ``dataobj`` property that contains an
196196
array or an array-like thing.
197197
"""
198+
meth_names = ('get_fdata', 'get_data')
199+
198200
def validate_data_interface(self, imaker, params):
199201
# Check get data returns array, and caches
200202
img = imaker()
201203
assert_equal(img.shape, img.dataobj.shape)
202204
assert_data_similar(img.dataobj, params)
203-
meth_names = ('get_fdata', 'get_data')
204-
for meth_name in meth_names:
205+
for meth_name in self.meth_names:
205206
if params['is_proxy']:
206-
# Parameters assert this is an array proxy
207-
img = imaker()
208-
# Does is_proxy agree?
209-
assert_true(is_proxy(img.dataobj))
210-
# Confirm it is not a numpy array
211-
assert_false(isinstance(img.dataobj, np.ndarray))
212-
# Confirm it can be converted to a numpy array with asarray
213-
proxy_data = np.asarray(img.dataobj)
214-
proxy_copy = proxy_data.copy()
215-
# Not yet cached, proxy image: in_memory is False
216-
assert_false(img.in_memory)
217-
# Load with caching='unchanged'
218-
method = getattr(img, meth_name)
219-
data = method(caching='unchanged')
220-
# Still not cached
221-
assert_false(img.in_memory)
222-
# Default load, does caching
223-
data = method()
224-
# Data now cached. in_memory is True if either of the get_data
225-
# or get_fdata caches are not-None
226-
assert_true(img.in_memory)
227-
# We previously got proxy_data from disk, but data, which we
228-
# have just fetched, is a fresh copy.
229-
assert_false(proxy_data is data)
230-
# asarray on dataobj, applied above, returns same numerical
231-
# values. This might not be true get_fdata operating on huge
232-
# integers, but lets assume that's not true here.
233-
assert_array_equal(proxy_data, data)
234-
# Now caching='unchanged' does nothing, returns cached version
235-
data_again = method(caching='unchanged')
236-
assert_true(data is data_again)
237-
# caching='fill' does nothing because the cache is already full
238-
data_yet_again = method(caching='fill')
239-
assert_true(data is data_yet_again)
240-
# changing array data does not change proxy data, or reloaded
241-
# data
242-
data[:] = 42
243-
assert_array_equal(proxy_data, proxy_copy)
244-
assert_array_equal(np.asarray(img.dataobj), proxy_copy)
245-
# It does change the result of get_data
246-
assert_array_equal(method(), 42)
247-
# until we uncache
248-
img.uncache()
249-
# Which unsets in_memory
250-
assert_false(img.in_memory)
251-
assert_array_equal(method(), proxy_copy)
252-
# Check caching='fill' does cache data
253-
img = imaker()
254-
method = getattr(img, meth_name)
255-
assert_false(img.in_memory)
256-
data = method(caching='fill')
257-
assert_true(img.in_memory)
258-
data_again = method()
259-
assert_true(data is data_again)
260-
# Check the interaction of caching with get_data, get_fdata.
261-
# Caching for `get_data` should have no effect on caching for
262-
# get_fdata, and vice versa.
263-
# Modify the cached data
264-
data[:] = 43
265-
# Load using the other data fetch method
266-
other_name = set(meth_names).difference({meth_name}).pop()
267-
other_method = getattr(img, other_name)
268-
other_data = other_method()
269-
# We get the original data, not the modified cache
270-
assert_array_equal(proxy_data, other_data)
271-
assert_false(np.all(data == other_data))
272-
# We can modify the other cache, without affecting the first
273-
other_data[:] = 44
274-
assert_array_equal(other_method(), 44)
275-
assert_false(np.all(method() == other_method()))
276-
# Check that caching refreshes for new floating point type.
277-
if meth_name == 'get_fdata':
278-
img.uncache()
279-
fdata = img.get_fdata()
280-
assert_equal(fdata.dtype, np.float64)
281-
fdata[:] = 42
282-
fdata_back = img.get_fdata()
283-
assert_array_equal(fdata_back, 42)
284-
assert_equal(fdata_back.dtype, np.float64)
285-
# New data dtype, no caching, doesn't use or alter cache
286-
fdata_new_dt = img.get_fdata(caching='unchanged', dtype='f4')
287-
# We get back the original read, not the modified cache
288-
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
289-
assert_equal(fdata_new_dt.dtype, np.float32)
290-
# The original cache stays in place, for default float64
291-
assert_array_equal(img.get_fdata(), 42)
292-
# And for not-default float32, because we haven't cached
293-
fdata_new_dt[:] = 43
294-
fdata_new_dt = img.get_fdata(caching='unchanged', dtype='f4')
295-
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
296-
# Until we reset with caching='fill', at which point we
297-
# drop the original float64 cache, and have a float32 cache
298-
fdata_new_dt = img.get_fdata(caching='fill', dtype='f4')
299-
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
300-
# We're using the cache, for dtype='f4' reads
301-
fdata_new_dt[:] = 43
302-
assert_array_equal(img.get_fdata(dtype='f4'), 43)
303-
# We've lost the cache for float64 reads (no longer 42)
304-
assert_array_equal(img.get_fdata(), proxy_data)
305-
else: # not proxy
306-
for caching in (None, 'fill', 'unchanged'):
307-
img = imaker()
308-
method = getattr(img, meth_name)
309-
get_data_func = (method if caching is None else
310-
partial(method, caching=caching))
311-
assert_true(isinstance(img.dataobj, np.ndarray))
312-
assert_true(img.in_memory)
313-
data = get_data_func()
314-
# Returned data same object as underlying dataobj if using
315-
# old ``get_data`` method, or using newer ``get_fdata``
316-
# method, where original array was float64.
317-
dataobj_is_data = (img.dataobj.dtype == np.float64
318-
or method == img.get_data)
319-
# Set something to the output array.
320-
data[:] = 42
321-
get_result_changed = np.all(get_data_func() == 42)
322-
assert_equal(get_result_changed,
323-
dataobj_is_data or caching != 'unchanged')
324-
if dataobj_is_data:
325-
assert_true(data is img.dataobj)
326-
# Changing array data changes
327-
# data
328-
assert_array_equal(np.asarray(img.dataobj), 42)
329-
# Uncache has no effect
330-
img.uncache()
331-
assert_array_equal(get_data_func(), 42)
332-
else:
333-
assert_false(data is img.dataobj)
334-
assert_false(np.all(np.asarray(img.dataobj) == 42))
335-
# Uncache does have an effect
336-
img.uncache()
337-
assert_false(np.all(get_data_func() == 42))
338-
# in_memory is always true for array images, regardless of
339-
# cache state.
340-
img.uncache()
341-
assert_true(img.in_memory)
342-
# Values to get_(f)data caching parameter must be 'fill' or
343-
# 'unchanged'
344-
assert_raises(ValueError, img.get_data, caching='something')
345-
assert_raises(ValueError, img.get_fdata, caching='something')
207+
self._check_proxy_interface(imaker, meth_name)
208+
else: # Array image
209+
self._check_array_interface(imaker, meth_name)
346210
# Data shape is same as image shape
347-
assert_equal(img.shape, method().shape)
211+
assert_equal(img.shape, getattr(img, meth_name)().shape)
348212
# Values to get_data caching parameter must be 'fill' or
349213
# 'unchanged'
350214
assert_raises(ValueError, img.get_data, caching='something')
@@ -354,6 +218,155 @@ def validate_data_interface(self, imaker, params):
354218
# So is in_memory
355219
assert_raises(AttributeError, setattr, img, 'in_memory', False)
356220

221+
def _check_proxy_interface(self, imaker, meth_name):
222+
# Parameters assert this is an array proxy
223+
img = imaker()
224+
# Does is_proxy agree?
225+
assert_true(is_proxy(img.dataobj))
226+
# Confirm it is not a numpy array
227+
assert_false(isinstance(img.dataobj, np.ndarray))
228+
# Confirm it can be converted to a numpy array with asarray
229+
proxy_data = np.asarray(img.dataobj)
230+
proxy_copy = proxy_data.copy()
231+
# Not yet cached, proxy image: in_memory is False
232+
assert_false(img.in_memory)
233+
# Load with caching='unchanged'
234+
method = getattr(img, meth_name)
235+
data = method(caching='unchanged')
236+
# Still not cached
237+
assert_false(img.in_memory)
238+
# Default load, does caching
239+
data = method()
240+
# Data now cached. in_memory is True if either of the get_data
241+
# or get_fdata caches are not-None
242+
assert_true(img.in_memory)
243+
# We previously got proxy_data from disk, but data, which we
244+
# have just fetched, is a fresh copy.
245+
assert_false(proxy_data is data)
246+
# asarray on dataobj, applied above, returns same numerical
247+
# values. This might not be true get_fdata operating on huge
248+
# integers, but lets assume that's not true here.
249+
assert_array_equal(proxy_data, data)
250+
# Now caching='unchanged' does nothing, returns cached version
251+
data_again = method(caching='unchanged')
252+
assert_true(data is data_again)
253+
# caching='fill' does nothing because the cache is already full
254+
data_yet_again = method(caching='fill')
255+
assert_true(data is data_yet_again)
256+
# changing array data does not change proxy data, or reloaded
257+
# data
258+
data[:] = 42
259+
assert_array_equal(proxy_data, proxy_copy)
260+
assert_array_equal(np.asarray(img.dataobj), proxy_copy)
261+
# It does change the result of get_data
262+
assert_array_equal(method(), 42)
263+
# until we uncache
264+
img.uncache()
265+
# Which unsets in_memory
266+
assert_false(img.in_memory)
267+
assert_array_equal(method(), proxy_copy)
268+
# Check caching='fill' does cache data
269+
img = imaker()
270+
method = getattr(img, meth_name)
271+
assert_false(img.in_memory)
272+
data = method(caching='fill')
273+
assert_true(img.in_memory)
274+
data_again = method()
275+
assert_true(data is data_again)
276+
# Check the interaction of caching with get_data, get_fdata.
277+
# Caching for `get_data` should have no effect on caching for
278+
# get_fdata, and vice versa.
279+
# Modify the cached data
280+
data[:] = 43
281+
# Load using the other data fetch method
282+
other_name = set(self.meth_names).difference({meth_name}).pop()
283+
other_method = getattr(img, other_name)
284+
other_data = other_method()
285+
# We get the original data, not the modified cache
286+
assert_array_equal(proxy_data, other_data)
287+
assert_false(np.all(data == other_data))
288+
# We can modify the other cache, without affecting the first
289+
other_data[:] = 44
290+
assert_array_equal(other_method(), 44)
291+
assert_false(np.all(method() == other_method()))
292+
if meth_name != 'get_fdata':
293+
return
294+
# Check that caching refreshes for new floating point type.
295+
img.uncache()
296+
fdata = img.get_fdata()
297+
assert_equal(fdata.dtype, np.float64)
298+
fdata[:] = 42
299+
fdata_back = img.get_fdata()
300+
assert_array_equal(fdata_back, 42)
301+
assert_equal(fdata_back.dtype, np.float64)
302+
# New data dtype, no caching, doesn't use or alter cache
303+
fdata_new_dt = img.get_fdata(caching='unchanged', dtype='f4')
304+
# We get back the original read, not the modified cache
305+
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
306+
assert_equal(fdata_new_dt.dtype, np.float32)
307+
# The original cache stays in place, for default float64
308+
assert_array_equal(img.get_fdata(), 42)
309+
# And for not-default float32, because we haven't cached
310+
fdata_new_dt[:] = 43
311+
fdata_new_dt = img.get_fdata(caching='unchanged', dtype='f4')
312+
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
313+
# Until we reset with caching='fill', at which point we
314+
# drop the original float64 cache, and have a float32 cache
315+
fdata_new_dt = img.get_fdata(caching='fill', dtype='f4')
316+
assert_array_equal(fdata_new_dt, proxy_data.astype('f4'))
317+
# We're using the cache, for dtype='f4' reads
318+
fdata_new_dt[:] = 43
319+
assert_array_equal(img.get_fdata(dtype='f4'), 43)
320+
# We've lost the cache for float64 reads (no longer 42)
321+
assert_array_equal(img.get_fdata(), proxy_data)
322+
323+
def _check_array_interface(self, imaker, meth_name):
324+
for caching in (None, 'fill', 'unchanged'):
325+
self._check_array_caching(imaker, meth_name, caching)
326+
# Values to get_(f)data caching parameter must be 'fill' or
327+
# 'unchanged'
328+
img = imaker()
329+
for meth_name in self.meth_names:
330+
method = getattr(img, meth_name)
331+
assert_raises(ValueError, method, caching='something')
332+
333+
def _check_array_caching(self, imaker, meth_name, caching):
334+
img = imaker()
335+
method = getattr(img, meth_name)
336+
get_data_func = (method if caching is None else
337+
partial(method, caching=caching))
338+
assert_true(isinstance(img.dataobj, np.ndarray))
339+
assert_true(img.in_memory)
340+
data = get_data_func()
341+
# Returned data same object as underlying dataobj if using
342+
# old ``get_data`` method, or using newer ``get_fdata``
343+
# method, where original array was float64.
344+
dataobj_is_data = (img.dataobj.dtype == np.float64
345+
or method == img.get_data)
346+
# Set something to the output array.
347+
data[:] = 42
348+
get_result_changed = np.all(get_data_func() == 42)
349+
assert_equal(get_result_changed,
350+
dataobj_is_data or caching != 'unchanged')
351+
if dataobj_is_data:
352+
assert_true(data is img.dataobj)
353+
# Changing array data changes
354+
# data
355+
assert_array_equal(np.asarray(img.dataobj), 42)
356+
# Uncache has no effect
357+
img.uncache()
358+
assert_array_equal(get_data_func(), 42)
359+
else:
360+
assert_false(data is img.dataobj)
361+
assert_false(np.all(np.asarray(img.dataobj) == 42))
362+
# Uncache does have an effect
363+
img.uncache()
364+
assert_false(np.all(get_data_func() == 42))
365+
# in_memory is always true for array images, regardless of
366+
# cache state.
367+
img.uncache()
368+
assert_true(img.in_memory)
369+
357370
def validate_data_deprecated(self, imaker, params):
358371
# Check _data property still exists, but raises warning
359372
img = imaker()
@@ -385,7 +398,6 @@ def validate_shape_deprecated(self, imaker, params):
385398
assert_equal(len(w), 1)
386399

387400

388-
389401
class HeaderShapeMixin(object):
390402
""" Tests that header shape can be set and got
391403

0 commit comments

Comments
 (0)