This file is indexed.

/usr/lib/python3/dist-packages/nibabel/arrayproxy.py is in python3-nibabel 2.2.1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# emacs: -*- mode: python-mode; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vi: set ft=python sts=4 ts=4 sw=4 et:
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
#
#   See COPYING file distributed along with the NiBabel package for the
#   copyright and license terms.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
""" Array proxy base class

The proxy API is - at minimum:

* The object has a read-only attribute ``shape``
* read only ``is_proxy`` attribute / property set to True
* the object returns the data array from ``np.asarray(prox)``
* returns array slice from ``prox[<slice_spec>]`` where ``<slice_spec>`` is any
  ndarray slice specification that does not use numpy 'advanced indexing'.
* modifying no object outside ``obj`` will affect the result of
  ``np.asarray(obj)``.  Specifically:

  * Changes in position (``obj.tell()``) of passed file-like objects will
    not affect the output of from ``np.asarray(proxy)``.
  * if you pass a header into the __init__, then modifying the original
    header will not affect the result of the array return.

See :mod:`nibabel.tests.test_proxy_api` for proxy API conformance checks.
"""
from contextlib import contextmanager
from threading import RLock

import numpy as np

from .deprecated import deprecate_with_version
from .volumeutils import array_from_file, apply_read_scaling
from .fileslice import fileslice
from .keywordonly import kw_only_meth
from .openers import ImageOpener, HAVE_INDEXED_GZIP


"""This flag controls whether a new file handle is created every time an image
is accessed through an ``ArrayProxy``, or a single file handle is created and
used for the lifetime of the ``ArrayProxy``. It should be set to one of
``True``, ``False``, or ``'auto'``.

If ``True``, a single file handle is created and used. If ``False``, a new
file handle is created every time the image is accessed. If ``'auto'``, and
the optional ``indexed_gzip`` dependency is present, a single file handle is
created and persisted. If ``indexed_gzip`` is not available, behaviour is the
same as if ``keep_file_open is False``.

If this is set to any other value, attempts to create an ``ArrayProxy`` without
specifying the ``keep_file_open`` flag will result in a ``ValueError`` being
raised.
"""
KEEP_FILE_OPEN_DEFAULT = False


