This file is indexed.

/usr/lib/python3/dist-packages/nibabel/wrapstruct.py is in python3-nibabel 2.0.2-2.

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
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# 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.
#
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
""" Class to wrap numpy structured array

============
 wrapstruct
============

The :class:`WrapStruct` class is a wrapper around a numpy structured array type.

It implements:

* Mappingness from the underlying structured array fields
* ``from_fileobj``, ``write_to`` methods to read and write data to fileobj
* A mechanism for setting checks and fixes to the data on object creation
* Endianness guessing, and on-the-fly swapping

The :class:`LabeledWrapStruct` subclass adds:

* A pretty printing mechanism whereby field values can be displayed as
  corresponding strings (see :meth:`LabeledWrapStruct.get_value_label` and
  :meth:`LabeledWrapStruct.__str_`)

Mappingness
-----------

You can access and set fields of the contained structarr using standard
__getitem__ / __setitem__ syntax:

    wrapped['field'] = 10

Wrapped structures also implement general mappingness:

    wrapped.keys()
    wrapped.items()
    wrapped.values()

Properties::

    .endianness (read only)
    .binaryblock (read only)
    .structarr (read only)

Methods::

    .as_byteswapped(endianness)
    .check_fix()
    .__str__
    .__eq__
    .__ne__
    .get_value_label(name)

Class methods::

    .diagnose_binaryblock
    .as_byteswapped(endianness)
    .write_to(fileobj)
    .from_fileobj(fileobj)
    .default_structarr() - return default structured array
    .guessed_endian(structarr) - return guessed endian code from this structarr

Class variables:
    template_dtype - native endian version of dtype for contained structarr

Consistency checks
------------------

We have a file, and we would like information as to whether there are any
problems with the binary data in this file, and whether they are fixable.
``WrapStruct`` can hold checks for internal consistency of the contained data::

   wrapped = WrapStruct.from_fileobj(open('myfile.bin'), check=False)
   dx_result = WrapStruct.diagnose_binaryblock(wrapped.binaryblock)

This will run all known checks, with no fixes, returning a string with
diagnostic output. See below for the ``check=False`` flag.

In creating a ``WrapStruct`` object, we often want to check the consistency of
the contained data.  The checks can test for problems of various levels of
severity.  If the problem is severe enough, it should raise an Error.  So, with
data that is consistent - no error::

   wrapped = WrapStruct.from_fileobj(good_fileobj)

whereas::

   wrapped = WrapStruct.from_fileobj(bad_fileobj)

would raise some error, with output to logging (see below).

If we want the created object, come what may::

   hdr = WrapStruct.from_fileobj(bad_fileobj, check=False)

We set the error level (the level of problem that the ``check=True``
versions will accept as OK) from global defaults::

   import nibabel as nib
   nib.imageglobals.error_level = 30

The same for logging::

   nib.imageglobals.logger = logger
"""
import numpy as np

from .volumeutils import (pretty_mapping, endian_codes, native_code,
                          swapped_code)
from . import imageglobals as imageglobals
from .batteryrunners import BatteryRunner


class WrapStructError(Exception):
    pass


