Skip to content

Signals

dorm.signals.Signal

Minimal signal/event dispatcher (pre/post save/delete).

Receivers may be either regular functions or async def coroutine functions:

  • :meth:send invokes synchronous receivers in order. Coroutine receivers are skipped with a single WARNING log 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:asend invokes both kinds: synchronous receivers are called directly, coroutine receivers are awaited sequentially in the order they were connected. The dispatch order matches :meth:send so 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.

dorm.signals.pre_save = Signal() module-attribute

dorm.signals.post_save = Signal() module-attribute

dorm.signals.pre_delete = Signal() module-attribute

dorm.signals.post_delete = Signal() module-attribute

dorm.signals.pre_query = Signal() module-attribute

dorm.signals.post_query = Signal() module-attribute