blackbull.event¶
blackbull.event
¶
Event dispatch with three lifetime contracts.
Interceptors run in order and propagate failures. Blocking observers finish before emit returns but isolate failures. Detached observers do not extend the event lifetime; use blocking observers for cleanup that must finish first.
Event
dataclass
¶
An immutable message dispatched through EventDispatcher.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The event name (e.g. |
detail |
dict
|
Arbitrary per-event data. |
EventDispatcher
¶
Minimal Pub/Sub dispatcher with split interception/observation paths.
Interception handlers (intercept) are awaited in registration order;
their exceptions propagate to the emitter. Observation handlers (on)
are scheduled via asyncio.create_task (fire-and-forget) and their
exceptions are caught and logged — they never reach the emitter.
Observer tasks are tracked so they can be drained at shutdown via
aclose. The drain timeout is configured at construction time
(shutdown_timeout); any task still running after the timeout is
logged at WARNING and cancelled.
aclose()
async
¶
Wait up to shutdown_timeout for quiescence; warn and cancel remaining observers.
drain(timeout=5.0)
async
¶
Wait for observer quiescence, including tasks emitted by observers.
Return False on timeout, True on quiescence; never cancel pending work.
emit(event, *, timeout=None)
async
¶
Dispatch event to all registered handlers.
Delivery order:
- Interceptors — awaited in registration order; their exceptions propagate (and abort the remaining interceptors).
- Blocking observers — awaited in registration order; their exceptions are caught and logged.
- Detached observers — scheduled as independent tasks (isolated),
tracked so they can be drained at shutdown via
aclose.
timeout limits the wait for each interceptor and blocking
observer. On expiry, cancellation is requested, a warning
identifies the handler, and dispatch continues without waiting for
it to stop. None disables this limit.
intercept(event_name, handler)
¶
Register an interception handler for event_name.
on(event_name, handler, blocking=False)
¶
Register an observation handler for event_name.
With blocking=False (the default) the handler is scheduled as an
independent task when the event fires — it never delays the emitter.
With blocking=True the handler is awaited in registration order,
subject to emit()'s timeout and cancellation; its exceptions are
logged.