xtr-event-dispatcher-contracts
The event dispatching contract, and nothing else — so a library that emits events installs nothing else.
Why?
A library that announces what happened — an order placed, a message consumed — should not decide who hears it. It takes an event dispatcher, hands it an event object, and leaves the listeners to the application that wires it. That needs the contract, not a dispatcher:
- 📣
EventDispatcherInterface—await dispatcher.dispatch(event), and the event comes back. - 🛑
Event/StoppableEventInterface— an event any listener can stop. - 🔎
ListenerIntrospectionInterface— which listeners an event has, and in which order. - 🧷
Listenerandevent_name_of()— what a listener is called with, and the name an event is known by. - 🪶 No dependencies.
from dataclasses import dataclass
from xtr_event_dispatcher_contracts import Event, EventDispatcherInterface
@dataclass(frozen=True)
class MessageConsumed(Event):
message_id: str
class Worker:
def __init__(self, events: EventDispatcherInterface) -> None:
self._events = events
async def handled(self, message_id: str) -> None:
await self._events.dispatch(MessageConsumed(message_id))
Install
uv add xtr-event-dispatcher-contracts
Requires Python 3.11+.
Who installs what
| Depends on | |
|---|---|
| A library that emits events | xtr-event-dispatcher-contracts at runtime. |
| An application | xtr-event-dispatcher, which implements this contract and wires listeners from a container. |
xtr-event-dispatcher re-exports Event, StoppableEventInterface,
ListenerIntrospectionInterface, Listener and event_name_of rather than redefining them, so a
library written against this package and an application using that one share the same objects.
Its own EventDispatcherInterface extends the one here with registering listeners.
The contract
class EventDispatcherInterface(Protocol):
async def dispatch(self, event: T, event_name: str | type | None = None) -> T: ...
- Names. Events are keyed by name: a string, or a class standing for
"<module>.<qualname>"(event_name_of). Without a name, an event is dispatched under its class's. - Order. Listeners run highest priority first, one after the other, each awaited before the next. An exception a listener raises reaches the caller unchanged.
- Stopping. An event that is a
StoppableEventInterfacestops reaching listeners once one stops it.Eventis the base class that does this, frozen dataclasses included:event.stop_propagation(). - Listeners (
Listener) are called with the event, its name and the dispatcher — as many of the three as they take, by position; an awaitable result is awaited.
ListenerIntrospectionInterface reads listeners without registering any: get_listeners(name)
in running order (or every event's, given no name), get_listener_priority(name, listener),
has_listeners(name=None).
Development
Developed in the python-xtr monorepo, under
packages/xtr-event-dispatcher-contracts; run the commands below from there. The
python-xtr-event-dispatcher-contracts repository is a read-only copy, so send issues and pull
requests to the monorepo.
uv sync
uv run ruff check && uv run ruff format --check && uv run basedpyright && uv run ty check && uv run pytest
License
MIT — see LICENSE.
Release files for xtr-event-dispatcher-contracts 1.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| xtr_event_dispatcher_contracts-1.4.0.tar.gz | 7.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| xtr_event_dispatcher_contracts-1.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.8 kB
Release files / xtr_event_dispatcher_contracts-1.4.0.tar.gz
| Download URL | xtr_event_dispatcher_contracts-1.4.0.tar.gz |
|---|---|
| Size | 7.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9b966947b6c28c59413d4c653539d79750d6f118dffbdf4305d6c8dd42a837c7
|
|
BLAKE2b-256 checksum How to use checksums |
02932364076c99628bac36700440f30e910d4134cf2c1d31cf2ac1f84500b0d7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency logRelease files / xtr_event_dispatcher_contracts-1.4.0-py3-none-any.whl
| Download URL | xtr_event_dispatcher_contracts-1.4.0-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
4011bcbc9b7d309f8957ea33a1682c9da764181006ecea51085e6c60a7921d43
|
|
BLAKE2b-256 checksum How to use checksums |
bde6d7b667bb90bba3792755ad1a67f00c1417f3861c70cb467a2e644e3f40a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 28, 2026.
Transparency log