26
26
See :mod:`nibabel.tests.test_proxy_api` for proxy API conformance checks.
27
27
"""
28
28
from contextlib import contextmanager
29
- from threading import Lock
29
+ from threading import RLock
30
30
31
31
import numpy as np
32
32
@@ -72,7 +72,7 @@ class ArrayProxy(object):
72
72
_header = None
73
73
74
74
@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' ):
76
76
"""Initialize array proxy instance
77
77
78
78
Parameters
@@ -102,11 +102,17 @@ def __init__(self, file_like, spec, mmap=True, keep_file_open=False):
102
102
True gives the same behavior as ``mmap='c'``. If `file_like`
103
103
cannot be memory-mapped, ignore `mmap` value and read array from
104
104
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.
110
116
"""
111
117
if mmap not in (True , False , 'c' , 'r' ):
112
118
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):
131
137
# Permit any specifier that can be interpreted as a numpy dtype
132
138
self ._dtype = np .dtype (self ._dtype )
133
139
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 ()
136
143
137
144
def __del__ (self ):
138
145
"""If this ``ArrayProxy`` was created with ``keep_file_open=True``,
@@ -151,7 +158,50 @@ def __getstate__(self):
151
158
def __setstate__ (self , state ):
152
159
"""Sets the state of this ``ArrayProxy`` during unpickling. """
153
160
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' )
155
205
156
206
@property
157
207
@deprecate_with_version ('ArrayProxy.header deprecated' , '2.2' , '3.0' )
0 commit comments