Introduction
PyESys is a Python-native event system that provides thread-safe, type-safe event handling with first-class async support. Designed for real-world applications requiring robust concurrency, simulation, or external control mechanisms.
Basic Example:
from pyesys import create_event
# Define event signature
event, listener = create_event(example=lambda msg: None)
# Define handler
def log_message(msg: str):
print(f"Message received: {msg}")
# Subscribe handler
listener += log_message
# Emit event
event.emit("Hello PyESys!")
# Output: Message received: Hello PyESys!
Decorator Example:
from pyesys.prop import event
class Button:
@event
def on_click(self):
"""Click event signature"""
@on_click.emitter
def click(self):
"""Automatically emits after execution"""
print("Button pressed!")
def handle_click():
print("Action performed!")
btn = Button()
btn.on_click += handle_click
btn.click()
# Output:
# Button pressed!
# Action performed!
Why PyESys?
While Python has several event handling solutions, many common approaches present challenges for modern applications:
Event Bus Patterns: Many libraries use global event buses with string-based keys, which can create tight coupling and make per-instance event management complex. When each object instance needs its own events, you often need to manage ID strings or implement filtering logic, leading to unnecessary event triggers across unrelated instances.
Memory Management: Event systems with bound methods can suffer from memory leaks if not carefully designed with weak references and proper cleanup mechanisms.
Async/Sync Integration: Mixing synchronous and asynchronous event handlers consistently can be challenging, especially when you need both to coexist seamlessly.
Pythonic Design: Many event systems don’t leverage familiar Python patterns, making them feel foreign compared to built-in language features like property descriptors.
PyESys addresses these architectural challenges with a clean, intuitive API:
> “When controlling real-time systems, I needed clean event handling that works seamlessly across threads and async contexts while feeling natural to Python developers.”
Design Approach
PyESys draws inspiration from familiar Python patterns:
Property-like syntax: The @event decorator mirrors Python’s @property descriptor pattern, making it instantly familiar
Operator overloading: Uses += (subscribe) and -= (unsubscribe) following Python’s established conventions
Per-instance events: Each object gets its own event instances, eliminating global state and cross-instance interference
Type safety: Runtime signature validation through example functions
Memory conscious: Built-in weak reference handling prevents common memory leak patterns
Goals
Intuitive syntax leveraging familiar Python patterns
True per-instance events without global registries or string-based dispatch
Seamless sync/async interoperability
Automatic memory management for bound methods
Zero dependencies with pure Python implementation
Runtime signature validation for type safety
Thread-safe by design
Key Use Cases
Real-time systems React to sensor inputs/control signals
Simulation frameworks Decouple models from visualization/control
Plugin architectures Extend core functionality safely
UI/backend integration Bridge synchronous and asynchronous worlds
Testable systems Replace complex callbacks with observable events
Design Philosophy
Explicit over implicit Handler signatures enforced via example functions
Pythonic first Leverages familiar language patterns like descriptors and operator overloading
Concurrency-ready Thread-safe emission with async support
Resource-conscious Weak references prevent memory leaks
Composable Events work standalone or in complex systems