Listeners Guide¶
The piethorn.collections.listener package provides a small event system for
attaching callbacks to function and method calls.
Core Pieces¶
ListenableBase class for objects that own named listeners.
ListenerA named callback chain. Each callback receives an
Eventand may returnFalseto stop the rest of that chain.EventRuntime context for a listener callback. It exposes
args,kwargs,returned,called_method,listener, andcaller.listens()Decorator that calls the wrapped function first, then dispatches the configured listener events.
Instance Listeners¶
Subclass Listenable, create named listeners in __init__(), and decorate
methods with @listens.
from piethorn.collections.listener import Listenable, listens
class Counter(Listenable):
def __init__(self):
super().__init__("changed")
self.value = 0
@listens("changed")
def set_value(self, value):
self.value = value
return self.value
counter = Counter()
seen = []
counter.add_listener(
"changed",
lambda event: seen.append((event.args, event.returned)) or True,
)
counter.set_value(3)
assert seen == [((3,), 3)]
For instance methods on Listenable objects, self is removed from
Event.args. The event records the meaningful arguments passed by the caller.
Global Listeners¶
Decorated plain functions, static methods, and class methods use
GLOBAL_LISTENERS when no Listenable instance is available.
from piethorn.collections.listener import GLOBAL_LISTENERS, listens
GLOBAL_LISTENERS.create("finished", replace=True)
GLOBAL_LISTENERS.add_listener(
"finished",
lambda event: print(event.returned) or True,
)
@listens("finished")
def run_task(name):
return f"{name} done"
run_task("build")
When an instance method is called on a Listenable object, the instance
listener wins. If the instance does not have a matching listener, the decorator
falls back to GLOBAL_LISTENERS when a global listener exists.
Multiple Events¶
A method can trigger more than one listener. Listeners fire in decorator order after the wrapped method returns.
class Store(Listenable):
def __init__(self):
super().__init__("saved", "changed")
@listens("saved", "changed")
def save(self, value):
return value
Callback Control¶
Callbacks control dispatch in three common ways:
return
Falseto stop the current listener’s remaining callback chaincall
event.stop_chain()to stop the listener chain after the current callback finishescall
event.end(force=True)to stop immediately by raisingEventEnd
calls = []
def first(event):
calls.append("first")
return False
def second(event):
calls.append("second")
return True
counter.add_listener("changed", first)
counter.add_listener("changed", second)
counter.set_value(4)
assert calls == ["first"]
event.stop_current() is most useful when a Listener is used as a
callback for another Listener and only the current nested chain should stop.
Recursion Behavior¶
listens() protects against recursive listener dispatch. By default,
recursive calls are allowed. Set allow_recurse=False to deny recursive event
dispatch for a decorated function.
@listens("changed", allow_recurse=False, throw_on_recurse_denied=False)
def update(value):
return value
When recursion is denied, the wrapper can raise RecursionError, return
None, or call the wrapped function directly, depending on
throw_on_recurse_denied and straight_call_on_recurse_denied.
Automatic Listener Creation¶
Listenable(auto_create=True) and ListenerHolder(auto_create=True) create
missing listeners when add_listener() is called.
from piethorn.collections.listener import ListenerHolder
holder = ListenerHolder(auto_create=True)
holder.add_listener("created_late", lambda event: True)
Manual Listener Registries¶
Use ListenerHolder when you need a standalone registry instead of a full
domain object.
holder = ListenerHolder("ready")
holder.add_listener("ready", lambda event: True)
holder.event_trigger("ready", (), {}, None, lambda: None)
Sequence Base Classes¶
ListenerSequence and MutableListenerSequence are abstract sequence base
classes that emit listeners from sequence operations.
ListenerSequence creates a get listener for __getitem__.
MutableListenerSequence adds add, set, and remove listeners for
insert(), __setitem__(), and __delitem__().
Inheritance¶
Listenable preserves listener metadata when a subclass overrides a decorated
member. This lets subclasses customize behavior without repeating the same
@listens declaration on every override.