暴露全部api,解决循环import问题
This commit is contained in:
@@ -4,6 +4,9 @@ import os
|
||||
import inspect
|
||||
import toml
|
||||
import json
|
||||
import shutil
|
||||
import datetime
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.plugin_system.base.component_types import (
|
||||
PluginInfo,
|
||||
@@ -11,13 +14,10 @@ from src.plugin_system.base.component_types import (
|
||||
PythonDependency,
|
||||
)
|
||||
from src.plugin_system.base.config_types import ConfigField
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
from src.plugin_system.utils.manifest_utils import ManifestValidator
|
||||
|
||||
logger = get_logger("base_plugin")
|
||||
|
||||
# 全局插件类注册表
|
||||
_plugin_classes: Dict[str, Type["BasePlugin"]] = {}
|
||||
|
||||
|
||||
class BasePlugin(ABC):
|
||||
"""插件基类
|
||||
@@ -29,7 +29,7 @@ class BasePlugin(ABC):
|
||||
"""
|
||||
|
||||
# 插件基本信息(子类必须定义)
|
||||
plugin_name: str = "" # 插件内部标识符(如 "doubao_pic_plugin")
|
||||
plugin_name: str = "" # 插件内部标识符(如 "hello_world_plugin")
|
||||
enable_plugin: bool = False # 是否启用插件
|
||||
dependencies: List[str] = [] # 依赖的其他插件
|
||||
python_dependencies: List[PythonDependency] = [] # Python包依赖
|
||||
@@ -103,7 +103,7 @@ class BasePlugin(ABC):
|
||||
if not self.get_manifest_info("description"):
|
||||
raise ValueError(f"插件 {self.plugin_name} 的manifest中缺少description字段")
|
||||
|
||||
def _load_manifest(self):
|
||||
def _load_manifest(self): # sourcery skip: raise-from-previous-error
|
||||
"""加载manifest文件(强制要求)"""
|
||||
if not self.plugin_dir:
|
||||
raise ValueError(f"{self.log_prefix} 没有插件目录路径,无法加载manifest")
|
||||
@@ -124,9 +124,6 @@ class BasePlugin(ABC):
|
||||
# 验证manifest格式
|
||||
self._validate_manifest()
|
||||
|
||||
# 从manifest覆盖插件基本信息(如果插件类中未定义)
|
||||
self._apply_manifest_overrides()
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
error_msg = f"{self.log_prefix} manifest文件格式错误: {e}"
|
||||
logger.error(error_msg)
|
||||
@@ -136,15 +133,6 @@ class BasePlugin(ABC):
|
||||
logger.error(error_msg)
|
||||
raise IOError(error_msg) # noqa
|
||||
|
||||
def _apply_manifest_overrides(self):
|
||||
"""从manifest文件覆盖插件信息(现在只处理内部标识符的fallback)"""
|
||||
if not self.manifest_data:
|
||||
return
|
||||
|
||||
# 只有当插件类中没有定义plugin_name时,才从manifest中获取作为fallback
|
||||
if not self.plugin_name:
|
||||
self.plugin_name = self.manifest_data.get("name", "").replace(" ", "_").lower()
|
||||
|
||||
def _get_author_name(self) -> str:
|
||||
"""从manifest获取作者名称"""
|
||||
author_info = self.get_manifest_info("author", {})
|
||||
@@ -156,10 +144,7 @@ class BasePlugin(ABC):
|
||||
def _validate_manifest(self):
|
||||
"""验证manifest文件格式(使用强化的验证器)"""
|
||||
if not self.manifest_data:
|
||||
return
|
||||
|
||||
# 导入验证器
|
||||
from src.plugin_system.utils.manifest_utils import ManifestValidator
|
||||
raise ValueError(f"{self.log_prefix} manifest数据为空,验证失败")
|
||||
|
||||
validator = ManifestValidator()
|
||||
is_valid = validator.validate_manifest(self.manifest_data)
|
||||
@@ -176,36 +161,6 @@ class BasePlugin(ABC):
|
||||
error_msg += f": {'; '.join(validator.validation_errors)}"
|
||||
raise ValueError(error_msg)
|
||||
|
||||
def _generate_default_manifest(self, manifest_path: str):
|
||||
"""生成默认的manifest文件"""
|
||||
if not self.plugin_name:
|
||||
logger.debug(f"{self.log_prefix} 插件名称未定义,无法生成默认manifest")
|
||||
return
|
||||
|
||||
# 从plugin_name生成友好的显示名称
|
||||
display_name = self.plugin_name.replace("_", " ").title()
|
||||
|
||||
default_manifest = {
|
||||
"manifest_version": 1,
|
||||
"name": display_name,
|
||||
"version": "1.0.0",
|
||||
"description": "插件描述",
|
||||
"author": {"name": "Unknown", "url": ""},
|
||||
"license": "MIT",
|
||||
"host_application": {"min_version": "1.0.0", "max_version": "4.0.0"},
|
||||
"keywords": [],
|
||||
"categories": [],
|
||||
"default_locale": "zh-CN",
|
||||
"locales_path": "_locales",
|
||||
}
|
||||
|
||||
try:
|
||||
with open(manifest_path, "w", encoding="utf-8") as f:
|
||||
json.dump(default_manifest, f, ensure_ascii=False, indent=2)
|
||||
logger.info(f"{self.log_prefix} 已生成默认manifest文件: {manifest_path}")
|
||||
except IOError as e:
|
||||
logger.error(f"{self.log_prefix} 保存默认manifest文件失败: {e}")
|
||||
|
||||
def get_manifest_info(self, key: str, default: Any = None) -> Any:
|
||||
"""获取manifest信息
|
||||
|
||||
@@ -304,9 +259,6 @@ class BasePlugin(ABC):
|
||||
|
||||
def _backup_config_file(self, config_file_path: str) -> str:
|
||||
"""备份配置文件"""
|
||||
import shutil
|
||||
import datetime
|
||||
|
||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{config_file_path}.backup_{timestamp}"
|
||||
|
||||
@@ -377,13 +329,14 @@ class BasePlugin(ABC):
|
||||
logger.warning(f"{self.log_prefix} 配置节 {section_name} 结构已改变,使用默认值")
|
||||
|
||||
# 检查旧配置中是否有新配置没有的节
|
||||
for section_name in old_config.keys():
|
||||
for section_name in old_config:
|
||||
if section_name not in migrated_config:
|
||||
logger.warning(f"{self.log_prefix} 配置节 {section_name} 在新版本中已被移除")
|
||||
|
||||
return migrated_config
|
||||
|
||||
def _generate_config_from_schema(self) -> Dict[str, Any]:
|
||||
# sourcery skip: dict-comprehension
|
||||
"""根据schema生成配置数据结构(不写入文件)"""
|
||||
if not self.config_schema:
|
||||
return {}
|
||||
@@ -473,7 +426,7 @@ class BasePlugin(ABC):
|
||||
except IOError as e:
|
||||
logger.error(f"{self.log_prefix} 保存配置文件失败: {e}", exc_info=True)
|
||||
|
||||
def _load_plugin_config(self):
|
||||
def _load_plugin_config(self): # sourcery skip: extract-method
|
||||
"""加载插件配置文件,支持版本检查和自动迁移"""
|
||||
if not self.config_file_name:
|
||||
logger.debug(f"{self.log_prefix} 未指定配置文件,跳过加载")
|
||||
@@ -568,7 +521,7 @@ class BasePlugin(ABC):
|
||||
|
||||
def register_plugin(self) -> bool:
|
||||
"""注册插件及其所有组件"""
|
||||
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
components = self.get_plugin_components()
|
||||
|
||||
# 检查依赖
|
||||
@@ -598,6 +551,7 @@ class BasePlugin(ABC):
|
||||
|
||||
def _check_dependencies(self) -> bool:
|
||||
"""检查插件依赖"""
|
||||
from src.plugin_system.core.component_registry import component_registry
|
||||
if not self.dependencies:
|
||||
return True
|
||||
|
||||
@@ -629,52 +583,3 @@ class BasePlugin(ABC):
|
||||
return default
|
||||
|
||||
return current
|
||||
|
||||
|
||||
def register_plugin(cls):
|
||||
"""插件注册装饰器
|
||||
|
||||
用法:
|
||||
@register_plugin
|
||||
class MyPlugin(BasePlugin):
|
||||
plugin_name = "my_plugin"
|
||||
plugin_description = "我的插件"
|
||||
...
|
||||
"""
|
||||
if not issubclass(cls, BasePlugin):
|
||||
logger.error(f"类 {cls.__name__} 不是 BasePlugin 的子类")
|
||||
return cls
|
||||
|
||||
# 只是注册插件类,不立即实例化
|
||||
# 插件管理器会负责实例化和注册
|
||||
plugin_name = cls.plugin_name or cls.__name__
|
||||
_plugin_classes[plugin_name] = cls
|
||||
logger.debug(f"插件类已注册: {plugin_name}")
|
||||
|
||||
return cls
|
||||
|
||||
|
||||
def get_registered_plugin_classes() -> Dict[str, Type["BasePlugin"]]:
|
||||
"""获取所有已注册的插件类"""
|
||||
return _plugin_classes.copy()
|
||||
|
||||
|
||||
def instantiate_and_register_plugin(plugin_class: Type["BasePlugin"], plugin_dir: str = None) -> bool:
|
||||
"""实例化并注册插件
|
||||
|
||||
Args:
|
||||
plugin_class: 插件类
|
||||
plugin_dir: 插件目录路径
|
||||
|
||||
Returns:
|
||||
bool: 是否成功
|
||||
"""
|
||||
try:
|
||||
plugin_instance = plugin_class(plugin_dir=plugin_dir)
|
||||
return plugin_instance.register_plugin()
|
||||
except Exception as e:
|
||||
logger.error(f"注册插件 {plugin_class.__name__} 时出错: {e}")
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user