Skip to content

Commit 4efc5e3

Browse files
committed
TEST: Refactor keep_file_open tests to be more comprehensive
1 parent dc04379 commit 4efc5e3

File tree

1 file changed

+107
-164
lines changed

1 file changed

+107
-164
lines changed

nibabel/tests/test_arrayproxy.py

Lines changed: 107 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -349,78 +349,128 @@ def __init__(self, *args, **kwargs):
349349

350350
def _count_ImageOpeners(proxy, data, voxels):
351351
CountingImageOpener.num_openers = 0
352+
# expected data is defined in the test_keep_file_open_* tests
352353
for i in range(voxels.shape[0]):
353354
x, y, z = [int(c) for c in voxels[i, :]]
354355
assert proxy[x, y, z] == x * 100 + y * 10 + z
355356
return CountingImageOpener.num_openers
356357

357358

359+
@contextlib.contextmanager
360+
def patch_keep_file_open_default(value):
361+
# Patch arrayproxy.KEEP_FILE_OPEN_DEFAULT with the given value
362+
with mock.patch('nibabel.arrayproxy.KEEP_FILE_OPEN_DEFAULT', value):
363+
yield
364+
365+
358366
def test_keep_file_open_true_false_invalid():
359367
# Test the behaviour of the keep_file_open __init__ flag, when it is set to
360368
# True or False. Expected behaviour is as follows:
361-
# igzip present | keep_file_open | persist ImageOpener | igzip.drop_handles
362-
# False | False | False | n/a
363-
# False | True | True | n/a
364-
# True | False | True | True
365-
# True | True | True | False
366-
CountingImageOpener.num_openers = 0
367-
fname = 'testdata'
369+
# keep_open | igzip present | persist ImageOpener | igzip.drop_handles
370+
# | and is gzip file | |
371+
# ----------|------------------|---------------------|-------------------
372+
# False | False | False | n/a
373+
# False | True | True | True
374+
# True | False | True | n/a
375+
# True | True | True | False
376+
# 'auto' | False | False | n/a
377+
# 'auto' | True | True | False
378+
#
379+
# Each test tuple contains:
380+
# - file type - gzipped ('gz') or not ('bin'), or an open file handle
381+
# ('open')
382+
# - keep_file_open value passed to ArrayProxy
383+
# - whether or not indexed_gzip is present
384+
# - expected value for internal ArrayProxy._persist_opener flag
385+
# - expected value for internal ArrayProxy._keep_file_open flag
386+
tests = [
387+
# open file handle - kfo and have_igzip are both irrelevant
388+
('open', False, False, False, False),
389+
('open', False, True, False, False),
390+
('open', True, False, False, False),
391+
('open', True, True, False, False),
392+
('open', 'auto', False, False, False),
393+
('open', 'auto', True, False, False),
394+
# non-gzip file - have_igzip is irrelevant, decision should be made
395+
# solely from kfo flag
396+
('bin', False, False, False, False),
397+
('bin', False, True, False, False),
398+
('bin', True, False, True, True),
399+
('bin', True, True, True, True),
400+
('bin', 'auto', False, False, False),
401+
('bin', 'auto', True, False, False),
402+
# gzip file. If igzip is present, we persist the ImageOpener. If kfo
403+
# is 'auto':
404+
# - if igzip is present, kfo -> True
405+
# - otherwise, kfo -> False
406+
('gz', False, False, False, False),
407+
('gz', False, True, True, False),
408+
('gz', True, False, True, True),
409+
('gz', True, True, True, True),
410+
('gz', 'auto', False, False, False),
411+
('gz', 'auto', True, True, True)]
412+
368413
dtype = np.float32
369414
data = np.arange(1000, dtype=dtype).reshape((10, 10, 10))
370415
voxels = np.random.randint(0, 10, (10, 3))
416+
417+
for test in tests:
418+
filetype, kfo, have_igzip, exp_persist, exp_kfo = test
419+
with InTemporaryDirectory(), \
420+
mock.patch('nibabel.openers.ImageOpener', CountingImageOpener), \
421+
patch_indexed_gzip(have_igzip):
422+
fname = 'testdata.{}'.format(filetype)
423+
# create the test data file
424+
if filetype == 'gz':
425+
with gzip.open(fname, 'wb') as fobj:
426+
fobj.write(data.tostring(order='F'))
427+
else:
428+
with open(fname, 'wb') as fobj:
429+
fobj.write(data.tostring(order='F'))
430+
if filetype == 'open':
431+
fname = open(fname, 'rb')
432+
try:
433+
proxy = ArrayProxy(fname, ((10, 10, 10), dtype),
434+
keep_file_open=kfo)
435+
# We also test that we get the same behaviour when the
436+
# KEEP_FILE_OPEN_DEFAULT flag is changed
437+
with patch_keep_file_open_default(kfo):
438+
proxy_def = ArrayProxy(fname, ((10, 10, 10), dtype))
439+
# check internal flags
440+
assert proxy._persist_opener == exp_persist
441+
assert proxy._keep_file_open == exp_kfo
442+
assert proxy_def._persist_opener == exp_persist
443+
assert proxy_def._keep_file_open == exp_kfo
444+
# check persist_opener behaviour - whether one imageopener is
445+
# created for the lifetime of the ArrayProxy, or one is
446+
# created on each access
447+
if exp_persist:
448+
assert _count_ImageOpeners(proxy, data, voxels) == 1
449+
assert _count_ImageOpeners(proxy_def, data, voxels) == 1
450+
else:
451+
assert _count_ImageOpeners(proxy, data, voxels) == 10
452+
assert _count_ImageOpeners(proxy_def, data, voxels) == 10
453+
# if indexed_gzip is active, check that the file object was
454+
# created correctly - the _opener.fobj will be a
455+
# MockIndexedGzipFile, defined in test_openers.py
456+
if filetype == 'gz' and have_igzip:
457+
assert proxy._opener.fobj._drop_handles == (not exp_kfo)
458+
# if we were using an open file handle, check that the proxy
459+
# didn't close it
460+
if filetype == 'open':
461+
assert not fname.closed
462+
except Exception:
463+
print('Failed test', test)
464+
raise
465+
finally:
466+
if filetype == 'open':
467+
fname.close()
468+
# Test invalid values of keep_file_open
469+
print('testinv')
371470
with InTemporaryDirectory():
471+
fname = 'testdata'
372472
with open(fname, 'wb') as fobj:
373473
fobj.write(data.tostring(order='F'))
374-
# Without indexed_gzip, test that ArrayProxy(keep_file_open=True) only
375-
# creates one ImageOpener, and that ArrayProxy(keep_file_open=False)
376-
# creates an ImageOpener on every data access.
377-
with mock.patch('nibabel.openers.ImageOpener', CountingImageOpener), \
378-
patch_indexed_gzip(False):
379-
proxy_no_kfp = ArrayProxy(fname, ((10, 10, 10), dtype),
380-
keep_file_open=False)
381-
assert not proxy_no_kfp._keep_file_open
382-
assert _count_ImageOpeners(proxy_no_kfp, data, voxels) == 10
383-
proxy_kfp = ArrayProxy(fname, ((10, 10, 10), dtype),
384-
keep_file_open=True)
385-
assert proxy_kfp._keep_file_open
386-
assert _count_ImageOpeners(proxy_kfp, data, voxels) == 1
387-
del proxy_kfp
388-
del proxy_no_kfp
389-
# With indexed_gzip, test that both ArrayProxy(keep_file_open=True)
390-
# and ArrayProxy(keep_file_open=False) only create one ImageOpener,
391-
# but that the drop_handles parameter passed to the IndexedGzipFile
392-
# is set appropriately
393-
with mock.patch('nibabel.openers.ImageOpener', CountingImageOpener), \
394-
patch_indexed_gzip(True):
395-
proxy_no_kfp = ArrayProxy(fname, ((10, 10, 10), dtype),
396-
keep_file_open=False)
397-
assert proxy_no_kfp._keep_file_open
398-
assert _count_ImageOpeners(proxy_no_kfp, data, voxels) == 1
399-
# check that the drop_handles flag is set - the fobj attribute
400-
# should be a MockIndexedGzipFile, defined in test_openers.
401-
assert proxy_no_kfp._opener.fobj._drop_handles
402-
proxy_kfp = ArrayProxy(fname, ((10, 10, 10), dtype),
403-
keep_file_open=True)
404-
assert proxy_kfp._keep_file_open
405-
assert _count_ImageOpeners(proxy_kfp, data, voxels) == 1
406-
assert not proxy_no_kfp._opener.fobj._drop_handles
407-
del proxy_kfp
408-
del proxy_no_kfp
409-
# Test that the keep_file_open flag has no effect if an open file
410-
# handle is passed in
411-
with open(fname, 'rb') as fobj:
412-
for kfo in (True, False, 'auto'):
413-
proxy = ArrayProxy(fobj, ((10, 10, 10), dtype),
414-
keep_file_open=kfo)
415-
assert proxy._keep_file_open is False
416-
for i in range(voxels.shape[0]):
417-
x, y, z = [int(c) for c in voxels[i, :]]
418-
assert proxy[x, y, z] == x * 100 + y * 10 + z
419-
assert not fobj.closed
420-
del proxy
421-
assert not fobj.closed
422-
assert fobj.closed
423-
# Test invalid values of keep_file_open
424474
with assert_raises(ValueError):
425475
ArrayProxy(fname, ((10, 10, 10), dtype), keep_file_open=55)
426476
with assert_raises(ValueError):
@@ -429,113 +479,6 @@ def test_keep_file_open_true_false_invalid():
429479
ArrayProxy(fname, ((10, 10, 10), dtype), keep_file_open='cauto')
430480

