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
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 의 별칭(동일 동작)
|