调整插件加载逻辑

This commit is contained in:
liuwei
2025-03-19 10:20:22 +08:00
parent feb14b16f6
commit 9e42a4690d
5 changed files with 125 additions and 86 deletions

View File

@@ -1,5 +1,6 @@
import importlib import importlib
import inspect import inspect
import logging
import os import os
import sys import sys
from typing import Dict, List, Any, Optional, Type from typing import Dict, List, Any, Optional, Type
@@ -13,7 +14,7 @@ from plugin_common.event_system import EventSystem, EventType
class PluginManager: class PluginManager:
"""插件管理器,负责插件的加载、初始化、启动、停止和卸载""" """插件管理器,负责插件的加载、初始化、启动、停止和卸载"""
def __init__(self, plugin_dir: str = "plugins"): def __init__(self, plugin_dir: str = "plugins"):
""" """
初始化插件管理器 初始化插件管理器
@@ -25,15 +26,16 @@ class PluginManager:
self.plugins: Dict[str, PluginInterface] = {} # 插件实例字典 self.plugins: Dict[str, PluginInterface] = {} # 插件实例字典
self.plugin_modules = {} # 插件模块字典 self.plugin_modules = {} # 插件模块字典
self.system_context = {} # 系统上下文 self.system_context = {} # 系统上下文
self.LOG = logging.getLogger(__name__)
# 确保插件目录存在 # 确保插件目录存在
if not os.path.exists(self.plugin_dir): if not os.path.exists(self.plugin_dir):
os.makedirs(self.plugin_dir) os.makedirs(self.plugin_dir)
# 将插件目录添加到Python路径 # 将插件目录添加到Python路径
if self.plugin_dir not in sys.path: if self.plugin_dir not in sys.path:
sys.path.insert(0, self.plugin_dir) sys.path.insert(0, self.plugin_dir)
def set_system_context(self, context: Dict[str, Any]): def set_system_context(self, context: Dict[str, Any]):
""" """
设置系统上下文 设置系统上下文
@@ -42,7 +44,7 @@ class PluginManager:
context: 系统上下文 context: 系统上下文
""" """
self.system_context = context self.system_context = context
def discover_plugins(self) -> List[str]: def discover_plugins(self) -> List[str]:
""" """
发现可用插件 发现可用插件
@@ -51,7 +53,7 @@ class PluginManager:
插件模块名称列表 插件模块名称列表
""" """
plugin_modules = [] plugin_modules = []
# 遍历插件目录 # 遍历插件目录
for item in os.listdir(self.plugin_dir): for item in os.listdir(self.plugin_dir):
if os.path.isdir(os.path.join(self.plugin_dir, item)) and not item.startswith("__"): if os.path.isdir(os.path.join(self.plugin_dir, item)) and not item.startswith("__"):
@@ -61,9 +63,9 @@ class PluginManager:
elif item.endswith(".py") and not item.startswith("__"): elif item.endswith(".py") and not item.startswith("__"):
# 单文件插件 # 单文件插件
plugin_modules.append(item[:-3]) plugin_modules.append(item[:-3])
self.LOG.info(f"plugin_modules:{plugin_modules}")
return plugin_modules return plugin_modules
def load_plugin(self, plugin_name: str) -> Optional[PluginInterface]: def load_plugin(self, plugin_name: str) -> Optional[PluginInterface]:
""" """
加载插件 加载插件
@@ -78,80 +80,107 @@ class PluginManager:
# 如果插件已加载,直接返回 # 如果插件已加载,直接返回
if plugin_name in self.plugins: if plugin_name in self.plugins:
return self.plugins[plugin_name] return self.plugins[plugin_name]
# 确定插件路径 # 确定插件路径
if os.path.isdir(os.path.join(self.plugin_dir, plugin_name)): if os.path.isdir(os.path.join(self.plugin_dir, plugin_name)):
plugin_path = os.path.join(self.plugin_dir, plugin_name) plugin_path = os.path.join(self.plugin_dir, plugin_name)
# 优先从main.py加载插件 # 直接从main.py加载插件不再尝试从__init__.py加载
main_module_path = f"{plugin_name}.main" main_module_path = f"{plugin_name}.main"
if os.path.exists(os.path.join(plugin_path, "main.py")): if os.path.exists(os.path.join(plugin_path, "main.py")):
try: try:
module = importlib.import_module(main_module_path) module = importlib.import_module(main_module_path)
self.plugin_modules[plugin_name] = module self.plugin_modules[plugin_name] = module
except ImportError: except ImportError as e:
# 如果main.py导入失败尝试从__init__.py加载 self.LOG.error(f"导入插件模块 {main_module_path} 失败: {e}")
module = importlib.import_module(plugin_name) return None
self.plugin_modules[plugin_name] = module
else: else:
# 如果没有main.py从__init__.py加载 self.LOG.error(f"插件 {plugin_name} 缺少 main.py 文件")
module = importlib.import_module(plugin_name) return None
self.plugin_modules[plugin_name] = module
else: else:
# 单文件插件 # 单文件插件
plugin_path = self.plugin_dir plugin_path = self.plugin_dir
module = importlib.import_module(plugin_name) module = importlib.import_module(plugin_name)
self.plugin_modules[plugin_name] = module self.plugin_modules[plugin_name] = module
# 查找插件类 # 查找插件类
plugin_class = None plugin_class = None
for name, obj in inspect.getmembers(module): for name, obj in inspect.getmembers(module):
if (inspect.isclass(obj) and if (inspect.isclass(obj) and
issubclass(obj, PluginInterface) and issubclass(obj, PluginInterface) and
obj != PluginInterface obj != PluginInterface and
# 修改这里:不排除继承自 MessagePluginInterface 和 ScheduledPluginInterface 的类 obj != MessagePluginInterface and
# obj != MessagePluginInterface and obj != ScheduledPluginInterface):
# obj != ScheduledPluginInterface
):
plugin_class = obj plugin_class = obj
break break
# 如果插件类为空尝试查找get_plugin函数
if plugin_class is None: if plugin_class is None:
print(f"插件 {plugin_name} 中未找到有效的插件类") get_plugin_func = getattr(module, "get_plugin", None)
if callable(get_plugin_func):
plugin = get_plugin_func()
if isinstance(plugin, PluginInterface):
# 设置插件路径
plugin.set_plugin_path(plugin_path)
# 加载插件配置
if not plugin.load_config():
self.LOG.error(f"插件 {plugin_name} 加载配置失败")
return None
# 初始化插件
if not plugin.initialize(self.system_context):
self.LOG.error(f"插件 {plugin_name} 初始化失败")
return None
# 注册插件
PluginRegistry().register(plugin)
# 存储插件实例
self.plugins[plugin.name] = plugin
# 发布插件加载事件
EventSystem().publish(EventType.PLUGIN_LOADED, {"plugin": plugin})
return plugin
else:
self.LOG.error(f"插件 {plugin_name} 的 get_plugin() 返回的不是有效的插件实例")
else:
self.LOG.error(f"插件 {plugin_name} 中未找到有效的插件类或 get_plugin 函数")
return None return None
# 实例化插件 # 实例化插件
plugin = plugin_class() plugin = plugin_class()
plugin.status = PluginStatus.LOADED plugin.status = PluginStatus.LOADED
# 设置插件路径 # 设置插件路径
plugin.set_plugin_path(plugin_path) plugin.set_plugin_path(plugin_path)
# 加载插件配置 # 加载插件配置
if not plugin.load_config(): if not plugin.load_config():
print(f"插件 {plugin_name} 加载配置失败") self.LOG.error(f"插件 {plugin_name} 加载配置失败")
return None return None
# 初始化插件 # 初始化插件
if not plugin.initialize(self.system_context): if not plugin.initialize(self.system_context):
print(f"插件 {plugin_name} 初始化失败") self.LOG.error(f"插件 {plugin_name} 初始化失败")
return None return None
# 注册插件 # 注册插件
PluginRegistry().register(plugin) PluginRegistry().register(plugin)
# 存储插件实例 # 存储插件实例
self.plugins[plugin.name] = plugin self.plugins[plugin.name] = plugin
# 发布插件加载事件 # 发布插件加载事件
EventSystem().publish(EventType.PLUGIN_LOADED, {"plugin": plugin}) EventSystem().publish(EventType.PLUGIN_LOADED, {"plugin": plugin})
return plugin return plugin
except Exception as e: except Exception as e:
print(f"加载插件 {plugin_name} 失败: {e}") self.LOG.error(f"加载插件 {plugin_name} 失败: {e}")
return None return None
def load_all_plugins(self) -> Dict[str, PluginInterface]: def load_all_plugins(self) -> Dict[str, PluginInterface]:
""" """
加载所有插件 加载所有插件
@@ -160,12 +189,12 @@ class PluginManager:
插件实例字典 插件实例字典
""" """
plugin_modules = self.discover_plugins() plugin_modules = self.discover_plugins()
for module_name in plugin_modules: for module_name in plugin_modules:
self.load_plugin(module_name) self.load_plugin(module_name)
return self.plugins return self.plugins
def unload_plugin(self, plugin_name: str) -> bool: def unload_plugin(self, plugin_name: str) -> bool:
""" """
卸载插件 卸载插件
@@ -177,33 +206,33 @@ class PluginManager:
卸载是否成功 卸载是否成功
""" """
if plugin_name not in self.plugins: if plugin_name not in self.plugins:
print(f"插件 {plugin_name} 未加载") self.LOG.info(f"插件 {plugin_name} 未加载")
return False return False
plugin = self.plugins[plugin_name] plugin = self.plugins[plugin_name]
# 停止插件 # 停止插件
if plugin.status == PluginStatus.RUNNING: if plugin.status == PluginStatus.RUNNING:
if not plugin.stop(): if not plugin.stop():
print(f"停止插件 {plugin_name} 失败") self.LOG.info(f"停止插件 {plugin_name} 失败")
return False return False
# 清理插件资源 # 清理插件资源
if not plugin.cleanup(): if not plugin.cleanup():
print(f"清理插件 {plugin_name} 资源失败") self.LOG.info(f"清理插件 {plugin_name} 资源失败")
return False return False
# 注销插件 # 注销插件
PluginRegistry().unregister(plugin_name) PluginRegistry().unregister(plugin_name)
# 移除插件实例 # 移除插件实例
del self.plugins[plugin_name] del self.plugins[plugin_name]
# 发布插件卸载事件 # 发布插件卸载事件
EventSystem().publish(EventType.PLUGIN_UNLOADED, {"plugin_name": plugin_name}) EventSystem().publish(EventType.PLUGIN_UNLOADED, {"plugin_name": plugin_name})
return True return True
def start_plugin(self, plugin_name: str) -> bool: def start_plugin(self, plugin_name: str) -> bool:
""" """
启动插件 启动插件
@@ -215,22 +244,22 @@ class PluginManager:
启动是否成功 启动是否成功
""" """
if plugin_name not in self.plugins: if plugin_name not in self.plugins:
print(f"插件 {plugin_name} 未加载") self.LOG.info(f"插件 {plugin_name} 未加载")
return False return False
plugin = self.plugins[plugin_name] plugin = self.plugins[plugin_name]
if plugin.status == PluginStatus.RUNNING: if plugin.status == PluginStatus.RUNNING:
print(f"插件 {plugin_name} 已经在运行") self.LOG.info(f"插件 {plugin_name} 已经在运行")
return True return True
if plugin.start(): if plugin.start():
plugin.status = PluginStatus.RUNNING plugin.status = PluginStatus.RUNNING
return True return True
else: else:
plugin.status = PluginStatus.ERROR plugin.status = PluginStatus.ERROR
return False return False
def stop_plugin(self, plugin_name: str) -> bool: def stop_plugin(self, plugin_name: str) -> bool:
""" """
停止插件 停止插件
@@ -242,22 +271,22 @@ class PluginManager:
停止是否成功 停止是否成功
""" """
if plugin_name not in self.plugins: if plugin_name not in self.plugins:
print(f"插件 {plugin_name} 未加载") self.LOG.info(f"插件 {plugin_name} 未加载")
return False return False
plugin = self.plugins[plugin_name] plugin = self.plugins[plugin_name]
if plugin.status != PluginStatus.RUNNING: if plugin.status != PluginStatus.RUNNING:
print(f"插件 {plugin_name} 未在运行") self.LOG.info(f"插件 {plugin_name} 未在运行")
return True return True
if plugin.stop(): if plugin.stop():
plugin.status = PluginStatus.STOPPED plugin.status = PluginStatus.STOPPED
return True return True
else: else:
plugin.status = PluginStatus.ERROR plugin.status = PluginStatus.ERROR
return False return False
def reload_plugin(self, plugin_name: str) -> Optional[PluginInterface]: def reload_plugin(self, plugin_name: str) -> Optional[PluginInterface]:
""" """
重新加载插件 重新加载插件
@@ -271,16 +300,16 @@ class PluginManager:
# 卸载插件 # 卸载插件
if plugin_name in self.plugins: if plugin_name in self.plugins:
if not self.unload_plugin(plugin_name): if not self.unload_plugin(plugin_name):
print(f"卸载插件 {plugin_name} 失败") self.LOG.info(f"卸载插件 {plugin_name} 失败")
return None return None
# 重新导入模块 # 重新导入模块
if plugin_name in self.plugin_modules: if plugin_name in self.plugin_modules:
try: try:
importlib.reload(self.plugin_modules[plugin_name]) importlib.reload(self.plugin_modules[plugin_name])
except Exception as e: except Exception as e:
print(f"重新导入插件模块 {plugin_name} 失败: {e}") self.LOG.info(f"重新导入插件模块 {plugin_name} 失败: {e}")
return None return None
# 加载插件 # 加载插件
return self.load_plugin(plugin_name) return self.load_plugin(plugin_name)

View File

@@ -1,7 +0,0 @@
from plugins.message_summary.main import MessageSummaryPlugin
def get_plugin():
"""获取插件实例"""
return MessageSummaryPlugin()
__all__ = ['MessageSummaryPlugin', 'get_plugin']

View File

@@ -1,7 +0,0 @@
from plugins.stats_collector.main import StatsCollectorPlugin
def get_plugin():
"""获取插件实例"""
return StatsCollectorPlugin()
__all__ = ['StatsCollectorPlugin', 'get_plugin']

View File

@@ -3,7 +3,7 @@ import time
from typing import Dict, Any, Tuple, Optional, List from typing import Dict, Any, Tuple, Optional, List
from datetime import datetime from datetime import datetime
from plugin_common.plugin_interface import PluginInterface from plugin_common.plugin_interface import PluginInterface, PluginStatus
from event_system.event_manager import EventManager from event_system.event_manager import EventManager
# 使用正确的事件类型导入 # 使用正确的事件类型导入
from event_system.events.plugin_events import PluginCallStartEvent, PluginCallEndEvent, PluginCallErrorEvent from event_system.events.plugin_events import PluginCallStartEvent, PluginCallEndEvent, PluginCallErrorEvent
@@ -154,3 +154,15 @@ class StatsCollectorPlugin(PluginInterface):
self.event_manager.unregister(PluginCallErrorEvent, self.handle_plugin_error) self.event_manager.unregister(PluginCallErrorEvent, self.handle_plugin_error)
self.logger.info("统计收集插件已关闭") self.logger.info("统计收集插件已关闭")
def start(self) -> bool:
"""启动插件"""
self.status = PluginStatus.RUNNING
self.LOG.info(f"{self.name} 插件已启动")
return True
def stop(self) -> bool:
"""停止插件"""
self.status = PluginStatus.STOPPED
self.LOG.info(f"{self.name} 插件已停止")
return True

View File

@@ -2,7 +2,7 @@ import logging
import threading import threading
from typing import Dict, Any, Tuple, Optional, List from typing import Dict, Any, Tuple, Optional, List
from plugin_common.plugin_interface import PluginInterface from plugin_common.plugin_interface import PluginInterface, PluginStatus
from .dashboard_server import DashboardServer from .dashboard_server import DashboardServer
@@ -144,3 +144,15 @@ class StatsDashboardPlugin(PluginInterface):
"""关闭插件""" """关闭插件"""
if self.server: if self.server:
self.stop_server() self.stop_server()
def start(self) -> bool:
"""启动插件"""
self.status = PluginStatus.RUNNING
self.LOG.info(f"{self.name} 插件已启动")
return True
def stop(self) -> bool:
"""停止插件"""
self.status = PluginStatus.STOPPED
self.LOG.info(f"{self.name} 插件已停止")
return True