File: _bunch.py

package info (click to toggle)
python-mne 0.19.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 100,440 kB
  • sloc: python: 120,243; pascal: 1,861; makefile: 225; sh: 15
file content (104 lines) | stat: -rw-r--r-- 3,058 bytes parent folder | download
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
# -*- coding: utf-8 -*-
"""Bunch-related classes."""
# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
#          Eric Larson <larson.eric.d@gmail.com>
#          Joan Massich <mailsik@gmail.com>
#
# License: BSD (3-clause)

from copy import deepcopy


###############################################################################
# Create a Bunch class that acts like a struct (mybunch.key = val)

class Bunch(dict):
    """Dictionary-like object that exposes its keys as attributes."""

    def __init__(self, **kwargs):  # noqa: D102
        dict.__init__(self, kwargs)
        self.__dict__ = self


###############################################################################
# A protected version that prevents overwriting

class BunchConst(Bunch):
    """Class to prevent us from re-defining constants (DRY)."""

    def __setattr__(self, attr, val):  # noqa: D105
        if attr != '__dict__' and hasattr(self, attr):
            raise AttributeError('Attribute "%s" already set' % attr)
        super().__setattr__(attr, val)


###############################################################################
# A version that tweaks the __repr__ of its values based on keys

class BunchConstNamed(BunchConst):
    """Class to provide nice __repr__ for our integer constants.

    Only supports string keys and int or float values.
    """

    def __setattr__(self, attr, val):  # noqa: D105
        assert isinstance(attr, str)
        if isinstance(val, int):
            val = NamedInt(attr, val)
        elif isinstance(val, float):
            val = NamedFloat(attr, val)
        else:
            assert isinstance(val, BunchConstNamed), type(val)
        super().__setattr__(attr, val)


class _Named(object):
    """Provide shared methods for giving named-representation subclasses."""

    def __new__(cls, name, val):  # noqa: D102,D105
        out = _named_subclass(cls).__new__(cls, val)
        out._name = name
        return out

    def __str__(self):  # noqa: D105
        return '%s (%s)' % (super().__str__(), self._name)

    __repr__ = __str__

    # see https://stackoverflow.com/a/15774013/2175965
    def __copy__(self):  # noqa: D105
        cls = self.__class__
        result = cls.__new__(cls)
        result.__dict__.update(self.__dict__)
        return result

    def __deepcopy__(self, memo):  # noqa: D105
        cls = self.__class__
        result = cls.__new__(cls, self._name, self)
        memo[id(self)] = result
        for k, v in self.__dict__.items():
            setattr(result, k, deepcopy(v, memo))
        return result

    def __getnewargs__(self):  # noqa: D105
        return self._name, _named_subclass(self)(self)


def _named_subclass(klass):
    if not isinstance(klass, type):
        klass = klass.__class__
    subklass = klass.mro()[-2]
    assert subklass in (int, float)
    return subklass


class NamedInt(_Named, int):
    """Int with a name in __repr__."""

    pass  # noqa


class NamedFloat(_Named, float):
    """Float with a name in __repr__."""

    pass  # noqa