2626See :mod:`nibabel.tests.test_proxy_api` for proxy API conformance checks.
2727"""
2828from contextlib import contextmanager
29- from threading import Lock
29+ from threading import RLock
3030
3131import 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' )
0 commit comments