Typed event system for small Python applications
Project description
kevent
Русский | English
kevent is a small, typed event system for Python applications. It provides a base Event type, declarative listeners, middleware hooks, and a lightweight handler that dispatches events by concrete type and by generic fallback.
What It Does
Eventis the base class for application eventsListenerbinds one event class to one callbacklistener()is a decorator for declarative listener registrationMiddlewareruns before listeners receive an eventEventHandlerstores listeners, runs middleware, and dispatches events
Quick Start
from kevent import Event, EventHandler, Middleware, listener
class UserLoggedIn(Event):
def __init__(self, username: str) -> None:
self.username = username
class TraceMiddleware(Middleware):
def __init__(self) -> None:
self.trace: list[str] = []
def process(self, event: Event) -> Event | None:
self.trace.append(type(event).__name__)
return event # Continue propagation
def on_login(event: UserLoggedIn) -> None:
print(f"welcome:{event.username}")
def on_any(event: Event) -> None:
print(f"received:{type(event).__name__}")
handler = EventHandler()
handler.add_middleware(TraceMiddleware())
handler.subscribe(listener(UserLoggedIn)(on_login))
handler.subscribe(listener(Event)(on_any))
handler.publish(UserLoggedIn("aria"))
CLI
Run a built-in demo that shows middleware and listener dispatch:
kevent demo "hello"
Expected output:
middleware:DemoEvent
listener:hello
fallback:DemoEvent
Installation
uv sync --dev
Development
Run tests:
just test
Run linting and type checks:
just check
Format code:
just fmt
Build release artifacts:
just build
Project Layout
kevent/
__init__.py
cli.py
event.py
handler.py
listener.py
middleware.py
examples/
quickstart.py
tests/
test_event_system.py
Notes
- The package has no runtime dependencies
- The public API is exported from kevent/init.py
- The CLI entry point is
kevent = "kevent.cli:main" - MRO-based dispatch is O(n) where n is the depth of the event class hierarchy; for deeply nested events, consider flattening or using explicit event types
- By default, exceptions in listeners propagate; use
on_errorto implement custom error strategies (logging, recovery, etc.)
Event Hierarchy and Dispatch
EventHandler.publish() uses method resolution order (MRO) to dispatch events. If you register a listener for a base event class, it will receive all subclass instances:
class UserEvent(Event):
pass
class UserLoggedIn(UserEvent):
pass
def on_user_event(event: UserEvent) -> None:
print(f"user event: {type(event).__name__}")
handler = EventHandler()
handler.subscribe(listener(UserEvent)(on_user_event))
handler.publish(UserLoggedIn("alice")) # Matches UserEvent via MRO
Middleware and Flow Control
Middleware can observe events and optionally cancel propagation by returning None:
class ValidationMiddleware(Middleware):
def process(self, event: Event) -> Event | None:
if not is_valid(event):
return None # Prevent listeners from receiving this event
return event
handler = EventHandler()
handler.add_middleware(ValidationMiddleware())
handler.publish(event) # Listeners won't run if validation fails
Error Handling
Catch exceptions in listeners without losing other events:
def on_error(exc: Exception, event: Event, listener_obj) -> None:
print(f"Error in {listener_obj}: {exc}")
handler = EventHandler(on_error=on_error)
handler.subscribe(listener(Event)(on_event)) # If this raises, on_error is called
handler.publish(event) # Other listeners still execute
Monitoring Listeners
Check how many listeners are registered:
handler = EventHandler()
print(handler.handlers_count) # 0
handler.subscribe(listener(Event)(lambda e: None))
print(handler.handlers_count) # 1
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file kevent-1.0.0.tar.gz.
File metadata
- Download URL: kevent-1.0.0.tar.gz
- Upload date:
- Size: 8.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2aa6402d0b8eaa93d8ee569dd62161c3889773efeec9646fb6b4199135b7f61
|
|
| MD5 |
5f6e4a6fba546e3c6b2391048a72b01e
|
|
| BLAKE2b-256 |
7ba3063471aefee6b299ffe276857de2b98fbfba0dac8f945b2a75c00dc556b5
|
File details
Details for the file kevent-1.0.0-py3-none-any.whl.
File metadata
- Download URL: kevent-1.0.0-py3-none-any.whl
- Upload date:
- Size: 7.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a7c23c7d27b527ca30b47303d33e84cc6217647272ab7e6795fc9fbab74cdc9
|
|
| MD5 |
6d61690d50c5a183038b60aa59bcc7a2
|
|
| BLAKE2b-256 |
92e43d2c077185656297dfda8b25cc923afc565c9b43192c820e9e18b33dc6ee
|