From 2405d670fb696f6e10fc07f8a6c8dc6c3aa8230c Mon Sep 17 00:00:00 2001 From: John Lancaster <32917998+jsl12@users.noreply.github.com> Date: Sat, 21 Feb 2026 19:00:10 -0600 Subject: [PATCH] broke apart --- src/hooked_containers/common.py | 33 ++++++++++++++ src/hooked_containers/events.py | 24 +++++++++++ src/hooked_containers/sequence.py | 71 +++++++------------------------ 3 files changed, 73 insertions(+), 55 deletions(-) create mode 100644 src/hooked_containers/common.py create mode 100644 src/hooked_containers/events.py diff --git a/src/hooked_containers/common.py b/src/hooked_containers/common.py new file mode 100644 index 0000000..a7d8d5f --- /dev/null +++ b/src/hooked_containers/common.py @@ -0,0 +1,33 @@ +from collections.abc import Callable, MutableSequence +from typing import Generic, TypeVar + +from . import events as e + +T = TypeVar("T") + + +class HookedContainer(Generic[T]): + _path: MutableSequence[int] + hook: Callable[[e.ChangeEvent[T]], None] | None + + 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, value): + return value in self._data + + def __setitem__(self, s, value): + self._data[s] = value + if self.hook: + self.hook(e.SetItemEvent(index=s, item=value)) + + def __delitem__(self, s): + del self._data[s] + if self.hook: + self.hook(e.RemoveItemEvent(index=s, item=self._data[s])) diff --git a/src/hooked_containers/events.py b/src/hooked_containers/events.py new file mode 100644 index 0000000..5f2136d --- /dev/null +++ b/src/hooked_containers/events.py @@ -0,0 +1,24 @@ +from dataclasses import dataclass +from typing import Generic, TypeVar + +T = TypeVar("T") + + +@dataclass(frozen=True) +class ChangeEvent(Generic[T]): + index: int + + +@dataclass(frozen=True) +class AddItemEvent(ChangeEvent[T]): + item: T + + +@dataclass(frozen=True) +class SetItemEvent(ChangeEvent[T]): + item: T + + +@dataclass(frozen=True) +class RemoveItemEvent(ChangeEvent[T]): + item: T diff --git a/src/hooked_containers/sequence.py b/src/hooked_containers/sequence.py index 6e89d63..4a4b3bf 100644 --- a/src/hooked_containers/sequence.py +++ b/src/hooked_containers/sequence.py @@ -1,7 +1,10 @@ from collections.abc import Callable, Iterable, MutableSequence, Sequence -from dataclasses import dataclass +from copy import copy from enum import Enum, auto -from typing import Generic, TypeVar +from typing import TypeVar + +from . import events as e +from .common import HookedContainer T = TypeVar("T") @@ -12,74 +15,32 @@ class ListChange(Enum): SET_ITEM = auto() -@dataclass(frozen=True) -class ChangeEvent(Generic[T]): - index: int - - -@dataclass(frozen=True) -class AddItemEvent(ChangeEvent[T]): - item: T - - -@dataclass(frozen=True) -class SetItemEvent(ChangeEvent[T]): - item: T - - -@dataclass(frozen=True) -class RemoveItemEvent(ChangeEvent[T]): - item: T - - -class HookedList(Generic[T], MutableSequence[T]): +class HookedList(HookedContainer[T], MutableSequence[T]): _data: MutableSequence[T] _path: MutableSequence[int] - hook: Callable[[ChangeEvent[T]], None] | None + hook: Callable[[e.ChangeEvent[T]], None] | None - def __init__(self, iterable: Iterable[T], path: Sequence[int] | None = None, *, hook=None): - match iterable: + def __init__(self, hook, existing: Iterable[T], path: Sequence[int] | None = None): + self.hook = hook + match existing: case MutableSequence() as seq: self._data = seq - case Iterable() as it: + case _ as it: self._data = list(it) self._path = list(path) if path is not None else [] - self.hook = hook - - def __repr__(self): - return f"{self.__class__.__name__}({self._data!r})" - - def __iter__(self): - return iter(self._data) - - def __contains__(self, value): - return value in self._data - - def __len__(self): - return len(self._data) def __getitem__(self, s): # print("Getting item:", s) match self._data[s]: - case HookedList() as hs: - hs.hook = self.hook - return hs case MutableSequence() as seq: - return HookedList(seq, hook=self.hook) + new_path = copy(self._path) + new_path.append(s) + # print(new_path) + return HookedList(self.hook, existing=seq, path=new_path) case _ as item: return item - def __setitem__(self, s, value): - self._data[s] = value - if self.hook: - self.hook(SetItemEvent(index=s, item=value)) - - def __delitem__(self, s): - del self._data[s] - if self.hook: - self.hook(RemoveItemEvent(index=s, item=self._data[s])) - def insert(self, index, value): self._data.insert(index, value) if self.hook: - self.hook(AddItemEvent(index=index, item=value)) + self.hook(e.AddItemEvent(index=index, item=value))