Skip to content

Commit 0a111ca

Browse files
committed
RF: Adjust the default behaviour of the ArrayProxy keep_file_open flag - if
file is gz, and indexed_gzip is True, kfo is set to True. ArrayProxy is also using an RLock instead of a Lock, as I don't think there is a good reason not to.
1 parent a6972c0 commit 0a111ca

File tree

4 files changed

+96
-29
lines changed

4 files changed

+96
-29
lines changed

nibabel/analyze.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -934,7 +934,7 @@ def set_data_dtype(self, dtype):
934934

935935
@classmethod
936936
@kw_only_meth(1)
937-
def from_file_map(klass, file_map, mmap=True, keep_file_open=False):
937+
def from_file_map(klass, file_map, mmap=True, keep_file_open='indexed'):
938938
''' class method to create image from mapping in `file_map ``
939939
940940
Parameters
@@ -950,11 +950,17 @@ def from_file_map(klass, file_map, mmap=True, keep_file_open=False):
950950
`mmap` value of True gives the same behavior as ``mmap='c'``. If
951951
image data file cannot be memory-mapped, ignore `mmap` value and
952952
read array from file.
953-
keep_file_open: If ``file_like`` is a file name, the default behaviour
954-
is to open a new file handle every time the data is accessed. If
955-
this flag is set to `True``, the file handle will be opened on the
956-
first access, and kept open until this ``ArrayProxy`` is garbage-
957-
collected.
953+
keep_file_open : { 'indexed', True, False }, optional, keyword only
954+
`keep_file_open` controls whether a new file handle is created
955+
every time the image is accessed, or a single file handle is
956+
created and used for the lifetime of this ``ArrayProxy``. If
957+
``True``, a single file handle is created and used. If ``False``,
958+
a new file handle is created every time the image is accessed. If
959+
``'indexed'`` (the default), and the optional ``indexed_gzip``
960+
dependency is present, a single file handle is created and
961+
persisted. If ``indexed_gzip`` is not available, behaviour is the
962+
same as if ``keep_file_open is False``. If ``file_like`` is an
963+
open file handle, this setting has no effect.
958964
959965
Returns
960966
-------
@@ -982,7 +988,7 @@ def from_file_map(klass, file_map, mmap=True, keep_file_open=False):
982988

983989
@classmethod
984990
@kw_only_meth(1)
985-
def from_filename(klass, filename, mmap=True, keep_file_open=False):
991+
def from_filename(klass, filename, mmap=True, keep_file_open='indexed'):
986992
''' class method to create image from filename `filename`
987993
988994
Parameters
@@ -996,11 +1002,17 @@ def from_filename(klass, filename, mmap=True, keep_file_open=False):
9961002
`mmap` value of True gives the same behavior as ``mmap='c'``. If
9971003
image data file cannot be memory-mapped, ignore `mmap` value and
9981004
read array from file.
999-
keep_file_open: If ``file_like`` is a file name, the default behaviour
1000-
is to open a new file handle every time the data is accessed. If
1001-
this flag is set to `True``, the file handle will be opened on the
1002-
first access, and kept open until this ``ArrayProxy`` is garbage-
1003-
collected.
1005+
keep_file_open : { 'indexed', True, False }, optional, keyword only
1006+
`keep_file_open` controls whether a new file handle is created
1007+
every time the image is accessed, or a single file handle is
1008+
created and used for the lifetime of this ``ArrayProxy``. If
1009+
``True``, a single file handle is created and used. If ``False``,
1010+
a new file handle is created every time the image is accessed. If
1011+
``'indexed'`` (the default), and the optional ``indexed_gzip``
1012+
dependency is present, a single file handle is created and
1013+
persisted. If ``indexed_gzip`` is not available, behaviour is the
1014+
same as if ``keep_file_open is False``. If ``file_like`` is an
1015+
open file handle, this setting has no effect.
10041016
10051017
Returns
10061018
-------

nibabel/arrayproxy.py

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
See :mod:`nibabel.tests.test_proxy_api` for proxy API conformance checks.
2727
"""
2828
from contextlib import contextmanager
29-
from threading import Lock
29+
from threading import RLock
3030

3131
import numpy as np
3232

@@ -72,7 +72,7 @@ class ArrayProxy(object):
7272
_header = None
7373

7474
@kw_only_meth(2)
75-
def __init__(self, file_like, spec, mmap=True, keep_file_open=False):
75+
def __init__(self, file_like, spec, mmap=True, keep_file_open='indexed'):
7676
"""Initialize array proxy instance
7777
7878
Parameters
@@ -102,11 +102,17 @@ def __init__(self, file_like, spec, mmap=True, keep_file_open=False):
102102
True gives the same behavior as ``mmap='c'``. If `file_like`
103103
cannot be memory-mapped, ignore `mmap` value and read array from
104104
file.
105-
keep_file_open: If ``file_like`` is a file name, the default behaviour
106-
is to open a new file handle every time the data is accessed. If
107-
this flag is set to `True``, the file handle will be opened on the
108-
first access, and kept open until this ``ArrayProxy`` is garbage-
109-
collected.
105+
keep_file_open : { 'indexed', True, False }, optional, keyword only
106+
`keep_file_open` controls whether a new file handle is created
107+
every time the image is accessed, or a single file handle is
108+
created and used for the lifetime of this ``ArrayProxy``. If
109+
``True``, a single file handle is created and used. If ``False``,
110+
a new file handle is created every time the image is accessed. If
111+
``'indexed'`` (the default), and the optional ``indexed_gzip``
112+
dependency is present, a single file handle is created and
113+
persisted. If ``indexed_gzip`` is not available, behaviour is the
114+
same as if ``keep_file_open is False``. If ``file_like`` is an
115+
open file handle, this setting has no effect.
110116
"""
111117
if mmap not in (True, False, 'c', 'r'):
112118
raise ValueError("mmap should be one of {True, False, 'c', 'r'}")
@@ -131,8 +137,9 @@ def __init__(self, file_like, spec, mmap=True, keep_file_open=False):
131137
# Permit any specifier that can be interpreted as a numpy dtype
132138
self._dtype = np.dtype(self._dtype)
133139
self._mmap = mmap
134-
self._keep_file_open = keep_file_open
135-
self._lock = Lock()
140+
self._keep_file_open = self._should_keep_file_open(file_like,
141+
keep_file_open)
142+
self._lock = RLock()
136143

137144
def __del__(self):
138145
"""If this ``ArrayProxy`` was created with ``keep_file_open=True``,
@@ -151,7 +158,50 @@ def __getstate__(self):
151158
def __setstate__(self, state):
152159
"""Sets the state of this ``ArrayProxy`` during unpickling. """
153160
self.__dict__.update(state)
154-
self._lock = Lock()
161+
self._lock = RLock()
162+
163+
def _should_keep_file_open(self, file_like, keep_file_open):
164+
"""Called by ``__init__``, and used to determine the final value of
165+
``keep_file_open``.
166+
167+
The return value is derived from these rules:
168+
169+
- If ``file_like`` is a file(-like) object, ``False`` is returned.
170+
Otherwise, ``file_like`` is assumed to be a file name.
171+
- if ``file_like`` ends with ``'gz'``, and the ``indexed_gzip``
172+
library is available, ``True`` is returned.
173+
- Otherwise, ``False`` is returned.
174+
175+
Parameters
176+
----------
177+
178+
file_like : object
179+
File-like object or filename, as passed to ``__init__``.
180+
keep_file_open : { 'indexed', True, False }
181+
Flag as passed to ``__init__``.
182+
183+
Returns
184+
-------
185+
186+
The value of ``keep_file_open`` that will be used by this
187+
``ArrayProxy``.
188+
"""
189+
190+
# file_like is a handle - keep_file_open is irrelevant
191+
if hasattr(file_like, 'read') and hasattr(file_like, 'seek'):
192+
return False
193+
# if keep_file_open is True/False, we do what the user wants us to do
194+
if keep_file_open != 'indexed':
195+
return bool(keep_file_open)
196+
# Otherwise, if file_like is gzipped, and we have_indexed_gzip, we set
197+
# keep_file_open to True, else we set it to False
198+
try:
199+
import indexed_gzip
200+
have_indexed_gzip = True
201+
except ImportError:
202+
have_indexed_gzip = False
203+
204+
return have_indexed_gzip and file_like.endswith('gz')
155205

