from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass from functools import wraps from typing import Dict, List, Callable from injectable import injectable, inject @dataclass class Event: pass @injectable(singleton=True) class SimpleEventBus: def __init__(self): self._handlers: Dict[type, List[Callable]] = {} self._executor = ThreadPoolExecutor() def publish(self, event: Event) -> None: for handler in self._handlers.get(type(event), []): # Fire-and-forget execution self._executor.submit(handler, event) def subscribe(self, event_type: type, handler: Callable) -> None: if event_type not in self._handlers: self._handlers[event_type] = [] self._handlers[event_type].append(handler) @staticmethod def on(event: type) -> Callable: def outer(func): inject(SimpleEventBus).subscribe(event, func) @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper return outer