From c407800b3021403da36646beba04b05bed5129e0 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Fri, 10 May 2024 19:27:44 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E7=83=AD=E5=8A=A0=E8=BD=BD=E4=B8=8D?= =?UTF-8?q?=E5=90=8C=E5=B9=B3=E5=8F=B0=E8=B7=AF=E5=BE=84=E5=8F=8A=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=AE=9E=E4=BE=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/core/plugin.py | 22 +++++++++++++++------- app/helper/module.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/app/core/plugin.py b/app/core/plugin.py index c2681c1e..5f1af8ef 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -1,7 +1,9 @@ import concurrent import concurrent.futures +import os import time import traceback +from pathlib import Path from typing import List, Any, Dict, Tuple, Optional from watchdog.events import FileSystemEventHandler @@ -39,8 +41,9 @@ class PluginMonitorHandler(FileSystemEventHandler): self.__last_modified = current_time # 读取插件根目录下的__init__.py文件,读取class XXXX(_PluginBase)的类名 try: - plugin_dir = event.src_path.split("plugins/")[1].split("/")[0] - init_file = settings.ROOT_PATH / "app" / "plugins" / plugin_dir / "__init__.py" + # 使用os.path和pathlib处理跨平台的路径问题 + plugin_dir = event.src_path.split("plugins" + os.sep)[1].split(os.sep)[0] + init_file = Path(settings.ROOT_PATH) / "app" / "plugins" / plugin_dir / "__init__.py" with open(init_file, "r", encoding="utf-8") as f: lines = f.readlines() pid = None @@ -91,11 +94,16 @@ class PluginManager(metaclass=Singleton): # 扫描插件目录 plugin_package = "app.plugins" if pid: - plugin_package = f"{plugin_package}.{pid.lower()}" - plugins = ModuleHelper.load( - plugin_package, - filter_func=lambda _, obj: hasattr(obj, 'init_plugin') and hasattr(obj, "plugin_name") - ) + plugins = ModuleHelper.load_with_pre_filter( + "app.plugins", + filter_func=lambda name, obj: + hasattr(obj, 'init_plugin') and hasattr(obj, "plugin_name") and name == pid + ) + else: + plugins = ModuleHelper.load( + "app.plugins", + filter_func=lambda _, obj: hasattr(obj, 'init_plugin') and hasattr(obj, "plugin_name") + ) # 已安装插件 installed_plugins = self.systemconfig.get(SystemConfigKey.UserInstalledPlugins) or [] # 排序 diff --git a/app/helper/module.py b/app/helper/module.py index 40f616c7..b7b328fe 100644 --- a/app/helper/module.py +++ b/app/helper/module.py @@ -40,6 +40,38 @@ class ModuleHelper: return submodules + @classmethod + def load_with_pre_filter(cls, package_path, filter_func=lambda name, obj: True): + """ + 导入子模块 + :param package_path: 父包名 + :param filter_func: 子模块过滤函数,入参为模块名和模块对象,返回True则导入,否则不导入 + :return: + """ + + submodules: list = [] + packages = importlib.import_module(package_path) + for importer, package_name, _ in pkgutil.iter_modules(packages.__path__): + try: + if package_name.startswith('_'): + continue + full_package_name = f'{package_path}.{package_name}' + module = importlib.import_module(full_package_name) + # 预检查模块中的对象 + candidates = [(name, obj) for name, obj in module.__dict__.items() if + not name.startswith('_') and isinstance(obj, type)] + # 确定是否需要重新加载 + if any(filter_func(name, obj) for name, obj in candidates): + importlib.reload(module) + # reload后,对象已经发生变更,重新过滤已经重新加载后的模块中的对象 + for name, obj in module.__dict__.items(): + if not name.startswith('_') and isinstance(obj, type) and filter_func(name, obj): + submodules.append(obj) + except Exception as err: + logger.debug(f'加载模块 {package_name} 失败:{str(err)} - {traceback.format_exc()}') + + return submodules + @staticmethod def dynamic_import_all_modules(base_path: Path, package_name: str): """