Merge pull request #2047 from InfinityPacer/main

fix 热加载不同平台路径及插件实例化
This commit is contained in:
jxxghp
2024-05-10 19:57:48 +08:00
committed by GitHub
2 changed files with 47 additions and 7 deletions
+15 -7
View File
@@ -1,7 +1,9 @@
import concurrent import concurrent
import concurrent.futures import concurrent.futures
import os
import time import time
import traceback import traceback
from pathlib import Path
from typing import List, Any, Dict, Tuple, Optional from typing import List, Any, Dict, Tuple, Optional
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
@@ -39,8 +41,9 @@ class PluginMonitorHandler(FileSystemEventHandler):
self.__last_modified = current_time self.__last_modified = current_time
# 读取插件根目录下的__init__.py文件,读取class XXXX(_PluginBase)的类名 # 读取插件根目录下的__init__.py文件,读取class XXXX(_PluginBase)的类名
try: try:
plugin_dir = event.src_path.split("plugins/")[1].split("/")[0] # 使用os.path和pathlib处理跨平台的路径问题
init_file = settings.ROOT_PATH / "app" / "plugins" / plugin_dir / "__init__.py" 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: with open(init_file, "r", encoding="utf-8") as f:
lines = f.readlines() lines = f.readlines()
pid = None pid = None
@@ -91,11 +94,16 @@ class PluginManager(metaclass=Singleton):
# 扫描插件目录 # 扫描插件目录
plugin_package = "app.plugins" plugin_package = "app.plugins"
if pid: if pid:
plugin_package = f"{plugin_package}.{pid.lower()}" plugins = ModuleHelper.load_with_pre_filter(
plugins = ModuleHelper.load( "app.plugins",
plugin_package, filter_func=lambda name, obj:
filter_func=lambda _, obj: hasattr(obj, 'init_plugin') and hasattr(obj, "plugin_name") 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 [] installed_plugins = self.systemconfig.get(SystemConfigKey.UserInstalledPlugins) or []
# 排序 # 排序
+32
View File
@@ -40,6 +40,38 @@ class ModuleHelper:
return submodules 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 @staticmethod
def dynamic_import_all_modules(base_path: Path, package_name: str): def dynamic_import_all_modules(base_path: Path, package_name: str):
""" """