316 lines
9.1 KiB
Python
316 lines
9.1 KiB
Python
"""
|
||
事件管理器模块
|
||
|
||
提供事件的注册、分发和管理:
|
||
- 优先级事件处理
|
||
- 处理器缓存优化
|
||
- 事件统计
|
||
- 异常隔离
|
||
"""
|
||
|
||
import asyncio
|
||
import time
|
||
import traceback
|
||
from collections import defaultdict
|
||
from dataclasses import dataclass, field
|
||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
||
|
||
from loguru import logger
|
||
|
||
|
||
@dataclass
|
||
class HandlerInfo:
|
||
"""事件处理器信息"""
|
||
handler: Callable
|
||
instance: object
|
||
priority: int
|
||
handler_name: str = field(default="")
|
||
|
||
def __post_init__(self):
|
||
if not self.handler_name:
|
||
self.handler_name = f"{self.instance.__class__.__name__}.{self.handler.__name__}"
|
||
|
||
|
||
@dataclass
|
||
class EventStats:
|
||
"""事件统计信息"""
|
||
emit_count: int = 0
|
||
handler_calls: int = 0
|
||
total_time_ms: float = 0
|
||
error_count: int = 0
|
||
stopped_count: int = 0 # 被 return False 中断的次数
|
||
|
||
|
||
class EventManager:
|
||
"""
|
||
事件管理器
|
||
|
||
特性:
|
||
- 优先级排序(高优先级先执行)
|
||
- 处理器可返回 False 中断后续处理
|
||
- 异常隔离(单个处理器异常不影响其他)
|
||
- 性能统计
|
||
"""
|
||
|
||
# 类级别存储
|
||
_handlers: Dict[str, List[HandlerInfo]] = {}
|
||
_stats: Dict[str, EventStats] = defaultdict(EventStats)
|
||
_handler_cache: Dict[str, List[HandlerInfo]] = {} # 排序后的缓存
|
||
_cache_valid: Set[str] = set()
|
||
|
||
@classmethod
|
||
def bind_instance(cls, instance: object):
|
||
"""
|
||
绑定实例的事件处理方法
|
||
|
||
扫描实例的所有方法,将带有 _event_type 属性的方法注册为事件处理器。
|
||
|
||
Args:
|
||
instance: 插件实例
|
||
"""
|
||
registered_count = 0
|
||
|
||
for method_name in dir(instance):
|
||
if method_name.startswith('_'):
|
||
continue
|
||
|
||
try:
|
||
method = getattr(instance, method_name)
|
||
except Exception:
|
||
continue
|
||
|
||
if not callable(method) or not hasattr(method, '_event_type'):
|
||
continue
|
||
|
||
event_type = getattr(method, '_event_type')
|
||
priority = getattr(method, '_priority', 50)
|
||
|
||
handler_info = HandlerInfo(
|
||
handler=method,
|
||
instance=instance,
|
||
priority=priority,
|
||
)
|
||
|
||
if event_type not in cls._handlers:
|
||
cls._handlers[event_type] = []
|
||
|
||
cls._handlers[event_type].append(handler_info)
|
||
|
||
# 使缓存失效
|
||
cls._cache_valid.discard(event_type)
|
||
|
||
registered_count += 1
|
||
logger.debug(
|
||
f"[EventManager] 注册: {handler_info.handler_name} -> "
|
||
f"{event_type} (优先级={priority})"
|
||
)
|
||
|
||
if registered_count > 0:
|
||
logger.success(
|
||
f"[EventManager] {instance.__class__.__name__} "
|
||
f"注册了 {registered_count} 个事件处理器"
|
||
)
|
||
|
||
@classmethod
|
||
def unbind_instance(cls, instance: object):
|
||
"""
|
||
解绑实例的所有事件处理器
|
||
|
||
Args:
|
||
instance: 插件实例
|
||
"""
|
||
unbound_count = 0
|
||
|
||
for event_type in list(cls._handlers.keys()):
|
||
original_count = len(cls._handlers[event_type])
|
||
cls._handlers[event_type] = [
|
||
h for h in cls._handlers[event_type]
|
||
if h.instance is not instance
|
||
]
|
||
removed = original_count - len(cls._handlers[event_type])
|
||
if removed > 0:
|
||
unbound_count += removed
|
||
cls._cache_valid.discard(event_type)
|
||
|
||
# 清理空列表
|
||
if not cls._handlers[event_type]:
|
||
del cls._handlers[event_type]
|
||
|
||
if unbound_count > 0:
|
||
logger.debug(
|
||
f"[EventManager] {instance.__class__.__name__} "
|
||
f"解绑了 {unbound_count} 个事件处理器"
|
||
)
|
||
|
||
@classmethod
|
||
def _get_sorted_handlers(cls, event_type: str) -> List[HandlerInfo]:
|
||
"""获取排序后的处理器列表(带缓存)"""
|
||
if event_type not in cls._cache_valid:
|
||
handlers = cls._handlers.get(event_type, [])
|
||
# 按优先级降序排序
|
||
cls._handler_cache[event_type] = sorted(
|
||
handlers,
|
||
key=lambda h: h.priority,
|
||
reverse=True
|
||
)
|
||
cls._cache_valid.add(event_type)
|
||
|
||
return cls._handler_cache.get(event_type, [])
|
||
|
||
@classmethod
|
||
async def emit(cls, event_type: str, *args, **kwargs) -> bool:
|
||
"""
|
||
触发事件
|
||
|
||
Args:
|
||
event_type: 事件类型
|
||
*args: 传递给处理器的位置参数(通常是 api_client, message)
|
||
**kwargs: 传递给处理器的关键字参数
|
||
|
||
Returns:
|
||
True 表示所有处理器都执行了,False 表示被中断
|
||
"""
|
||
handlers = cls._get_sorted_handlers(event_type)
|
||
|
||
if not handlers:
|
||
logger.debug(f"[EventManager] 事件 {event_type} 没有处理器")
|
||
return True
|
||
|
||
# 更新统计
|
||
stats = cls._stats[event_type]
|
||
stats.emit_count += 1
|
||
|
||
start_time = time.time()
|
||
all_completed = True
|
||
|
||
logger.debug(
|
||
f"[EventManager] 触发: {event_type}, "
|
||
f"处理器数量: {len(handlers)}"
|
||
)
|
||
|
||
for handler_info in handlers:
|
||
stats.handler_calls += 1
|
||
|
||
try:
|
||
logger.debug(f"[EventManager] 调用: {handler_info.handler_name}")
|
||
|
||
result = await handler_info.handler(*args, **kwargs)
|
||
|
||
# 检查是否中断
|
||
if result is False:
|
||
stats.stopped_count += 1
|
||
all_completed = False
|
||
logger.debug(
|
||
f"[EventManager] {handler_info.handler_name} "
|
||
f"返回 False,中断事件处理"
|
||
)
|
||
break
|
||
|
||
except Exception as e:
|
||
stats.error_count += 1
|
||
logger.error(
|
||
f"[EventManager] {handler_info.handler_name} 执行失败: {e}"
|
||
)
|
||
logger.debug(f"详细错误:\n{traceback.format_exc()}")
|
||
# 继续执行其他处理器
|
||
|
||
elapsed_ms = (time.time() - start_time) * 1000
|
||
stats.total_time_ms += elapsed_ms
|
||
|
||
return all_completed
|
||
|
||
@classmethod
|
||
async def emit_parallel(
|
||
cls,
|
||
event_type: str,
|
||
*args,
|
||
max_concurrency: int = 5,
|
||
**kwargs
|
||
) -> List[Any]:
|
||
"""
|
||
并行触发事件(忽略优先级和中断)
|
||
|
||
适用于不需要顺序执行的场景。
|
||
|
||
Args:
|
||
event_type: 事件类型
|
||
max_concurrency: 最大并发数
|
||
*args, **kwargs: 传递给处理器的参数
|
||
|
||
Returns:
|
||
所有处理器的返回值列表
|
||
"""
|
||
handlers = cls._get_sorted_handlers(event_type)
|
||
|
||
if not handlers:
|
||
return []
|
||
|
||
semaphore = asyncio.Semaphore(max_concurrency)
|
||
|
||
async def run_handler(handler_info: HandlerInfo):
|
||
async with semaphore:
|
||
try:
|
||
return await handler_info.handler(*args, **kwargs)
|
||
except Exception as e:
|
||
logger.error(f"[EventManager] {handler_info.handler_name} 失败: {e}")
|
||
return None
|
||
|
||
tasks = [run_handler(h) for h in handlers]
|
||
return await asyncio.gather(*tasks, return_exceptions=True)
|
||
|
||
@classmethod
|
||
def get_handlers(cls, event_type: str) -> List[str]:
|
||
"""获取事件的所有处理器名称"""
|
||
handlers = cls._get_sorted_handlers(event_type)
|
||
return [h.handler_name for h in handlers]
|
||
|
||
@classmethod
|
||
def get_all_events(cls) -> List[str]:
|
||
"""获取所有已注册的事件类型"""
|
||
return list(cls._handlers.keys())
|
||
|
||
@classmethod
|
||
def get_stats(cls, event_type: str = None) -> Dict[str, Any]:
|
||
"""
|
||
获取事件统计信息
|
||
|
||
Args:
|
||
event_type: 指定事件类型,None 返回所有
|
||
|
||
Returns:
|
||
统计信息字典
|
||
"""
|
||
if event_type:
|
||
stats = cls._stats.get(event_type, EventStats())
|
||
return {
|
||
"emit_count": stats.emit_count,
|
||
"handler_calls": stats.handler_calls,
|
||
"total_time_ms": stats.total_time_ms,
|
||
"avg_time_ms": stats.total_time_ms / max(stats.emit_count, 1),
|
||
"error_count": stats.error_count,
|
||
"stopped_count": stats.stopped_count,
|
||
}
|
||
|
||
return {
|
||
event: cls.get_stats(event)
|
||
for event in cls._stats.keys()
|
||
}
|
||
|
||
@classmethod
|
||
def reset_stats(cls):
|
||
"""重置所有统计"""
|
||
cls._stats.clear()
|
||
|
||
@classmethod
|
||
def clear(cls):
|
||
"""清除所有处理器和统计(用于测试)"""
|
||
cls._handlers.clear()
|
||
cls._handler_cache.clear()
|
||
cls._cache_valid.clear()
|
||
cls._stats.clear()
|
||
|
||
|
||
# ==================== 导出 ====================
|
||
|
||
__all__ = ['EventManager', 'HandlerInfo', 'EventStats']
|