From 0c3dfde336fc89a13d737217ee87b8d07fa853c0 Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Sun, 22 Feb 2026 21:00:12 -0600 Subject: [PATCH] types module --- src/hooked_containers/__init__.py | 12 -------- src/hooked_containers/container.py | 10 ++----- src/hooked_containers/events.py | 20 ++++++++------ src/hooked_containers/mapping.py | 7 +++-- src/hooked_containers/sequence.py | 3 +- src/hooked_containers/state.py | 44 +++++++++++++++++++----------- src/hooked_containers/types.py | 6 ++++ 7 files changed, 55 insertions(+), 47 deletions(-) create mode 100644 src/hooked_containers/types.py diff --git a/src/hooked_containers/__init__.py b/src/hooked_containers/__init__.py index e1a4a41..e69de29 100644 --- a/src/hooked_containers/__init__.py +++ b/src/hooked_containers/__init__.py @@ -1,12 +0,0 @@ -from .container import HookedContainer, HookFunction -from .mapping import HookedMapping -from .sequence import HookedList -from .state import NameSpaceState - -__all__ = [ - "HookedContainer", - "HookFunction", - "HookedMapping", - "HookedList", - "NameSpaceState", -] diff --git a/src/hooked_containers/container.py b/src/hooked_containers/container.py index 7376de6..9d37f55 100644 --- a/src/hooked_containers/container.py +++ b/src/hooked_containers/container.py @@ -1,17 +1,13 @@ from abc import ABC, abstractmethod from collections.abc import Container, Iterable, MutableMapping, MutableSequence, Sized -from typing import Protocol, TypeVar +from typing import TypeVar from . import events as e +from .events import HookFunction +from .types import MutableNesting 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] diff --git a/src/hooked_containers/events.py b/src/hooked_containers/events.py index 70cc102..1da2798 100644 --- a/src/hooked_containers/events.py +++ b/src/hooked_containers/events.py @@ -1,26 +1,30 @@ from collections.abc import MutableSequence -from dataclasses import dataclass -from typing import Generic, TypeVar +from dataclasses import dataclass, field +from typing import Generic, Protocol, TypeVar + +from .types import MutableNesting T = TypeVar("T") @dataclass(frozen=True) class ChangeEvent(Generic[T]): + root: MutableNesting[T] = field(repr=False) path: MutableSequence[int] item: T @dataclass(frozen=True) -class AddItemEvent(ChangeEvent[T]): - item: T +class AddItemEvent(ChangeEvent[T]): ... @dataclass(frozen=True) -class SetItemEvent(ChangeEvent[T]): - item: T +class SetItemEvent(ChangeEvent[T]): ... @dataclass(frozen=True) -class RemoveItemEvent(ChangeEvent[T]): - item: T +class RemoveItemEvent(ChangeEvent[T]): ... + + +class HookFunction(Protocol): + def __call__(self, event: ChangeEvent[T]) -> None: ... diff --git a/src/hooked_containers/mapping.py b/src/hooked_containers/mapping.py index 2eb67f4..6327cf7 100644 --- a/src/hooked_containers/mapping.py +++ b/src/hooked_containers/mapping.py @@ -2,7 +2,8 @@ from collections.abc import MutableMapping, MutableSequence from typing import TypeVar from . import events as e -from .container import HookedContainer, HookFunction, MutableNesting +from .container import HookedContainer, MutableNesting +from .events import HookFunction T = TypeVar("T") @@ -46,7 +47,7 @@ class HookedMapping(HookedContainer[T], MutableMapping[T, MutableNesting[T]]): suppress_hook: bool = False, ) -> 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) + event = e.SetItemEvent(self, new_path, value) if key in self._data else e.AddItemEvent(self, new_path, value) match value: case HookedMapping(_data=value): self._data[key] = value @@ -59,4 +60,4 @@ class HookedMapping(HookedContainer[T], MutableMapping[T, MutableNesting[T]]): def __delitem__(self, key: T) -> None: item = self._data.pop(key) if self.hook: - self.hook(e.RemoveItemEvent(self.new_path(key), item)) + self.hook(e.RemoveItemEvent(self, self.new_path(key), item)) diff --git a/src/hooked_containers/sequence.py b/src/hooked_containers/sequence.py index bd57a6c..3c17cc9 100644 --- a/src/hooked_containers/sequence.py +++ b/src/hooked_containers/sequence.py @@ -3,7 +3,8 @@ from copy import copy from typing import TypeVar from . import events as e -from .container import HookedContainer, HookFunction +from .container import HookedContainer +from .types import HookFunction T = TypeVar("T") diff --git a/src/hooked_containers/state.py b/src/hooked_containers/state.py index c9a7a23..b384ce2 100644 --- a/src/hooked_containers/state.py +++ b/src/hooked_containers/state.py @@ -1,31 +1,43 @@ +from collections.abc import MutableMapping from datetime import datetime from .mapping import HookedMapping class EntityState(HookedMapping[str]): - pass + def __setitem__(self, key, value): + super().__setitem__(key, value) + super().__setitem__("last_changed", datetime.now(), suppress_hook=True) class DomainState(HookedMapping[str]): - _data: EntityState + _data: MutableMapping[str, EntityState] - def __setitem__(self, key, value): - super().__setitem__(key, value) - super().__setitem__("last_changed", datetime.now()) + def __getitem__(self, key): + match super().__getitem__(key): + case HookedMapping(_data=val): + return EntityState(val, hook=self.hook, path=self.new_path(key)) + case _ as val: + raise TypeError(f"Expected a mapping for domain state, got {type(val)}") class NameSpaceState(HookedMapping[str]): - _data: DomainState - - def __iter__(self): - return super().__iter__() - - def __setitem__(self, key, value): - super().__setitem__(key, value) - # print("ns SetItem") + _data: MutableMapping[str, DomainState] def __getitem__(self, key): - val = super().__getitem__(key) - # print("ns GetItem") - return DomainState(val, hook=self.hook, path=self.new_path(key)) + match super().__getitem__(key): + case HookedMapping(_data=val): + return DomainState(val, hook=self.hook, path=self.new_path(key)) + case _ as val: + raise TypeError(f"Expected a mapping for domain state, got {type(val)}") + + +class FullState(HookedMapping[str]): + _data: MutableMapping[str, NameSpaceState] + + def __getitem__(self, key): + match super().__getitem__(key): + case HookedMapping(_data=val): + return NameSpaceState(val, hook=self.hook, path=self.new_path(key)) + case _ as val: + raise TypeError(f"Expected a mapping for namespace state, got {type(val)}") diff --git a/src/hooked_containers/types.py b/src/hooked_containers/types.py new file mode 100644 index 0000000..9aebafa --- /dev/null +++ b/src/hooked_containers/types.py @@ -0,0 +1,6 @@ +from collections.abc import MutableMapping, MutableSequence +from typing import TypeVar + +T = TypeVar("T") + +type MutableNesting[T] = T | MutableSequence[T] | MutableMapping[T, MutableNesting[T]]