"""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