Aluode/PerceptionLabPortable
0
1"""Bunch-related classes."""2 3# Authors: The MNE-Python contributors.4# License: BSD-3-Clause5# Copyright the MNE-Python contributors.6 7from copy import deepcopy8 9###############################################################################10# Create a Bunch class that acts like a struct (mybunch.key = val)11 12 13class Bunch(dict):14 """Dictionary-like object that exposes its keys as attributes."""15 16 def __init__(self, **kwargs):17 dict.__init__(self, kwargs)18 self.__dict__ = self19 20 21###############################################################################22# A protected version that prevents overwriting23 24 25class BunchConst(Bunch):26 """Class to prevent us from re-defining constants (DRY)."""27 28 def __setitem__(self, key, val): # noqa: D10529 if key != "__dict__" and key in self:30 raise AttributeError(f"Attribute {repr(key)} already set")31 super().__setitem__(key, val)32 33 34###############################################################################35# A version that tweaks the __repr__ of its values based on keys36 37 38class BunchConstNamed(BunchConst):39 """Class to provide nice __repr__ for our integer constants.40 41 Only supports string keys and int or float values.42 """43 44 def __setattr__(self, attr, val): # noqa: D10545 assert isinstance(attr, str)46 if isinstance(val, int):47 val = NamedInt(attr, val)48 elif isinstance(val, float):49 val = NamedFloat(attr, val)50 else:51 assert isinstance(val, BunchConstNamed), type(val)52 super().__setattr__(attr, val)53 54 55class _Named:56 """Provide shared methods for giving named-representation subclasses."""57 58 def __new__(cls, name, val): # noqa: D102,D10559 out = _named_subclass(cls).__new__(cls, val)60 out._name = name61 return out62 63 def __str__(self): # noqa: D10564 return f"{self.__class__.mro()[-2](self)} ({self._name})"65 66 __repr__ = __str__67 68 # see https://stackoverflow.com/a/15774013/217596569 def __copy__(self): # noqa: D10570 cls = self.__class__71 result = cls.__new__(cls)72 result.__dict__.update(self.__dict__)73 return result74 75 def __deepcopy__(self, memo): # noqa: D10576 cls = self.__class__77 result = cls.__new__(cls, self._name, self)78 memo[id(self)] = result79 for k, v in self.__dict__.items():80 setattr(result, k, deepcopy(v, memo))81 return result82 83 def __getnewargs__(self): # noqa: D10584 return self._name, _named_subclass(self)(self)85 86 87def _named_subclass(klass):88 if not isinstance(klass, type):89 klass = klass.__class__90 subklass = klass.mro()[-2]91 assert subklass in (int, float)92 return subklass93 94 95class NamedInt(_Named, int):96 """Int with a name in __repr__."""97 98 pass # noqa99 100 101class NamedFloat(_Named, float):102 """Float with a name in __repr__."""103 104 pass # noqa105 