Hooked Containers
Hooked Containers provides an abstract container foundation that calls a function hook whenever its contents are changed through its API. The aim is to retain familiar Python container operations while letting application code react to additions, replacements, and removals.
Mixin Design
The design follows Python's collections.abc abstract base classes. Separate mixin classes should build up functionality in layers, so a custom container can compose the behavior it needs:
| Layer | Responsibility |
|---|---|
| Container | Membership testing through __contains__. |
| Collection | Iteration and size through __iter__ and __len__, alongside membership. |
| Hook support | A shared callback and change-event reporting for mutation methods. |
| Sequence | Indexed access through __getitem__, with sequence helpers such as index and count. |
| Mutable sequence | Assignment, deletion, and insertion, supporting helpers such as append, extend, and pop. |
| Mapping | Key-based access, iteration over keys, and helpers such as keys, items, and values. |
| Mutable mapping | Assignment and deletion, supporting helpers such as update, setdefault, and pop. |
Sequences and mappings are separate branches built on the common container foundation, not successive steps in a single inheritance chain. Mutation hooks belong in the primitive operations so inherited ABC mixin methods also report changes. A compound operation may therefore invoke the hook more than once.
This layered separation is the intended architecture. Currently, HookedContainer combines shared container and hook behavior; HookedList and HookedMapping extend it with the standard library's MutableSequence and MutableMapping mixins, respectively.
Change Hooks
A hook receives one change-event object after a mutation. The event identifies the root container (root), the index or key path (path), and the affected value (item). The current event types are AddItemEvent, SetItemEvent, and RemoveItemEvent.
from hooked_containers.mapping import HookedMapping
from hooked_containers.sequence import HookedList
def on_change(event):
print(type(event).__name__, event.path, event.item)
items = HookedList(["first"], hook=on_change)
items.append("second") # AddItemEvent (1,) second
items[0] = "updated" # SetItemEvent (0,) updated
del items[1] # RemoveItemEvent (1,) second
settings = HookedMapping({}, hook=on_change)
settings["enabled"] = True # AddItemEvent ('enabled',) True
The wrappers can share existing mutable storage. Mutating that storage directly bypasses the hook; make changes through the hooked container to receive events. Hooks are optional, and nested wrappers retain the root and path for supported nested container types.
Status
Requires Python 3.12 or newer. This repository is an early implementation of the container and mixin design; the API and separation of responsibilities may evolve.