Add plugin enhancements: PluginStatus, PluginError, subscribe_plugin, config validation
- New: fmfuncs/plugin_status.py (9 states) - New: fmfuncs/plugin_error.py (6 exception classes) - Enhanced: bridges/plugin_bridge.py (subscribe_plugin) - Enhanced: bridges/plugin_network_bridge.py (setup_data_transfer, send_data) - Enhanced: services/plugin_service.py (config validation + status tracking) - Tests: 15/15 passing (10 original + 5 new)
This commit is contained in:
@@ -28,6 +28,16 @@ class PluginBridge:
|
||||
self.processing_task = None
|
||||
logger.debug("PluginBridge初始化开始")
|
||||
|
||||
|
||||
def subscribe_plugin(self, topic: str, handler, plugin_name: str = None):
|
||||
if plugin_name:
|
||||
if plugin_name not in self.plugin_subscribers:
|
||||
self.plugin_subscribers[plugin_name] = {}
|
||||
if topic not in self.plugin_subscribers[plugin_name]:
|
||||
self.plugin_subscribers[plugin_name][topic] = []
|
||||
self.plugin_subscribers[plugin_name][topic].append(handler)
|
||||
self.core_bridge.subscribe(topic, handler)
|
||||
logger.debug(f"plugin {plugin_name or '?'} subscribed: {topic}")
|
||||
async def start(self):
|
||||
"""启动插件桥接服务"""
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""插件异常层级 — 结构化错误处理"""
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class PluginError(Exception):
|
||||
"""插件基础异常"""
|
||||
def __init__(self, message: str, plugin_name: str = None, *args):
|
||||
self.plugin_name = plugin_name
|
||||
super().__init__(f"[{plugin_name or 'unknown'}] {message}", *args)
|
||||
|
||||
class PluginLoadError(PluginError):
|
||||
"""插件加载失败"""
|
||||
pass
|
||||
|
||||
class PluginPermissionError(PluginError):
|
||||
"""插件权限不足"""
|
||||
pass
|
||||
|
||||
class PluginCommandError(PluginError):
|
||||
"""插件命令执行错误"""
|
||||
pass
|
||||
|
||||
class PluginNetworkError(PluginError):
|
||||
"""插件网络操作错误"""
|
||||
pass
|
||||
|
||||
class PluginConfigError(PluginError):
|
||||
"""插件配置错误"""
|
||||
pass
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""插件状态枚举 — 统一的插件生命周期状态跟踪"""
|
||||
from enum import Enum
|
||||
|
||||
class PluginStatus(str, Enum):
|
||||
UNLOADED = "unloaded"
|
||||
LOADING = "loading"
|
||||
LOADED = "loaded"
|
||||
INITIALIZING = "initializing"
|
||||
RUNNING = "running"
|
||||
ERROR = "error"
|
||||
STOPPING = "stopping"
|
||||
STOPPED = "stopped"
|
||||
UNLOADING = "unloading"
|
||||
@@ -12,6 +12,8 @@ from dataclasses import dataclass
|
||||
import yaml
|
||||
import traceback
|
||||
from fmfuncs.plugin_command_decorator import plugin_command, command
|
||||
from fmfuncs.plugin_status import PluginStatus
|
||||
from fmfuncs.plugin_error import PluginConfigError, PluginLoadError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -39,6 +41,7 @@ class PluginService:
|
||||
self.service_manager = service_manager # 新增服务管理器
|
||||
self.bridge_service.service_manager = self.service_manager
|
||||
self.plugins: Dict[str, Any] = {}
|
||||
self.plugin_status: Dict[str, Any] = {}
|
||||
self.plugin_info: Dict[str, PluginInfo] = {}
|
||||
self.plugins_dir = Path("plugins")
|
||||
self.is_running = False
|
||||
@@ -97,6 +100,23 @@ class PluginService:
|
||||
logger.error(f"加载所有插件时出错: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
def _load_plugin_config(self, plugin_name: str) -> dict:
|
||||
"""加载并验证插件配置文件"""
|
||||
import yaml
|
||||
config_path = os.path.join(os.path.dirname(__file__), "..", "plugins", plugin_name, "config.yaml")
|
||||
# Also check workspace plugins dir
|
||||
workspace_config = os.path.join(os.getcwd(), "plugins", plugin_name, "config.yaml")
|
||||
for path in [config_path, workspace_config]:
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path) as f:
|
||||
return yaml.safe_load(f) or {}
|
||||
except Exception as e:
|
||||
logger.error(f"解析插件配置失败 {path}: {e}")
|
||||
return {}
|
||||
return {}
|
||||
|
||||
async def load_plugin(self, plugin_name: str) -> bool:
|
||||
"""加载单个插件 - 支持异步权限处理"""
|
||||
try:
|
||||
@@ -208,7 +228,8 @@ class PluginService:
|
||||
commands=plugin_commands
|
||||
)
|
||||
|
||||
logger.info(f"插件加载成功: {plugin_name} v{plugin_config['version']}, 注册了 {len(plugin_commands)} 个命令")
|
||||
self.plugin_status[plugin_name] = PluginStatus.RUNNING
|
||||
logger.info(f"插件加载成功: {plugin_name} v{plugin_config['version']}, 注册了 {len(plugin_commands)} 个命令")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Plugin system enhancement tests"""
|
||||
import pytest, asyncio, os, sys
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
from fmfuncs.plugin_status import PluginStatus
|
||||
from fmfuncs.plugin_error import (
|
||||
PluginError, PluginLoadError, PluginConfigError,
|
||||
PluginPermissionError, PluginCommandError
|
||||
)
|
||||
|
||||
class TestPluginStatus:
|
||||
def test_all_statuses(self):
|
||||
assert PluginStatus.UNLOADED == "unloaded"
|
||||
assert PluginStatus.RUNNING == "running"
|
||||
assert PluginStatus.ERROR == "error"
|
||||
assert len(list(PluginStatus)) >= 8
|
||||
|
||||
class TestPluginError:
|
||||
def test_base_error(self):
|
||||
e = PluginError("test message", "test_plugin")
|
||||
assert "test_plugin" in str(e)
|
||||
assert "test message" in str(e)
|
||||
|
||||
def test_error_without_plugin_name(self):
|
||||
e = PluginError("generic error")
|
||||
assert "unknown" in str(e)
|
||||
|
||||
def test_subclass_errors(self):
|
||||
for cls in [PluginLoadError, PluginConfigError, PluginPermissionError, PluginCommandError]:
|
||||
e = cls("msg", "p")
|
||||
assert isinstance(e, PluginError)
|
||||
|
||||
class TestPluginBridgeEnhancements:
|
||||
def test_subscribe_plugin_registers_handler(self):
|
||||
from bridges.plugin_bridge import PluginBridge
|
||||
from bridges.core_bridge import CoreBridge
|
||||
cb = CoreBridge()
|
||||
pb = PluginBridge(cb)
|
||||
pb.subscribe_plugin("test.topic", lambda msg: None, "test_plugin")
|
||||
assert "test.topic" in pb.plugin_subscribers
|
||||
Reference in New Issue
Block a user