431481

432-
def test_keep_file_open_auto():
433-
# Test the behaviour of the keep_file_open __init__ flag, when it is set to
434-
# 'auto'. Expected behaviour is as follows:
435-
# igzip present | keep_file_open | persist ImageOpener | igzip.drop_handles
436-
# False | 'auto' | False | n/a
437-
# True | 'auto' | False | False
438-
dtype = np.float32
439-
data = np.arange(1000, dtype=dtype).reshape((10, 10, 10))
440-
voxels = np.random.randint(0, 10, (10, 3))
441-
with InTemporaryDirectory():
442-
fname = 'testdata.gz'
443-
with gzip.open(fname, 'wb') as fobj:
444-
fobj.write(data.tostring(order='F'))
445-
# If no have_indexed_gzip, then a separate ImageOpener should be
446-
# created on every access.
447-
with patch_indexed_gzip(False), \
448-
mock.patch('nibabel.openers.ImageOpener', CountingImageOpener):
449-
CountingImageOpener.num_openers = 0
450-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype),
451-
keep_file_open='auto')
452-
assert not proxy._keep_file_open
453-
assert _count_ImageOpeners(proxy, data, voxels) == 10
454-
# If have_indexed_gzip, then the arrayproxy should create one
455-
# ImageOpener, and the IndexedGzipFile drop_handles parameter should
456-
# be set to False, so the file handle stays open.
457-
with patch_indexed_gzip(True), \
458-
mock.patch('nibabel.openers.ImageOpener', CountingImageOpener):
459-
CountingImageOpener.num_openers = 0
460-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype),
461-
keep_file_open='auto')
462-
assert proxy._keep_file_open
463-
assert _count_ImageOpeners(proxy, data, voxels) == 1
464-
assert not proxy._opener.fobj._drop_handles
465-
# If not a gzip file, keep_file_open should be False
466-
fname = 'testdata'
467-
with open(fname, 'wb') as fobj:
468-
fobj.write(data.tostring(order='F'))
469-
# regardless of whether indexed_gzip is present or not
470-
with patch_indexed_gzip(True), \
471-
mock.patch('nibabel.openers.ImageOpener', CountingImageOpener):
472-
CountingImageOpener.num_openers = 0
473-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype),
474-
keep_file_open='auto')
475-
assert proxy._keep_file_open is False
476-
assert _count_ImageOpeners(proxy, data, voxels) == 10
477-
with patch_indexed_gzip(False), \
478-
mock.patch('nibabel.openers.ImageOpener', CountingImageOpener):
479-
CountingImageOpener.num_openers = 0
480-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype),
481-
keep_file_open='auto')
482-
assert proxy._keep_file_open is False
483-
assert _count_ImageOpeners(proxy, data, voxels) == 10
484-
485-
486-
@contextlib.contextmanager
487-
def patch_keep_file_open_default(value):
488-
# Patch arrayproxy.KEEP_FILE_OPEN_DEFAULT with the given value
489-
with mock.patch('nibabel.arrayproxy.KEEP_FILE_OPEN_DEFAULT', value):
490-
yield
491-
492-
493-
def test_keep_file_open_default():
494-
# Test the behaviour of the keep_file_open __init__ flag, when the
495-
# arrayproxy.KEEP_FILE_OPEN_DEFAULT value is changed
496-
dtype = np.float32
497-
data = np.arange(1000, dtype=dtype).reshape((10, 10, 10))
498-
with InTemporaryDirectory():
499-
fname = 'testdata.gz'
500-
with gzip.open(fname, 'wb') as fobj:
501-
fobj.write(data.tostring(order='F'))
502-
# If KEEP_FILE_OPEN_DEFAULT is False, ArrayProxy instances should
503-
# interpret keep_file_open as False
504-
with patch_keep_file_open_default(False):
505-
with patch_indexed_gzip(False):
506-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
507-
assert proxy._keep_file_open is False
508-
with patch_indexed_gzip(True):
509-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
510-
assert proxy._keep_file_open is False
511-
# If KEEP_FILE_OPEN_DEFAULT is True, ArrayProxy instances should
512-
# interpret keep_file_open as True
513-
with patch_keep_file_open_default(True):
514-
with patch_indexed_gzip(False):
515-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
516-
assert proxy._keep_file_open is True
517-
with patch_indexed_gzip(True):
518-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
519-
assert proxy._keep_file_open is True
520-
# If KEEP_FILE_OPEN_DEFAULT is auto, ArrayProxy instances should
521-
# interpret it as auto if indexed_gzip is present, False otherwise.
522-
with patch_keep_file_open_default('auto'):
523-
with patch_indexed_gzip(False):
524-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
525-
assert proxy._keep_file_open is False
526-
with patch_indexed_gzip(True):
527-
proxy = ArrayProxy(fname, ((10, 10, 10), dtype))
528-
assert proxy._keep_file_open == 'auto'
529-
# KEEP_FILE_OPEN_DEFAULT=any other value should cuse an error to be
530-
# raised
531-
with patch_keep_file_open_default('badvalue'):
532-
assert_raises(ValueError, ArrayProxy, fname, ((10, 10, 10),
533-
dtype))
534-
with patch_keep_file_open_default(None):
535-
assert_raises(ValueError, ArrayProxy, fname, ((10, 10, 10),
536-
dtype))
537-
538-
539482
def test_pickle_lock():
540483
# Test that ArrayProxy can be pickled, and that thread lock is created
541484

0 commit comments

Comments
 (0)