Skip to content

Commit dfd113c

Browse files
authored
Merge pull request #614 from pauldmccarthy/rf/deprecate_keep_file_open
ENH: Take advantage of IndexedGzipFile drop_handles flag
2 parents 2edca76 + e96d71c commit dfd113c

6 files changed

+158
-123
lines changed

nibabel/arrayproxy.py

+22-19
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .volumeutils import array_from_file, apply_read_scaling
3535
from .fileslice import fileslice
3636
from .keywordonly import kw_only_meth
37-
from .openers import ImageOpener, HAVE_INDEXED_GZIP
37+
from . import openers
3838

3939

4040
"""This flag controls whether a new file handle is created every time an image
@@ -43,14 +43,18 @@
4343
``True``, ``False``, or ``'auto'``.
4444
4545
If ``True``, a single file handle is created and used. If ``False``, a new
46-
file handle is created every time the image is accessed. If ``'auto'``, and
47-
the optional ``indexed_gzip`` dependency is present, a single file handle is
48-
created and persisted. If ``indexed_gzip`` is not available, behaviour is the
49-
same as if ``keep_file_open is False``.
46+
file handle is created every time the image is accessed. For gzip files, if
47+
``'auto'``, and the optional ``indexed_gzip`` dependency is present, a single
48+
file handle is created and persisted. If ``indexed_gzip`` is not available,
49+
behaviour is the same as if ``keep_file_open is False``.
5050
5151
If this is set to any other value, attempts to create an ``ArrayProxy`` without
5252
specifying the ``keep_file_open`` flag will result in a ``ValueError`` being
5353
raised.
54+
55+
.. warning:: Setting this flag to a value of ``'auto'`` will become deprecated
56+
behaviour in version 2.4.0. Support for ``'auto'`` will be removed
57+
in version 3.0.0.
5458
"""
5559
KEEP_FILE_OPEN_DEFAULT = False
5660

@@ -187,9 +191,9 @@ def _should_keep_file_open(self, file_like, keep_file_open):
187191
188192
- If ``file_like`` is a file(-like) object, ``False`` is returned.
189193
Otherwise, ``file_like`` is assumed to be a file name.
190-
- if ``file_like`` ends with ``'gz'``, and the ``indexed_gzip``
191-
library is available, ``True`` is returned.
192-
- Otherwise, ``False`` is returned.
194+
- If ``keep_file_open`` is ``auto``, and ``indexed_gzip`` is
195+
not available, ``False`` is returned.
196+
- Otherwise, the value of ``keep_file_open`` is returned unchanged.
193197
194198
Parameters
195199
----------
@@ -203,23 +207,21 @@ def _should_keep_file_open(self, file_like, keep_file_open):
203207
-------
204208
205209
The value of ``keep_file_open`` that will be used by this
206-
``ArrayProxy``.
210+
``ArrayProxy``, and passed through to ``ImageOpener`` instances.
207211
"""
208212
if keep_file_open is None:
209213
keep_file_open = KEEP_FILE_OPEN_DEFAULT
210-
# if keep_file_open is True/False, we do what the user wants us to do
211-
if isinstance(keep_file_open, bool):
212-
return keep_file_open
213-
if keep_file_open != 'auto':
214+
if keep_file_open not in ('auto', True, False):
214215
raise ValueError('keep_file_open should be one of {None, '
215216
'\'auto\', True, False}')
216-
217217
# file_like is a handle - keep_file_open is irrelevant
218218
if hasattr(file_like, 'read') and hasattr(file_like, 'seek'):
219219
return False
220-
# Otherwise, if file_like is gzipped, and we have_indexed_gzip, we set
221-
# keep_file_open to True, else we set it to False
222-
return HAVE_INDEXED_GZIP and file_like.endswith('gz')
220+
# don't have indexed_gzip - auto -> False
221+
if keep_file_open == 'auto' and not (openers.HAVE_INDEXED_GZIP and
222+
file_like.endswith('.gz')):
223+
return False
224+
return keep_file_open
223225

224226
@property
225227
@deprecate_with_version('ArrayProxy.header deprecated', '2.2', '3.0')
@@ -265,10 +267,11 @@ def _get_fileobj(self):
265267
"""
266268
if self._keep_file_open:
267269
if not hasattr(self, '_opener'):
268-
self._opener = ImageOpener(self.file_like, keep_open=True)
270+
self._opener = openers.ImageOpener(
271+
self.file_like, keep_open=self._keep_file_open)
269272
yield self._opener
270273
else:
271-
with ImageOpener(self.file_like, keep_open=False) as opener:
274+
with openers.ImageOpener(self.file_like) as opener:
272275
yield opener
273276

