From 11c10ea783bdd692b58d99c86a13572a89f4aac7 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:44:13 +0800 Subject: [PATCH 1/2] fix(event): improve handler enablement check mechanism --- app/core/event.py | 57 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/app/core/event.py b/app/core/event.py index 7b87b569..90dd5237 100644 --- a/app/core/event.py +++ b/app/core/event.py @@ -6,13 +6,14 @@ import threading import time import traceback import uuid -from queue import PriorityQueue, Empty -from typing import Callable, Dict, List, Union, Optional +from functools import lru_cache +from queue import Empty, PriorityQueue +from typing import Callable, Dict, List, Optional, Union from app.helper.message import MessageHelper from app.helper.thread import ThreadHelper from app.log import logger -from app.schemas.types import EventType, ChainEventType +from app.schemas.types import ChainEventType, EventType from app.utils.limit import ExponentialBackoffRateLimiter from app.utils.singleton import Singleton @@ -254,24 +255,46 @@ class EventManager(metaclass=Singleton): handler_info.append(handler_dict) return handler_info - @staticmethod - def __get_handler_identifier(target: Union[Callable, type]) -> str: + @classmethod + @lru_cache(maxsize=1000) + def __get_handler_identifier(cls, target: Union[Callable, type]) -> Optional[str]: """ - 获取处理器或处理器类的唯一标识符,包括模块名和类名 + 获取处理器或处理器类的唯一标识符,包括模块名和类名/方法名 :param target: 处理器函数或类 :return: 唯一标识符 """ - if isinstance(target, type): - # 如果是类,使用模块名和类名 - module_name = target.__module__ - class_name = target.__qualname__ - return f"{module_name}.{class_name}" - else: - # 如果是函数或方法,使用 inspect.getmodule 来获取模块名 - module = inspect.getmodule(target) + # 统一使用 inspect.getmodule 来获取模块名 + module = inspect.getmodule(target) + module_name = module.__name__ if module else "unknown_module" + + # 使用 __qualname__ 获取目标的限定名 + qualname = target.__qualname__ + return f"{module_name}.{qualname}" + + @classmethod + @lru_cache(maxsize=1000) + def __get_class_from_callable(cls, handler: Callable) -> Optional[str]: + """ + 获取可调用对象所属类的唯一标识符 + :param handler: 可调用对象(函数、方法等) + :return: 类的唯一标识符 + """ + # 对于绑定方法,通过 __self__.__class__ 获取类 + if inspect.ismethod(handler) and hasattr(handler, "__self__"): + return cls.__get_handler_identifier(handler.__self__.__class__) + + # 对于类实例(实现了 __call__ 方法) + if not inspect.isfunction(handler) and hasattr(handler, "__call__"): + handler_cls = handler.__class__ + return cls.__get_handler_identifier(handler_cls) + + # 对于未绑定方法、静态方法、类方法,使用 __qualname__ 提取类信息 + qualname_parts = handler.__qualname__.split(".") + if len(qualname_parts) > 1: + class_name = ".".join(qualname_parts[:-1]) + module = inspect.getmodule(handler) module_name = module.__name__ if module else "unknown_module" - qualname = target.__qualname__ - return f"{module_name}.{qualname}" + return f"{module_name}.{class_name}" def __is_handler_enabled(self, handler: Callable) -> bool: """ @@ -283,7 +306,7 @@ class EventManager(metaclass=Singleton): handler_id = self.__get_handler_identifier(handler) # 获取处理器所属类的唯一标识符 - class_id = self.__get_handler_identifier(handler.__self__.__class__) if hasattr(handler, '__self__') else None + class_id = self.__get_class_from_callable(handler) # 检查处理器或类是否被禁用,只要其中之一被禁用则返回 False if handler_id in self.__disabled_handlers or (class_id is not None and class_id in self.__disabled_classes): From 9548409bd596ca3b59b30585c82fc24fa9068cf4 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:09:32 +0800 Subject: [PATCH 2/2] fix(event): refine handler invocation and improve class loading checks --- app/core/event.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/app/core/event.py b/app/core/event.py index 90dd5237..43ea8f7c 100644 --- a/app/core/event.py +++ b/app/core/event.py @@ -409,18 +409,29 @@ class EventManager(metaclass=Singleton): """ # 检查类是否在全局变量中 if class_name in globals(): - class_obj = globals()[class_name]() - else: - # 如果类不在全局变量中,尝试动态导入模块并创建实例 - # 导入模块,除了插件和Command,只有chain能响应事件 try: - module = importlib.import_module(f"app.chain.{class_name[:-5].lower()}") - class_obj = getattr(module, class_name)() + class_obj = globals()[class_name]() + return class_obj except Exception as e: - logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}") + logger.error(f"事件处理出错:创建全局类实例出错:{str(e)} - {traceback.format_exc()}") return None - return class_obj + # 如果类不在全局变量中,尝试动态导入模块并创建实例 + try: + # 导入模块,除了插件和Command,只有chain能响应事件 + if not class_name.endswith("Chain"): + logger.debug(f"事件处理出错:无效的 Chain 类名: {class_name},类名必须以 'Chain' 结尾") + return None + module_name = f"app.chain.{class_name[:-5].lower()}" + module = importlib.import_module(module_name) + if hasattr(module, class_name): + class_obj = getattr(module, class_name)() + return class_obj + else: + logger.debug(f"事件处理出错:模块 {module_name} 中没有找到类 {class_name}") + except Exception as e: + logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}") + return None def __broadcast_consumer_loop(self): """