class WrapStruct(object):
    # placeholder datatype
    template_dtype = np.dtype([('integer', 'i2')])

    def __init__(self,
                 binaryblock=None,
                 endianness=None,
                 check=True):
        ''' Initialize WrapStruct from binary data block

        Parameters
        ----------
        binaryblock : {None, string} optional
            binary block to set into object.  By default, None, in
            which case we insert the default empty block
        endianness : {None, '<','>', other endian code} string, optional
            endianness of the binaryblock.  If None, guess endianness
            from the data.
        check : bool, optional
            Whether to check content of binary data in initialization.
            Default is True.

        Examples
        --------
        >>> wstr1 = WrapStruct() # a default structure
        >>> wstr1.endianness == native_code
        True
        >>> wstr1['integer']
        array(0, dtype=int16)
        >>> wstr1['integer'] = 1
        >>> wstr1['integer']
        array(1, dtype=int16)
        '''
        if binaryblock is None:
            self._structarr = self.__class__.default_structarr(endianness)
            return
        # check size
        if len(binaryblock) != self.template_dtype.itemsize:
            raise WrapStructError('Binary block is wrong size')
        wstr = np.ndarray(shape=(),
                         dtype=self.template_dtype,
                         buffer=binaryblock)
        if endianness is None:
            endianness = self.__class__.guessed_endian(wstr)
        else:
            endianness = endian_codes[endianness]
        if endianness != native_code:
            dt = self.template_dtype.newbyteorder(endianness)
            wstr = np.ndarray(shape=(),
                             dtype=dt,
                             buffer=binaryblock)
        self._structarr = wstr.copy()
        if check:
            self.check_fix()
        return

    @classmethod
    def from_fileobj(klass, fileobj, endianness=None, check=True):
        ''' Return read structure with given or guessed endiancode

        Parameters
        ----------
        fileobj : file-like object
           Needs to implement ``read`` method
        endianness : None or endian code, optional
           Code specifying endianness of read data

        Returns
        -------
        wstr : WrapStruct object
           WrapStruct object initialized from data in fileobj
        '''
        raw_str = fileobj.read(klass.template_dtype.itemsize)
        return klass(raw_str, endianness, check)

    @property
    def binaryblock(self):
        ''' binary block of data as string

        Returns
        -------
        binaryblock : string
            string giving binary data block

        Examples
        --------
        >>> # Make default empty structure
        >>> wstr = WrapStruct()
        >>> len(wstr.binaryblock)
        2
        '''
        return self._structarr.tostring()

    def write_to(self, fileobj):
        ''' Write structure to fileobj

        Write starts at fileobj current file position.

        Parameters
        ----------
        fileobj : file-like object
           Should implement ``write`` method

        Returns
        -------
        None

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> from io import BytesIO
        >>> str_io = BytesIO()
        >>> wstr.write_to(str_io)
        >>> wstr.binaryblock == str_io.getvalue()
        True
        '''
        fileobj.write(self.binaryblock)

    @property
    def endianness(self):
        ''' endian code of binary data

        The endianness code gives the current byte order
        interpretation of the binary data.

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> code = wstr.endianness
        >>> code == native_code
        True

        Notes
        -----
        Endianness gives endian interpretation of binary data. It is
        read only because the only common use case is to set the
        endianness on initialization, or occasionally byteswapping the
        data - but this is done via the as_byteswapped method
        '''
        if self._structarr.dtype.isnative:
            return native_code
        return swapped_code

    def copy(self):
        ''' Return copy of structure

        >>> wstr = WrapStruct()
        >>> wstr['integer'] = 3
        >>> wstr2 = wstr.copy()
        >>> wstr2 is wstr
        False
        >>> wstr2['integer']
        array(3, dtype=int16)
        '''
        return self.__class__(
                self.binaryblock,
                self.endianness, check=False)

    def __eq__(self, other):
        ''' equality between two structures defined by binaryblock

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> wstr2 = WrapStruct()
        >>> wstr == wstr2
        True
        >>> wstr3 = WrapStruct(endianness=swapped_code)
        >>> wstr == wstr3
        True
        '''
        this_end = self.endianness
        this_bb = self.binaryblock
        try:
            other_end = other.endianness
            other_bb = other.binaryblock
        except AttributeError:
            return False
        if this_end == other_end:
            return this_bb == other_bb
        other_bb = other._structarr.byteswap().tostring()
        return this_bb == other_bb

    def __ne__(self, other):
        return not self == other

    def __getitem__(self, item):
        ''' Return values from structure data

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> wstr['integer'] == 0
        True
        '''
        return self._structarr[item]

    def __setitem__(self, item, value):
        ''' Set values in structured data

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> wstr['integer'] = 3
        >>> wstr['integer']
        array(3, dtype=int16)
        '''
        self._structarr[item] = value

    def __iter__(self):
        return iter(self.keys())

    def keys(self):
        ''' Return keys from structured data'''
        return list(self.template_dtype.names)

    def values(self):
        ''' Return values from structured data'''
        data = self._structarr
        return [data[key] for key in self.template_dtype.names]

    def items(self):
        ''' Return items from structured data'''
        return zip(self.keys(), self.values())

    def get(self, k, d=None):
        ''' Return value for the key k if present or d otherwise'''
        return (k in self.keys()) and self._structarr[k] or d

    def check_fix(self, logger=None, error_level=None):
        ''' Check structured data with checks '''
        if logger is None:
            logger = imageglobals.logger
        if error_level is None:
            error_level = imageglobals.error_level
        battrun = BatteryRunner(self.__class__._get_checks())
        self, reports = battrun.check_fix(self)
        for report in reports:
            report.log_raise(logger, error_level)

    @classmethod
    def diagnose_binaryblock(klass, binaryblock, endianness=None):
        ''' Run checks over binary data, return string '''
        wstr = klass(binaryblock, endianness=endianness, check=False)
        battrun = BatteryRunner(klass._get_checks())
        reports = battrun.check_only(wstr)
        return '\n'.join([report.message
                          for report in reports if report.message])

    @classmethod
    def guessed_endian(self, mapping):
        ''' Guess intended endianness from mapping-like ``mapping``

        Parameters
        ----------
        wstr : mapping-like
            Something implementing a mapping.  We will guess the endianness from
            looking at the field values

        Returns
        -------
        endianness : {'<', '>'}
           Guessed endianness of binary data in ``wstr``
        '''
        raise NotImplementedError

    @classmethod
    def default_structarr(klass, endianness=None):
        ''' Return structured array for default structure, with given endianness
        '''
        dt = klass.template_dtype
        if endianness is not None:
            endianness = endian_codes[endianness]
            dt = dt.newbyteorder(endianness)
        return np.zeros((), dtype=dt)

    @property
    def structarr(self):
        ''' Structured data, with data fields

        Examples
        --------
        >>> wstr1 = WrapStruct() # with default data
        >>> an_int = wstr1.structarr['integer']
        >>> wstr1.structarr = None
        Traceback (most recent call last):
           ...
        AttributeError: can't set attribute
        '''
        return self._structarr

    def __str__(self):
        ''' Return string representation for printing '''
        summary = "%s object, endian='%s'" % (self.__class__,
                                              self.endianness)
        return '\n'.join([summary, pretty_mapping(self)])

    def as_byteswapped(self, endianness=None):
        ''' return new byteswapped object with given ``endianness``

        Guaranteed to make a copy even if endianness is the same as
        the current endianness.

        Parameters
        ----------
        endianness : None or string, optional
           endian code to which to swap.  None means swap from current
           endianness, and is the default

        Returns
        -------
        wstr : ``WrapStruct``
           ``WrapStruct`` object with given endianness

        Examples
        --------
        >>> wstr = WrapStruct()
        >>> wstr.endianness == native_code
        True
        >>> bs_wstr = wstr.as_byteswapped()
        >>> bs_wstr.endianness == swapped_code
        True
        >>> bs_wstr = wstr.as_byteswapped(swapped_code)
        >>> bs_wstr.endianness == swapped_code
        True
        >>> bs_wstr is wstr
        False
        >>> bs_wstr == wstr
        True

        If you write to the resulting byteswapped data, it does not
        change the original.

        >>> bs_wstr['integer'] = 3
        >>> bs_wstr == wstr
        False

        If you swap to the same endianness, it returns a copy

        >>> nbs_wstr = wstr.as_byteswapped(native_code)
        >>> nbs_wstr.endianness == native_code
        True
        >>> nbs_wstr is wstr
        False
        '''
        current = self.endianness
        if endianness is None:
            if current == native_code:
                endianness = swapped_code
            else:
                endianness = native_code
        else:
            endianness = endian_codes[endianness]
        if endianness == current:
            return self.copy()
        wstr_data = self._structarr.byteswap()
        return self.__class__(wstr_data.tostring(),
                              endianness,
                              check=False)

    @classmethod
    def _get_checks(klass):
        ''' Return sequence of check functions for this class '''
        return ()


