4242used for the lifetime of the ``ArrayProxy``. It should be set to one of
4343``True``, ``False``, or ``'auto'``.
4444
45- 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. 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``.
45+ Management of file handles will be performed either by ``ArrayProxy`` objects,
46+ or by the ``indexed_gzip`` package if it is used.
47+
48+ If this flag is set to ``True``, a single file handle is created and used. If
49+ ``False``, a new file handle is created every time the image is accessed. For
50+ gzip files, if ``'auto'``, and the optional ``indexed_gzip`` dependency is
51+ present, a single file handle is created and persisted. If ``indexed_gzip`` is
52+ not available, behaviour is the same as if ``keep_file_open is False``.
5053
5154If this is set to any other value, attempts to create an ``ArrayProxy`` without
5255specifying the ``keep_file_open`` flag will result in a ``ValueError`` being
@@ -160,8 +163,10 @@ def __init__(self, file_like, spec, mmap=True, keep_file_open=None):
160163 # Permit any specifier that can be interpreted as a numpy dtype
161164 self ._dtype = np .dtype (self ._dtype )
162165 self ._mmap = mmap
163- self ._keep_file_open = self ._should_keep_file_open (file_like ,
164- keep_file_open )
166+ # Flags to keep track of whether a single ImageOpener is created, and
167+ # whether a single underlying file handle is created.
168+ self ._keep_file_open , self ._persist_opener = \
169+ self ._should_keep_file_open (file_like , keep_file_open )
165170 self ._lock = RLock ()
166171
167172 def __del__ (self ):
@@ -184,16 +189,64 @@ def __setstate__(self, state):
184189 self ._lock = RLock ()
185190
186191 def _should_keep_file_open (self , file_like , keep_file_open ):
187- """Called by ``__init__``, and used to determine the final value of
188- ``keep_file_open``.
192+ """Called by ``__init__``.
193+
194+ This method determines how to manage ``ImageOpener`` instances,
195+ and the underlying file handles - the behaviour depends on:
196+
197+ - whether ``file_like`` is an an open file handle, or a path to a
198+ ``'.gz'`` file, or a path to a non-gzip file.
199+ - whether ``indexed_gzip`` is present (see
200+ :attr:`.openers.HAVE_INDEXED_GZIP`).
201+
202+ An ``ArrayProxy`` object uses two internal flags to manage
203+ ``ImageOpener`` instances and underlying file handles.
204+
205+ - The ``_persist_opener`` flag controls whether a single
206+ ``ImageOpener`` should be created and used for the lifetime of
207+ this ``ArrayProxy``, or whether separate ``ImageOpener`` instances
208+ should be created on each file access.
209+
210+ - The ``_keep_file_open`` flag controls qwhether the underlying file
211+ handle should be kept open for the lifetime of this
212+ ``ArrayProxy``, or whether the file handle should be (re-)opened
213+ and closed on each file access.
214+
215+ The internal ``_keep_file_open`` flag is only relevant if
216+ ``file_like`` is a ``'.gz'`` file, and the ``indexed_gzip`` library is
217+ present.
189218
190- The return value is derived from these rules:
219+ This method returns the values to be used for the internal
220+ ``_persist_opener`` and ``_keep_file_open`` flags; these values are
221+ derived according to the following rules:
191222
192- - If ``file_like`` is a file(-like) object, ``False`` is returned.
193- Otherwise, ``file_like`` is assumed to be a file name.
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.
223+ 1. If ``file_like`` is a file(-like) object, both flags are set to
224+ ``False``.
225+
226+ 2. If ``keep_file_open`` (as passed to :meth:``__init__``) is
227+ ``True``, both internal flags are set to ``True``.
228+
229+ 3. If ``keep_file_open`` is ``False``, but ``file_like`` is not a path
230+ to a ``.gz`` file or ``indexed_gzip`` is not present, both flags
231+ are set to ``False``.
232+
233+ 4. If ``keep_file_open`` is ``False``, ``file_like`` is a path to a
234+ ``.gz`` file, and ``indexed_gzip`` is present, ``_persist_opener``
235+ is set to ``True``, and ``_keep_file_open`` is set to ``False``.
236+ In this case, file handle management is delegated to the
237+ ``indexed_gzip`` library.
238+
239+ 5. If ``keep_file_open`` is ``'auto'``, ``file_like`` is a path to a
240+ ``.gz`` file, and ``indexed_gzip`` is present, both internal flags
241+ are set to ``True``.
242+
243+ 6. If ``keep_file_open`` is ``'auto'``, and ``file_like`` is not a
244+ path to a ``.gz`` file, or ``indexed_gzip`` is not present, both
245+ internal flags are set to ``False``.
246+
247+ Note that a value of ``'auto'`` for ``keep_file_open`` will become
248+ deprecated behaviour in version 2.4.0, and support for ``'auto'`` will
249+ be removed in version 3.0.0.
197250
198251 Parameters
199252 ----------
@@ -206,8 +259,10 @@ def _should_keep_file_open(self, file_like, keep_file_open):
206259 Returns
207260 -------
208261
209- The value of ``keep_file_open`` that will be used by this
210- ``ArrayProxy``, and passed through to ``ImageOpener`` instances.
262+ A tuple containing:
263+ - ``keep_file_open`` flag to control persistence of file handles
264+ - ``persist_opener`` flag to control persistence of ``ImageOpener``
265+ objects.
211266 """
212267 if keep_file_open is None :
213268 keep_file_open = KEEP_FILE_OPEN_DEFAULT
@@ -216,12 +271,15 @@ def _should_keep_file_open(self, file_like, keep_file_open):
216271 '\' auto\' , True, False}' )
217272 # file_like is a handle - keep_file_open is irrelevant
218273 if hasattr (file_like , 'read' ) and hasattr (file_like , 'seek' ):
219- return False
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
274+ return False , False
275+ # if the file is a gzip file, and we have_indexed_gzip,
276+ have_igzip = openers .HAVE_INDEXED_GZIP and file_like .endswith ('.gz' )
277+ if keep_file_open == 'auto' :
278+ return have_igzip , have_igzip
279+ elif keep_file_open :
280+ return True , True
281+ else :
282+ return False , have_igzip
225283
226284 @property
227285 @deprecate_with_version ('ArrayProxy.header deprecated' , '2.2' , '3.0' )
@@ -269,13 +327,14 @@ def _get_fileobj(self):
269327 A newly created ``ImageOpener`` instance, or an existing one,
270328 which provides access to the file.
271329 """
272- if self ._keep_file_open :
330+ if self ._persist_opener :
273331 if not hasattr (self , '_opener' ):
274332 self ._opener = openers .ImageOpener (
275333 self .file_like , keep_open = self ._keep_file_open )
276334 yield self ._opener
277335 else :
278- with openers .ImageOpener (self .file_like ) as opener :
336+ with openers .ImageOpener (
337+ self .file_like , keep_open = False ) as opener :
279338 yield opener
280339
281340 def get_unscaled (self ):
0 commit comments