class ArrayProxy(object):
    """ Class to act as proxy for the array that can be read from a file

    The array proxy allows us to freeze the passed fileobj and header such that
    it returns the expected data array.

    This implementation assumes a contiguous array in the file object, with one
    of the numpy dtypes, starting at a given file position ``offset`` with
    single ``slope`` and ``intercept`` scaling to produce output values.

    The class ``__init__`` requires a spec which defines how the data will be
    read and rescaled. The spec may be a tuple of length 2 - 5, containing the
    shape, storage dtype, offset, slope and intercept, or a ``header`` object
    with methods:

    * get_data_shape
    * get_data_dtype
    * get_data_offset
    * get_slope_inter

    A header should also have a 'copy' method.  This requirement will go away
    when the deprecated 'header' propoerty goes away.

    This implementation allows us to deal with Analyze and its variants,
    including Nifti1, and with the MGH format.

    Other image types might need more specific classes to implement the API.
    See :mod:`nibabel.minc1`, :mod:`nibabel.ecat` and :mod:`nibabel.parrec` for
    examples.
    """
    # Assume Fortran array memory layout
    order = 'F'
    _header = None

    @kw_only_meth(2)
    def __init__(self, file_like, spec, mmap=True, keep_file_open=None):
        """Initialize array proxy instance

        Parameters
        ----------
        file_like : object
            File-like object or filename. If file-like object, should implement
            at least ``read`` and ``seek``.
        spec : object or tuple
            Tuple must have length 2-5, with the following values:

            #. shape: tuple - tuple of ints describing shape of data;
            #. storage_dtype: dtype specifier - dtype of array inside proxied
               file, or input to ``numpy.dtype`` to specify array dtype;
            #. offset: int - offset, in bytes, of data array from start of file
               (default: 0);
            #. slope: float - scaling factor for resulting data (default: 1.0);
            #. inter: float - intercept for rescaled data (default: 0.0).

            OR

            Header object implementing ``get_data_shape``, ``get_data_dtype``,
            ``get_data_offset``, ``get_slope_inter``
        mmap : {True, False, 'c', 'r'}, optional, keyword only
            `mmap` controls the use of numpy memory mapping for reading data.
            If False, do not try numpy ``memmap`` for data array.  If one of
            {'c', 'r'}, try numpy memmap with ``mode=mmap``.  A `mmap` value of
            True gives the same behavior as ``mmap='c'``.  If `file_like`
            cannot be memory-mapped, ignore `mmap` value and read array from
            file.
        keep_file_open : { None, 'auto', True, False }, optional, keyword only
            `keep_file_open` controls whether a new file handle is created
            every time the image is accessed, or a single file handle is
            created and used for the lifetime of this ``ArrayProxy``. If
            ``True``, a single file handle is created and used. If ``False``,
            a new file handle is created every time the image is accessed. If
            ``'auto'``, and the optional ``indexed_gzip`` dependency is
            present, a single file handle is created and persisted. If
            ``indexed_gzip`` is not available, behaviour is the same as if
            ``keep_file_open is False``. If ``file_like`` is an open file
            handle, this setting has no effect. The default value (``None``)
            will result in the value of ``KEEP_FILE_OPEN_DEFAULT`` being used.
        """
        if mmap not in (True, False, 'c', 'r'):
            raise ValueError("mmap should be one of {True, False, 'c', 'r'}")
        self.file_like = file_like
        if hasattr(spec, 'get_data_shape'):
            slope, inter = spec.get_slope_inter()
            par = (spec.get_data_shape(),
                   spec.get_data_dtype(),
                   spec.get_data_offset(),
                   1. if slope is None else slope,
                   0. if inter is None else inter)
            # Reference to original header; we will remove this soon
            self._header = spec.copy()
        elif 2 <= len(spec) <= 5:
            optional = (0, 1., 0.)
            par = spec + optional[len(spec) - 2:]
        else:
            raise TypeError('spec must be tuple of length 2-5 or header object')

        # Copies of values needed to read array
        self._shape, self._dtype, self._offset, self._slope, self._inter = par
        # Permit any specifier that can be interpreted as a numpy dtype
        self._dtype = np.dtype(self._dtype)
        self._mmap = mmap
        self._keep_file_open = self._should_keep_file_open(file_like,
                                                           keep_file_open)
        self._lock = RLock()

    def __del__(self):
        """If this ``ArrayProxy`` was created with ``keep_file_open=True``,
        the open file object is closed if necessary.
        """
        if hasattr(self, '_opener') and not self._opener.closed:
            self._opener.close_if_mine()
            self._opener = None

    def __getstate__(self):
        """Returns the state of this ``ArrayProxy`` during pickling. """
        state = self.__dict__.copy()
        state.pop('_lock', None)
        return state

    def __setstate__(self, state):
        """Sets the state of this ``ArrayProxy`` during unpickling. """
        self.__dict__.update(state)
        self._lock = RLock()

    def _should_keep_file_open(self, file_like, keep_file_open):
        """Called by ``__init__``, and used to determine the final value of
        ``keep_file_open``.

        The return value is derived from these rules:

          - If ``file_like`` is a file(-like) object, ``False`` is returned.
            Otherwise, ``file_like`` is assumed to be a file name.
          - if ``file_like`` ends with ``'gz'``, and the ``indexed_gzip``
            library is available, ``True`` is returned.
          - Otherwise, ``False`` is returned.

        Parameters
        ----------

        file_like : object
            File-like object or filename, as passed to ``__init__``.
        keep_file_open : { 'auto', True, False }
            Flag as passed to ``__init__``.

        Returns
        -------

        The value of ``keep_file_open`` that will be used by this
        ``ArrayProxy``.
        """
        if keep_file_open is None:
            keep_file_open = KEEP_FILE_OPEN_DEFAULT
        # if keep_file_open is True/False, we do what the user wants us to do
        if isinstance(keep_file_open, bool):
            return keep_file_open
        if keep_file_open != 'auto':
            raise ValueError('keep_file_open should be one of {None, '
                             '\'auto\', True, False}')

        # file_like is a handle - keep_file_open is irrelevant
        if hasattr(file_like, 'read') and hasattr(file_like, 'seek'):
            return False
        # Otherwise, if file_like is gzipped, and we have_indexed_gzip, we set
        # keep_file_open to True, else we set it to False
        return HAVE_INDEXED_GZIP and file_like.endswith('gz')

    @property
    @deprecate_with_version('ArrayProxy.header deprecated', '2.2', '3.0')
    def header(self):
        return self._header

    @property
    def shape(self):
        return self._shape

    @property
    def dtype(self):
        return self._dtype

    @property
    def offset(self):
        return self._offset

    @property
    def slope(self):
        return self._slope

    @property
    def inter(self):
        return self._inter

    @property
    def is_proxy(self):
        return True

    @contextmanager
    def _get_fileobj(self):
        """Create and return a new ``ImageOpener``, or return an existing one.

        The specific behaviour depends on the value of the ``keep_file_open``
        flag that was passed to ``__init__``.

        Yields
        ------
        ImageOpener
            A newly created ``ImageOpener`` instance, or an existing one,
            which provides access to the file.
        """
        if self._keep_file_open:
            if not hasattr(self, '_opener'):
                self._opener = ImageOpener(self.file_like, keep_open=True)
            yield self._opener
        else:
            with ImageOpener(self.file_like, keep_open=False) as opener:
                yield opener

    def get_unscaled(self):
        """ Read of data from file

        This is an optional part of the proxy API
        """
        with self._get_fileobj() as fileobj, self._lock:
            raw_data = array_from_file(self._shape,
                                       self._dtype,
                                       fileobj,
                                       offset=self._offset,
                                       order=self.order,
                                       mmap=self._mmap)
        return raw_data

    def __array__(self):
        # Read array and scale
        raw_data = self.get_unscaled()
        return apply_read_scaling(raw_data, self._slope, self._inter)

    def __getitem__(self, slicer):
        with self._get_fileobj() as fileobj:
            raw_data = fileslice(fileobj,
                                 slicer,
                                 self._shape,
                                 self._dtype,
                                 self._offset,
                                 order=self.order,
                                 lock=self._lock)
        # Upcast as necessary for big slopes, intercepts
        return apply_read_scaling(raw_data, self._slope, self._inter)

    def reshape(self, shape):
        """ Return an ArrayProxy with a new shape, without modifying data """
        size = np.prod(self._shape)

        # Calculate new shape if not fully specified
        from operator import mul
        from functools import reduce
        n_unknowns = len([e for e in shape if e == -1])
        if n_unknowns > 1:
            raise ValueError("can only specify one unknown dimension")
        elif n_unknowns == 1:
            known_size = reduce(mul, shape, -1)
            unknown_size = size // known_size
            shape = tuple(unknown_size if e == -1 else e for e in shape)

        if np.prod(shape) != size:
            raise ValueError("cannot reshape array of size {:d} into shape "
                             "{!s}".format(size, shape))
        return self.__class__(file_like=self.file_like,
                              spec=(shape, self._dtype, self._offset,
                                    self._slope, self._inter),
                              mmap=self._mmap)


def is_proxy(obj):
    """ Return True if `obj` is an array proxy
    """
    try:
        return obj.is_proxy
    except AttributeError:
        return False


def reshape_dataobj(obj, shape):
    """ Use `obj` reshape method if possible, else numpy reshape function
    """
    return (obj.reshape(shape) if hasattr(obj, 'reshape')
            else np.reshape(obj, shape))