File: _bunch.py

package info (click to toggle)
python-mne 1.9.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 131,492 kB
  • sloc: python: 213,302; javascript: 12,910; sh: 447; makefile: 144
file content (104 lines) | stat: -rw-r--r-- 2,936 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
"""Bunch-related classes."""

# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

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):
        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 __setitem__(self, key, val):  # noqa: D105
        if key != "__dict__" and key in self:
            raise AttributeError(f"Attribute {repr(key)} already set")
        super().__setitem__(key, 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:
    """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 f"{self.__class__.mro()[-2](self)} ({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