from abc import ABC, abstractmethod from collections.abc import Container, Iterable, MutableMapping, MutableSequence, Sized from copy import copy from typing import Protocol, TypeVar from . import events as e T = TypeVar("T") type MutableNesting[T] = T | MutableSequence[T] | MutableMapping[T, MutableNesting[T]] class HookFunction(Protocol): def __call__(self, event: e.ChangeEvent[T]) -> None: ... class HookedContainer(ABC, Sized, Iterable[T], Container[T]): _data: MutableNesting[T] _path: MutableSequence[int] _root: MutableMapping[T] | MutableSequence[T] | None = None hook: HookFunction def __repr__(self): return f"{self.__class__.__name__}({self._data!r})" def __len__(self): return len(self._data) def __iter__(self): return iter(self._data) def __contains__(self, x): return x in self._data # Sequence Methods # __contains__, __iter__, __reversed__, index, and count @abstractmethod def __getitem__(self, key): ... # MutableSequence Methods # append, reverse, extend, pop, remove, and __iadd__ def __setitem__(self, s, value): self._data[s] = value if self.hook: self.hook(e.SetItemEvent(self.new_path(s), value)) def __delitem__(self, s): item = self._data.pop(s) if self.hook: self.hook(e.RemoveItemEvent(self.new_path(s), item)) # @abstractmethod # def insert(self, index, value): ... # Custom Methods def new_path(self, key): new_path = copy(self._path) new_path.append(key) return new_path def get_nested(self, keys): """Recursively call __getitem__ with each key in the iterable.""" result = self for key in keys: result = result[key] return result