修改::Testing started at 下午6:35 ...
Launching pytest with arguments C:\Users\Administrator\Desktop\project\core\event_center.py --no-header --no-summary -q in C:\Users\Administrator\Desktop\project\core
============================= test session starts =============================
collecting ... collected 0 items
============================ no tests ran in 0.32s ============================
进程已结束,退出代码为 5
空套件 代码#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
优化后的事件中心模块 (最终版)
"""
import logging
import threading
import uuid
import time
from enum import Enum, auto
from typing import Tuple, Dict, List, Callable, Optional, Any, Set, DefaultDict, Union
from collections import defaultdict
from dataclasses import dataclass, field
from concurrent.futures import ThreadPoolExecutor
# 初始化日志
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
class EventType(Enum):
"""完整事件类型枚举(合并基础版和增强版)"""
# 基础事件类型
MODULE_RUN = auto()
ANALYSIS_RESULT = auto()
MODULE_ERROR = auto()
REGISTER_UI = auto()
GET_RESULTS = auto()
# 系统事件
SYSTEM_STARTUP = auto()
SYSTEM_SHUTDOWN = auto()
MODULE_READY = auto()
ERROR = auto()
# 模块特定事件
INPUT_ANALYSIS_START = auto()
INPUT_ANALYSIS_END = auto()
COMBINATION_ANALYSIS_START = auto()
COMBINATION_ANALYSIS_END = auto()
FOLLOW_ANALYSIS_START = auto()
FOLLOW_ANALYSIS_UPDATE = auto()
TREND_ANALYSIS_REQUEST = auto()
TREND_ANALYSIS_REPORT = auto()
NUMBER_GENERATION_START = auto()
NUMBER_GENERATION_FINISH = auto()
# 测试事件
TEST_EVENT = auto()
@dataclass
class Event:
"""事件数据类"""
type: Union[str, EventType] # 必须放在非默认参数位置
source: str # 必须放在非默认参数位置
target: Optional[str] = None
event_id: str = field(default_factory=lambda: str(uuid.uuid4()))
token: Optional[str] = None
data: Optional[Dict[str, Any]] = field(default_factory=dict)
timestamp: float = field(default_factory=time.time)
def __post_init__(self):
"""数据验证和类型转换"""
if isinstance(self.type, EventType):
self.type = self.type.name
if not isinstance(self.type, str) or not self.type:
raise ValueError("type 必须是非空字符串或EventType枚举")
class EventCenter:
"""线程安全的事件中心(最终优化版)"""
_instance = None
_lock = threading.Lock()
_executor = ThreadPoolExecutor(max_workers=10, thread_name_prefix="EventWorker")
def __new__(cls):
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.__initialized = False
return cls._instance
def __init__(self):
"""初始化事件中心(单例模式)"""
if getattr(self, '__initialized', False):
return
self.__initialized = True
self._subscribers: DefaultDict[str, List[Tuple[Callable[[Event], None], Optional[str]]]] = defaultdict(list)
self._event_history: Dict[str, Event] = {}
self._pending_acks: Set[str] = set()
self._ack_timeout = 5.0
self._stats = {
'total_events': 0,
'failed_events': 0,
'delivered_events': 0
}
def subscribe(self,
event_type: Union[str, EventType],
handler: Callable[[Event], None],
token: Optional[str] = None) -> None:
"""订阅事件(支持token过滤)"""
event_type_str = event_type.name if isinstance(event_type, EventType) else str(event_type)
with self._lock:
self._subscribers[event_type_str].append((handler, token))
logger.debug(f"已订阅事件: {event_type_str}, token: {token}")
def unsubscribe(self,
event_type: Union[str, EventType],
handler: Callable[[Event], None]) -> bool:
"""取消订阅事件"""
event_type_str = event_type.name if isinstance(event_type, EventType) else str(event_type)
with self._lock:
if event_type_str not in self._subscribers:
return False
before = len(self._subscribers[event_type_str])
self._subscribers[event_type_str] = [
(h, t) for h, t in self._subscribers[event_type_str] if h != handler
]
removed = before != len(self._subscribers[event_type_str])
if removed:
logger.debug(f"已取消订阅: {event_type_str}")
return removed
def publish(self, event, require_ack=False, async_handle=True) -> bool:
"""发布事件(支持同步/异步处理)"""
# 验证事件数据 - 使用更精确的异常处理
try:
# 确保事件对象有 __post_init__ 方法
if hasattr(event, '__post_init__'):
event.__post_init__()
else:
logger.warning("事件对象缺少验证方法 __post_init__")
except (ValueError, TypeError) as e:
# 只捕获预期的验证错误
logger.error(f"事件验证失败: {e}")
with self._lock:
self._stats['failed_events'] += 1
return False
except Exception as e:
# 记录但继续处理其他意外错误
logger.exception(f"事件验证过程中发生意外错误: {e}")
# 主事件处理逻辑
try:
with self._lock:
if event.event_id in self._event_history:
logger.warning(f"重复事件ID: {event.event_id}")
return False
self._event_history[event.event_id] = event
self._stats['total_events'] += 1
if require_ack:
self._pending_acks.add(event.event_id)
handlers = self._get_matching_handlers(event)
if not handlers:
logger.debug(f"没有处理器订阅: {event.type}")
return True
if async_handle:
self._executor.submit(self._dispatch_event, event, handlers, require_ack)
else:
self._dispatch_event(event, handlers, require_ack)
return True
except (KeyError, AttributeError) as e:
# 处理内部数据结构错误
logger.error(f"内部数据结构错误: {e}")
return False
except Exception as e:
# 捕获所有其他异常但继续运行
logger.exception(f"发布事件时发生未预期错误: {e}")
return False
def _get_matching_handlers(self, event: Event) -> List[Callable[[Event], None]]:
"""获取匹配的事件处理器"""
with self._lock:
return [
h for h, t in self._subscribers.get(event.type, [])
if t is None or t == event.token
]
def _dispatch_event(self,
event: Event,
handlers: List[Callable[[Event], None]],
require_ack: bool) -> None:
"""分发事件到处理器"""
for handler in handlers:
try:
handler(event)
self._stats['delivered_events'] += 1
logger.debug(f"事件处理成功: {event.event_id[:8]}")
except Exception as e:
logger.exception(f"处理器异常: {e}")
if require_ack and event.target:
self._wait_for_ack(event)
def _wait_for_ack(self, event: Event) -> None:
"""等待事件确认"""
start = time.time()
while time.time() - start < self._ack_timeout:
with self._lock:
if event.event_id not in self._pending_acks:
return
time.sleep(0.05)
logger.warning(f"事件确认超时: {event.event_id[:8]}")
def get_stats(self) -> Dict[str, int]:
"""获取事件中心统计信息"""
with self._lock:
return self._stats.copy()
# 全局单例实例
event_center = EventCenter()
# 测试代码
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
def test_handler(event: Event):
print(f"处理测试事件: {event.event_id[:8]}, 数据: {event.data}")
time.sleep(0.1)
# 测试订阅和发布
event_center.subscribe(EventType.TEST_EVENT, test_handler)
test_event = Event(
type=EventType.TEST_EVENT,
source="test_script",
data={"message": "Hello EventCenter"},
token="test_token"
)
event_center.publish(test_event)
time.sleep(0.5)
# 测试统计功能
print("事件中心统计:", event_center.get_stats())