class LabeledWrapStruct(WrapStruct):
    """ A WrapStruct with some fields having value labels for printing etc
    """
    _field_recoders = {} # for recoding values for str

    def get_value_label(self, fieldname):
        ''' Returns label for coded field

        A coded field is an int field containing codes that stand for
        discrete values that also have string labels.

        Parameters
        ----------
        fieldname : str
           name of header field to get label for

        Returns
        -------
        label : str
           label for code value in header field `fieldname`

        Raises
        ------
        ValueError
            if field is not coded.

        Examples
        --------
        >>> from nibabel.volumeutils import Recoder
        >>> recoder = Recoder(((1, 'one'), (2, 'two')), ('code', 'label'))
        >>> class C(LabeledWrapStruct):
        ...     template_dtype = np.dtype([('datatype', 'i2')])
        ...     _field_recoders = dict(datatype = recoder)
        >>> hdr  = C()
        >>> hdr.get_value_label('datatype')
        '<unknown code 0>'
        >>> hdr['datatype'] = 2
        >>> hdr.get_value_label('datatype')
        'two'
        '''
        if not fieldname in self._field_recoders:
            raise ValueError('%s not a coded field' % fieldname)
        code = int(self._structarr[fieldname])
        try:
            return self._field_recoders[fieldname].label[code]
        except KeyError:
            return '<unknown code {0}>'.format(code)

    def __str__(self):
        ''' Return string representation for printing '''
        summary = "%s object, endian='%s'" % (self.__class__,
                                              self.endianness)
        def _getter(obj, key):
            try:
                return obj.get_value_label(key)
            except ValueError:
                return obj[key]

        return '\n'.join(
            [summary,
             pretty_mapping(self, _getter)])