Signals¶
dorm.signals.Signal
¶
Minimal signal/event dispatcher (pre/post save/delete).
Receivers may be either regular functions or async def coroutine
functions:
- :meth:
sendinvokes synchronous receivers in order. Coroutine receivers are skipped with a singleWARNINGlog line per call, since there is no event loop on the synchronous path to await them on. Connect them via :meth:asend(typically from :meth:Model.asave/ :meth:Model.adelete) instead. - :meth:
asendinvokes both kinds: synchronous receivers are called directly, coroutine receivers are awaited sequentially in the order they were connected. The dispatch order matches :meth:sendso receivers that depend on each other behave the same way under both entry points.
connect(receiver: Callable[..., Any] | Callable[..., Awaitable[Any]], sender: type | None = None, weak: bool = True, dispatch_uid: str | None = None) -> None
¶
Register receiver to be called when this signal fires.
receiver may be a regular callable or an async def
coroutine function. Coroutine receivers only fire from
:meth:asend; on the sync :meth:send path they are logged-and-
skipped, so registering one does not silently turn synchronous
Model.save() calls into a no-op for that receiver.
asend(sender: Any, **kwargs: Any) -> list[tuple[Callable[..., Any], Any]]
async
¶
Async dispatch. Awaits coroutine receivers; calls sync ones
directly. Order matches :meth:send.
Receivers are awaited sequentially, not concurrently, so two
receivers that share state (e.g. both write to a buffer) behave
the same as on the sync path. If you want concurrency, fan out
to asyncio.gather from inside one receiver.