diff --git a/src/hooked_containers/container.py b/src/hooked_containers/container.py index e80ea15..e76483f 100644 --- a/src/hooked_containers/container.py +++ b/src/hooked_containers/container.py @@ -15,6 +15,7 @@ class HookFunction(Protocol): class HookedContainer(ABC, Sized, Iterable[T], Container[T]): + _data: MutableNesting[T] _path: MutableSequence[int] _root: MutableMapping[T] | MutableSequence[T] | None = None hook: HookFunction @@ -22,15 +23,21 @@ class HookedContainer(ABC, Sized, Iterable[T], Container[T]): 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): ... - def __len__(self): - return len(self._data) - # MutableSequence Methods # append, reverse, extend, pop, remove, and __iadd__ @@ -44,8 +51,8 @@ class HookedContainer(ABC, Sized, Iterable[T], Container[T]): if self.hook: self.hook(e.RemoveItemEvent(self.new_path(s), item)) - @abstractmethod - def insert(self, index, value): ... + # @abstractmethod + # def insert(self, index, value): ... # Custom Methods diff --git a/src/hooked_containers/mapping.py b/src/hooked_containers/mapping.py index e19ce6b..f993329 100644 --- a/src/hooked_containers/mapping.py +++ b/src/hooked_containers/mapping.py @@ -16,52 +16,41 @@ class HookedMapping(HookedContainer[T], MutableMapping[T, MutableNesting[T]]): hook: HookFunction | None = None, path: MutableSequence[int] | None = None, ): - self.hook = hook match existing: case HookedMapping(_data=seq): self._data = seq case MutableMapping() as seq: self._data = seq - case _ as it: - self._data = dict(it) + case _: + raise TypeError(f"Expected a mapping, got {type(existing)}") + # self._data = dict(it) self._data = existing + self.hook = hook self._path = list(path) if path is not None else [] - # MutableMapping Methods - - def __iter__(self): - return iter(self._data) - - def insert(self, key: T, value: MutableNesting[T]) -> None: - self._data[key] = value - if self.hook: - self.hook(e.AddItemEvent(self.new_path(key), value)) - - # HookedContainer methods - def __getitem__(self, key: T) -> MutableNesting[T]: if key not in self._data: - self.insert(key, {}) - return HookedMapping(self._data[key], hook=self.hook, path=self.new_path(key)) + return self.__setitem__(key, {}) value = self._data[key] match value: - case HookedMapping(_data=seq): - return type(self)(seq, hook=self.hook, path=self.new_path(key)) case MutableMapping() as mapping: return HookedMapping(mapping, hook=self.hook, path=self.new_path(key)) case _ as item: return item - def __setitem__(self, key: T, value: MutableNesting[T]) -> None: - if key not in self._data: - self.insert(key, value) - else: - self._data[key] = value - if self.hook: - self.hook(e.SetItemEvent(self.new_path(key), value)) + def __setitem__(self, key: T, value: MutableNesting[T]) -> MutableNesting[T] | None: + new_path = self.new_path(key) + event = e.SetItemEvent(new_path, value) if key in self._data else e.AddItemEvent(new_path, value) + match value: + case HookedMapping(_data=value): + self._data[key] = value + case _: + self._data[key] = value + if self.hook: + self.hook(event) + return value def __delitem__(self, key: T) -> None: - item = self._data[key] - del self._data[key] + item = self._data.pop(key) if self.hook: self.hook(e.RemoveItemEvent(self.new_path(key), item))