""" Event bus - pub/sub messaging for decoupled component communication. """ import threading from collections import defaultdict from collections.abc import Callable from typing import Any from engine.events import EventType class EventBus: """Thread-safe event bus for publish-subscribe messaging.""" def __init__(self): self._subscribers: dict[EventType, list[Callable[[Any], None]]] = defaultdict( list ) self._lock = threading.Lock() def subscribe(self, event_type: EventType, callback: Callable[[Any], None]) -> None: """Register a callback for a specific event type.""" with self._lock: self._subscribers[event_type].append(callback) def unsubscribe( self, event_type: EventType, callback: Callable[[Any], None] ) -> None: """Remove a callback for a specific event type.""" with self._lock: if callback in self._subscribers[event_type]: self._subscribers[event_type].remove(callback) def publish(self, event_type: EventType, event: Any = None) -> None: """Publish an event to all subscribers.""" with self._lock: callbacks = list(self._subscribers.get(event_type, [])) for callback in callbacks: try: callback(event) except Exception: pass def clear(self) -> None: """Remove all subscribers.""" with self._lock: self._subscribers.clear() def subscriber_count(self, event_type: EventType | None = None) -> int: """Get subscriber count for an event type, or total if None.""" with self._lock: if event_type is None: return sum(len(cb) for cb in self._subscribers.values()) return len(self._subscribers.get(event_type, [])) _event_bus: EventBus | None = None def get_event_bus() -> EventBus: """Get the global event bus instance.""" global _event_bus if _event_bus is None: _event_bus = EventBus() return _event_bus def set_event_bus(bus: EventBus) -> None: """Set the global event bus instance (for testing).""" global _event_bus _event_bus = bus