You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

50 lines
2.0 KiB
Python

# backend/app/automation/event_bus.py
# event_bus 정본(단일 고정) — post-mvp-overview.md §5.
# `from app.automation.event_bus import bus` (bus.publish('x', {...}))
# `from app.automation.event_bus import publish` (publish('x', {...}), emit 동일)
# 둘 다 정상. app/events.py·app/event_bus.py 같은 다른 경로는 정본이 아니다.
from __future__ import annotations
from collections import defaultdict
from collections.abc import Callable
from dataclasses import dataclass, field
from datetime import UTC, datetime
@dataclass
class Event:
type: str
payload: dict = field(default_factory=dict)
at: datetime = field(default_factory=lambda: datetime.now(UTC))
class EventBus:
"""in-process 동기 이벤트 버스. 핸들러는 (Event) -> None.
프로토타입은 publish 즉시 동기 디스패치. phase-14 worker 가 비동기/스케줄로 확장."""
def __init__(self) -> None:
self._subs: dict[str, list[Callable[[Event], None]]] = defaultdict(list)
self.history: list[Event] = [] # 테스트/디버그용 발행 기록
def subscribe(self, event_type: str, handler: Callable[[Event], None]) -> None:
self._subs[event_type].append(handler)
def publish(self, event_type: str, payload: dict | None = None) -> Event:
ev = Event(type=event_type, payload=payload or {})
self.history.append(ev)
for h in list(self._subs.get(event_type, [])):
try:
h(ev) # 한 구독자의 예외가 다른 구독자를 막지 않도록 격리
except Exception:
pass
return ev
# 전역 단일 버스(데모). 테스트는 새 인스턴스를 주입해 격리한다.
bus = EventBus()
# 모듈 레벨 편의 export — post-mvp-overview.md event_bus 정본.
publish = bus.publish # publish('automation.matched', {...})
subscribe = bus.subscribe # subscribe('automation.matched', handler)
emit = bus.publish # emit 은 publish 의 별칭(동일 동작)