274277
def get_unscaled(self):

nibabel/benchmarks/bench_array_to_file.py

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from __future__ import division, print_function
1717

1818
import sys
19+
from io import BytesIO # NOQA
1920

2021
import numpy as np
2122

nibabel/benchmarks/bench_arrayproxy_slicing.py

+4-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
"""
1616

1717
from timeit import timeit
18-
import contextlib
1918
import gc
2019
import itertools as it
2120
import numpy as np
@@ -51,24 +50,14 @@
5150
('?', '?', '?', ':'),
5251
]
5352

54-
KEEP_OPENS = [False, True]
53+
KEEP_OPENS = [False, True, 'auto']
5554

5655
if HAVE_INDEXED_GZIP:
5756
HAVE_IGZIP = [False, True]
5857
else:
5958
HAVE_IGZIP = [False]
6059

6160

62-
@contextlib.contextmanager
63-
def patch_indexed_gzip(have_igzip):
64-
65-
atts = ['nibabel.openers.HAVE_INDEXED_GZIP',
66-
'nibabel.arrayproxy.HAVE_INDEXED_GZIP']
67-
68-
with mock.patch(atts[0], have_igzip), mock.patch(atts[1], have_igzip):
69-
yield
70-
71-
7261
def bench_arrayproxy_slicing():
7362

7463
print_git_title('\nArrayProxy gzip slicing')
@@ -154,14 +143,15 @@ def fmt_sliceobj(sliceobj):
154143
# load uncompressed and compressed versions of the image
155144
img = nib.load(testfile, keep_file_open=keep_open)
156145

157-
with patch_indexed_gzip(have_igzip):
146+
with mock.patch('nibabel.openers.HAVE_INDEXED_GZIP', have_igzip):
158147
imggz = nib.load(testfilegz, keep_file_open=keep_open)
159148

160149
def basefunc():
161150
img.dataobj[fix_sliceobj(sliceobj)]
162151

163152
def testfunc():
164-
with patch_indexed_gzip(have_igzip):
153+
with mock.patch('nibabel.openers.HAVE_INDEXED_GZIP',
154+
have_igzip):
165155
imggz.dataobj[fix_sliceobj(sliceobj)]
166156

167157
# make sure nothing is floating around from the previous test

nibabel/openers.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,22 @@
1818

1919
# is indexed_gzip present and modern?
2020
try:
21-
from indexed_gzip import SafeIndexedGzipFile, __version__ as version
21+
import indexed_gzip as igzip
22+
version = igzip.__version__
2223

2324
HAVE_INDEXED_GZIP = True
2425

25-
if StrictVersion(version) < StrictVersion('0.6.0'):
26+
# < 0.7 - no good
27+
if StrictVersion(version) < StrictVersion('0.7.0'):
2628
warnings.warn('indexed_gzip is present, but too old '
27-
'(>= 0.6.0 required): {})'.format(version))
29+
'(>= 0.7.0 required): {})'.format(version))
2830
HAVE_INDEXED_GZIP = False
29-
30-
del version
31+
# >= 0.8 SafeIndexedGzipFile renamed to IndexedGzipFile
32+
elif StrictVersion(version) < StrictVersion('0.8.0'):
33+
IndexedGzipFile = igzip.SafeIndexedGzipFile
34+
else:
35+
IndexedGzipFile = igzip.IndexedGzipFile
36+
del igzip, version
3137

3238
except ImportError:
3339
HAVE_INDEXED_GZIP = False
@@ -80,9 +86,11 @@ def readinto(self, buf):
8086

8187
def _gzip_open(filename, mode='rb', compresslevel=9, keep_open=False):
8288

83-
# use indexed_gzip if possible for faster read access
84-
if keep_open and mode == 'rb' and HAVE_INDEXED_GZIP:
85-
gzip_file = SafeIndexedGzipFile(filename)
89+
# use indexed_gzip if possible for faster read access. If keep_open ==
90+
# True, we tell IndexedGzipFile to keep the file handle open. Otherwise
91+
# the IndexedGzipFile will close/open the file on each read.
92+
if HAVE_INDEXED_GZIP and mode == 'rb':
93+
gzip_file = IndexedGzipFile(filename, drop_handles=not keep_open)
8694

8795
# Fall-back to built-in GzipFile (wrapped with the BufferedGzipFile class
8896
# defined above)

0 commit comments

Comments
 (0)