156206
@property
157207
@deprecate_with_version('ArrayProxy.header deprecated', '2.2', '3.0')

nibabel/fileslice.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
SKIP_THRESH = 2 ** 8
1818

1919

20-
2120
class _NullLock(object):
2221
"""The ``_NullLock`` is an object which can be used in place of a
2322
``threading.Lock`` object, but doesn't actually do anything.

nibabel/spm99analyze.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ class Spm99AnalyzeImage(analyze.AnalyzeImage):
245245

246246
@classmethod
247247
@kw_only_meth(1)
248-
def from_file_map(klass, file_map, mmap=True, keep_file_open=False):
248+
def from_file_map(klass, file_map, mmap=True, keep_file_open='indexed'):
249249
'''class method to create image from mapping in `file_map ``
250250
251251
Parameters
@@ -261,11 +261,17 @@ def from_file_map(klass, file_map, mmap=True, keep_file_open=False):
261261
`mmap` value of True gives the same behavior as ``mmap='c'``. If
262262
image data file cannot be memory-mapped, ignore `mmap` value and
263263
read array from file.
264-
keep_file_open: If the file-ilke(s) is a file name, the default
265-
behaviour is to open a new file handle every time the data is
266-
accessed. If this flag is set to `True``, the file handle will be
267-
opened on the first access, and kept open until this
268-
``ArrayProxy`` is garbage-collected.
264+
keep_file_open : { 'indexed', True, False }, optional, keyword only
265+
`keep_file_open` controls whether a new file handle is created
266+
every time the image is accessed, or a single file handle is
267+
created and used for the lifetime of this ``ArrayProxy``. If
268+
``True``, a single file handle is created and used. If ``False``,
269+
a new file handle is created every time the image is accessed. If
270+
``'indexed'`` (the default), and the optional ``indexed_gzip``
271+
dependency is present, a single file handle is created and
272+
persisted. If ``indexed_gzip`` is not available, behaviour is the
273+
same as if ``keep_file_open is False``. If ``file_like`` is an
274+
open file handle, this setting has no effect.
269275
270276
Returns
271277
-------

0 commit comments

Comments
 (0)