移除了dependency_manager,dependency文档修改
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
6. 增加了插件和组件管理的API。
|
6. 增加了插件和组件管理的API。
|
||||||
7. `BaseCommand`的`execute`方法现在返回一个三元组,包含是否执行成功、可选的回复消息和是否拦截消息。
|
7. `BaseCommand`的`execute`方法现在返回一个三元组,包含是否执行成功、可选的回复消息和是否拦截消息。
|
||||||
- 这意味着你终于可以动态控制是否继续后续消息的处理了。
|
- 这意味着你终于可以动态控制是否继续后续消息的处理了。
|
||||||
|
8. 移除了dependency_manager,但是依然保留了`python_dependencies`属性,等待后续重构。
|
||||||
|
- 一并移除了文档有关manager的内容。
|
||||||
|
|
||||||
# 插件系统修改
|
# 插件系统修改
|
||||||
1. 现在所有的匹配模式不再是关键字了,而是枚举类。**(可能有遗漏)**
|
1. 现在所有的匹配模式不再是关键字了,而是枚举类。**(可能有遗漏)**
|
||||||
|
|||||||
@@ -1,93 +1,6 @@
|
|||||||
# 📦 插件依赖管理系统
|
# 📦 插件依赖管理系统
|
||||||
|
|
||||||
> 🎯 **简介**:MaiBot插件系统提供了强大的Python包依赖管理功能,让插件开发更加便捷和可靠。
|
现在的Python依赖包管理依然存在问题,请保留你的`python_dependencies`属性,等待后续重构。
|
||||||
|
|
||||||
## ✨ 功能概述
|
|
||||||
|
|
||||||
### 🎯 核心能力
|
|
||||||
- **声明式依赖**:插件可以明确声明需要的Python包
|
|
||||||
- **智能检查**:自动检查依赖包的安装状态
|
|
||||||
- **版本控制**:精确的版本要求管理
|
|
||||||
- **可选依赖**:区分必需依赖和可选依赖
|
|
||||||
- **自动安装**:可选的自动安装功能
|
|
||||||
- **批量管理**:生成统一的requirements文件
|
|
||||||
- **安全控制**:防止意外安装和版本冲突
|
|
||||||
|
|
||||||
### 🔄 工作流程
|
|
||||||
1. **声明依赖** → 在插件中声明所需的Python包
|
|
||||||
2. **加载检查** → 插件加载时自动检查依赖状态
|
|
||||||
3. **状态报告** → 详细报告缺失或版本不匹配的依赖
|
|
||||||
4. **智能安装** → 可选择自动安装或手动安装
|
|
||||||
5. **运行时处理** → 插件运行时优雅处理依赖缺失
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
### 步骤1:声明依赖
|
|
||||||
|
|
||||||
在你的插件类中添加`python_dependencies`字段:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system import BasePlugin, PythonDependency, register_plugin
|
|
||||||
|
|
||||||
@register_plugin
|
|
||||||
class MyPlugin(BasePlugin):
|
|
||||||
name = "my_plugin"
|
|
||||||
|
|
||||||
# 声明Python包依赖
|
|
||||||
python_dependencies = [
|
|
||||||
PythonDependency(
|
|
||||||
package_name="requests",
|
|
||||||
version=">=2.25.0",
|
|
||||||
description="HTTP请求库,用于网络通信"
|
|
||||||
),
|
|
||||||
PythonDependency(
|
|
||||||
package_name="numpy",
|
|
||||||
version=">=1.20.0",
|
|
||||||
optional=True,
|
|
||||||
description="数值计算库(可选功能)"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_plugin_components(self):
|
|
||||||
# 返回插件组件
|
|
||||||
return []
|
|
||||||
```
|
|
||||||
|
|
||||||
### 步骤2:处理依赖
|
|
||||||
|
|
||||||
在组件代码中优雅处理依赖缺失:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MyAction(BaseAction):
|
|
||||||
async def execute(self, action_input, context=None):
|
|
||||||
try:
|
|
||||||
import requests
|
|
||||||
# 使用requests进行网络请求
|
|
||||||
response = requests.get("https://api.example.com")
|
|
||||||
return {"status": "success", "data": response.json()}
|
|
||||||
except ImportError:
|
|
||||||
return {
|
|
||||||
"status": "error",
|
|
||||||
"message": "功能不可用:缺少requests库",
|
|
||||||
"hint": "请运行: pip install requests>=2.25.0"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 步骤3:检查和管理
|
|
||||||
|
|
||||||
使用依赖管理API:
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system import plugin_manager
|
|
||||||
|
|
||||||
# 检查所有插件的依赖状态
|
|
||||||
result = plugin_manager.check_all_dependencies()
|
|
||||||
print(f"检查了 {result['total_plugins_checked']} 个插件")
|
|
||||||
print(f"缺少必需依赖的插件: {result['plugins_with_missing_required']} 个")
|
|
||||||
|
|
||||||
# 生成requirements文件
|
|
||||||
plugin_manager.generate_plugin_requirements("plugin_requirements.txt")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 详细教程
|
## 📚 详细教程
|
||||||
|
|
||||||
@@ -97,11 +10,11 @@ plugin_manager.generate_plugin_requirements("plugin_requirements.txt")
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
PythonDependency(
|
PythonDependency(
|
||||||
package_name="requests", # 导入时的包名
|
package_name="PIL", # 导入时的包名
|
||||||
version=">=2.25.0", # 版本要求
|
version=">=11.2.0", # 版本要求
|
||||||
optional=False, # 是否为可选依赖
|
optional=False, # 是否为可选依赖
|
||||||
description="HTTP请求库", # 依赖描述
|
description="图像处理库", # 依赖描述
|
||||||
install_name="" # pip安装时的包名(可选)
|
install_name="pillow" # pip安装时的包名(可选)
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -110,10 +23,10 @@ PythonDependency(
|
|||||||
| 参数 | 类型 | 必需 | 说明 |
|
| 参数 | 类型 | 必需 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `package_name` | str | ✅ | Python导入时使用的包名(如`requests`) |
|
| `package_name` | str | ✅ | Python导入时使用的包名(如`requests`) |
|
||||||
| `version` | str | ❌ | 版本要求,支持pip格式(如`>=1.0.0`, `==2.1.3`) |
|
| `version` | str | ❌ | 版本要求,使用pip格式(如`>=1.0.0`, `==2.1.3`) |
|
||||||
| `optional` | bool | ❌ | 是否为可选依赖,默认`False` |
|
| `optional` | bool | ❌ | 是否为可选依赖,默认`False` |
|
||||||
| `description` | str | ❌ | 依赖的用途描述 |
|
| `description` | str | ❌ | 依赖的用途描述 |
|
||||||
| `install_name` | str | ❌ | pip安装时的包名,默认与`package_name`相同 |
|
| `install_name` | str | ❌ | pip安装时的包名,默认与`package_name`相同,用于处理安装名称和导入名称不一致的情况 |
|
||||||
|
|
||||||
#### 版本格式示例
|
#### 版本格式示例
|
||||||
|
|
||||||
@@ -125,201 +38,3 @@ PythonDependency("pillow", "==8.3.2") # 精确版本
|
|||||||
PythonDependency("scipy", ">=1.7.0,!=1.8.0") # 排除特定版本
|
PythonDependency("scipy", ">=1.7.0,!=1.8.0") # 排除特定版本
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 特殊情况处理
|
|
||||||
|
|
||||||
**导入名与安装名不同的包:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
PythonDependency(
|
|
||||||
package_name="PIL", # import PIL
|
|
||||||
install_name="Pillow", # pip install Pillow
|
|
||||||
version=">=8.0.0"
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
**可选依赖示例:**
|
|
||||||
|
|
||||||
```python
|
|
||||||
python_dependencies = [
|
|
||||||
# 必需依赖 - 核心功能
|
|
||||||
PythonDependency(
|
|
||||||
package_name="requests",
|
|
||||||
version=">=2.25.0",
|
|
||||||
description="HTTP库,插件核心功能必需"
|
|
||||||
),
|
|
||||||
|
|
||||||
# 可选依赖 - 增强功能
|
|
||||||
PythonDependency(
|
|
||||||
package_name="numpy",
|
|
||||||
version=">=1.20.0",
|
|
||||||
optional=True,
|
|
||||||
description="数值计算库,用于高级数学运算"
|
|
||||||
),
|
|
||||||
PythonDependency(
|
|
||||||
package_name="matplotlib",
|
|
||||||
version=">=3.0.0",
|
|
||||||
optional=True,
|
|
||||||
description="绘图库,用于数据可视化功能"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 依赖检查机制
|
|
||||||
|
|
||||||
系统在以下时机会自动检查依赖:
|
|
||||||
|
|
||||||
1. **插件加载时**:检查插件声明的所有依赖
|
|
||||||
2. **手动调用时**:通过API主动检查
|
|
||||||
3. **运行时检查**:在组件执行时动态检查
|
|
||||||
|
|
||||||
#### 检查结果状态
|
|
||||||
|
|
||||||
| 状态 | 描述 | 处理建议 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `no_dependencies` | 插件未声明任何依赖 | 无需处理 |
|
|
||||||
| `ok` | 所有依赖都已满足 | 正常使用 |
|
|
||||||
| `missing_optional` | 缺少可选依赖 | 部分功能不可用,考虑安装 |
|
|
||||||
| `missing_required` | 缺少必需依赖 | 插件功能受限,需要安装 |
|
|
||||||
|
|
||||||
## 🎯 最佳实践
|
|
||||||
|
|
||||||
### 1. 依赖声明原则
|
|
||||||
|
|
||||||
#### ✅ 推荐做法
|
|
||||||
|
|
||||||
```python
|
|
||||||
python_dependencies = [
|
|
||||||
# 明确的版本要求
|
|
||||||
PythonDependency(
|
|
||||||
package_name="requests",
|
|
||||||
version=">=2.25.0,<3.0.0", # 主版本兼容
|
|
||||||
description="HTTP请求库,用于API调用"
|
|
||||||
),
|
|
||||||
|
|
||||||
# 合理的可选依赖
|
|
||||||
PythonDependency(
|
|
||||||
package_name="numpy",
|
|
||||||
version=">=1.20.0",
|
|
||||||
optional=True,
|
|
||||||
description="数值计算库,用于数据处理功能"
|
|
||||||
),
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ❌ 避免的做法
|
|
||||||
|
|
||||||
```python
|
|
||||||
python_dependencies = [
|
|
||||||
# 过于宽泛的版本要求
|
|
||||||
PythonDependency("requests"), # 没有版本限制
|
|
||||||
|
|
||||||
# 过于严格的版本要求
|
|
||||||
PythonDependency("numpy", "==1.21.0"), # 精确版本过于严格
|
|
||||||
|
|
||||||
# 缺少描述
|
|
||||||
PythonDependency("matplotlib", ">=3.0.0"), # 没有说明用途
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 错误处理模式
|
|
||||||
|
|
||||||
#### 优雅降级模式
|
|
||||||
|
|
||||||
```python
|
|
||||||
class SmartAction(BaseAction):
|
|
||||||
async def execute(self, action_input, context=None):
|
|
||||||
# 检查可选依赖
|
|
||||||
try:
|
|
||||||
import numpy as np
|
|
||||||
# 使用numpy的高级功能
|
|
||||||
return await self._advanced_processing(action_input, np)
|
|
||||||
except ImportError:
|
|
||||||
# 降级到基础功能
|
|
||||||
return await self._basic_processing(action_input)
|
|
||||||
|
|
||||||
async def _advanced_processing(self, input_data, np):
|
|
||||||
"""使用numpy的高级处理"""
|
|
||||||
result = np.array(input_data).mean()
|
|
||||||
return {"result": result, "method": "advanced"}
|
|
||||||
|
|
||||||
async def _basic_processing(self, input_data):
|
|
||||||
"""基础处理(不依赖外部库)"""
|
|
||||||
result = sum(input_data) / len(input_data)
|
|
||||||
return {"result": result, "method": "basic"}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 使用API
|
|
||||||
|
|
||||||
### 检查依赖状态
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.plugin_system import plugin_manager
|
|
||||||
|
|
||||||
# 检查所有插件依赖(仅检查,不安装)
|
|
||||||
result = plugin_manager.check_all_dependencies(auto_install=False)
|
|
||||||
|
|
||||||
# 检查并自动安装缺失的必需依赖
|
|
||||||
result = plugin_manager.check_all_dependencies(auto_install=True)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 生成requirements文件
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 生成包含所有插件依赖的requirements文件
|
|
||||||
plugin_manager.generate_plugin_requirements("plugin_requirements.txt")
|
|
||||||
```
|
|
||||||
|
|
||||||
### 获取依赖状态报告
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 获取详细的依赖检查报告
|
|
||||||
result = plugin_manager.check_all_dependencies()
|
|
||||||
for plugin_name, status in result['plugin_status'].items():
|
|
||||||
print(f"插件 {plugin_name}: {status['status']}")
|
|
||||||
if status['missing']:
|
|
||||||
print(f" 缺失必需依赖: {status['missing']}")
|
|
||||||
if status['optional_missing']:
|
|
||||||
print(f" 缺失可选依赖: {status['optional_missing']}")
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛡️ 安全考虑
|
|
||||||
|
|
||||||
### 1. 自动安装控制
|
|
||||||
- 🛡️ **默认手动**: 自动安装默认关闭,需要明确启用
|
|
||||||
- 🔍 **依赖审查**: 安装前会显示将要安装的包列表
|
|
||||||
- ⏱️ **超时控制**: 安装操作有超时限制(5分钟)
|
|
||||||
|
|
||||||
### 2. 权限管理
|
|
||||||
- 📁 **环境隔离**: 推荐在虚拟环境中使用
|
|
||||||
- 🔒 **版本锁定**: 支持精确的版本控制
|
|
||||||
- 📝 **安装日志**: 记录所有安装操作
|
|
||||||
|
|
||||||
## 📊 故障排除
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
1. **依赖检查失败**
|
|
||||||
```python
|
|
||||||
# 手动检查包是否可导入
|
|
||||||
try:
|
|
||||||
import package_name
|
|
||||||
print("包可用")
|
|
||||||
except ImportError:
|
|
||||||
print("包不可用,需要安装")
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **版本冲突**
|
|
||||||
```python
|
|
||||||
# 检查已安装的包版本
|
|
||||||
import package_name
|
|
||||||
print(f"当前版本: {package_name.__version__}")
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **安装失败**
|
|
||||||
```python
|
|
||||||
# 查看安装日志
|
|
||||||
from src.plugin_system import dependency_manager
|
|
||||||
result = dependency_manager.get_install_summary()
|
|
||||||
print("安装日志:", result['install_log'])
|
|
||||||
print("失败详情:", result['failed_installs'])
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
|
|
||||||
from src.plugin_system.core.plugin_manager import plugin_manager
|
from src.plugin_system.core.plugin_manager import plugin_manager
|
||||||
from src.plugin_system.core.component_registry import component_registry
|
from src.plugin_system.core.component_registry import component_registry
|
||||||
from src.plugin_system.core.dependency_manager import dependency_manager
|
|
||||||
from src.plugin_system.core.events_manager import events_manager
|
from src.plugin_system.core.events_manager import events_manager
|
||||||
from src.plugin_system.core.global_announcement_manager import global_announcement_manager
|
from src.plugin_system.core.global_announcement_manager import global_announcement_manager
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"plugin_manager",
|
"plugin_manager",
|
||||||
"component_registry",
|
"component_registry",
|
||||||
"dependency_manager",
|
|
||||||
"events_manager",
|
"events_manager",
|
||||||
"global_announcement_manager",
|
"global_announcement_manager",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
"""
|
|
||||||
插件依赖管理器
|
|
||||||
|
|
||||||
负责检查和安装插件的Python包依赖
|
|
||||||
"""
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import importlib
|
|
||||||
from typing import List, Dict, Tuple, Any
|
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
|
||||||
from src.plugin_system.base.component_types import PythonDependency
|
|
||||||
|
|
||||||
logger = get_logger("dependency_manager")
|
|
||||||
|
|
||||||
|
|
||||||
class DependencyManager:
|
|
||||||
"""依赖管理器"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.install_log: List[str] = []
|
|
||||||
self.failed_installs: Dict[str, str] = {}
|
|
||||||
|
|
||||||
def check_dependencies(
|
|
||||||
self, dependencies: List[PythonDependency]
|
|
||||||
) -> Tuple[List[PythonDependency], List[PythonDependency]]:
|
|
||||||
"""检查依赖包状态
|
|
||||||
|
|
||||||
Args:
|
|
||||||
dependencies: 依赖包列表
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple[List[PythonDependency], List[PythonDependency]]: (缺失的依赖, 可选缺失的依赖)
|
|
||||||
"""
|
|
||||||
missing_required = []
|
|
||||||
missing_optional = []
|
|
||||||
|
|
||||||
for dep in dependencies:
|
|
||||||
if self._is_package_available(dep.package_name):
|
|
||||||
logger.debug(f"依赖包已存在: {dep.package_name}")
|
|
||||||
elif dep.optional:
|
|
||||||
missing_optional.append(dep)
|
|
||||||
logger.warning(f"可选依赖包缺失: {dep.package_name} - {dep.description}")
|
|
||||||
else:
|
|
||||||
missing_required.append(dep)
|
|
||||||
logger.error(f"必需依赖包缺失: {dep.package_name} - {dep.description}")
|
|
||||||
return missing_required, missing_optional
|
|
||||||
|
|
||||||
def _is_package_available(self, package_name: str) -> bool:
|
|
||||||
"""检查包是否可用"""
|
|
||||||
try:
|
|
||||||
importlib.import_module(package_name)
|
|
||||||
return True
|
|
||||||
except ImportError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def install_dependencies(self, dependencies: List[PythonDependency], auto_install: bool = False) -> bool:
|
|
||||||
"""安装依赖包
|
|
||||||
|
|
||||||
Args:
|
|
||||||
dependencies: 需要安装的依赖包列表
|
|
||||||
auto_install: 是否自动安装(True时不询问用户)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 安装是否成功
|
|
||||||
"""
|
|
||||||
if not dependencies:
|
|
||||||
return True
|
|
||||||
|
|
||||||
logger.info(f"需要安装 {len(dependencies)} 个依赖包")
|
|
||||||
|
|
||||||
# 显示将要安装的包
|
|
||||||
for dep in dependencies:
|
|
||||||
install_cmd = dep.get_pip_requirement()
|
|
||||||
logger.info(f" - {install_cmd} {'(可选)' if dep.optional else '(必需)'}")
|
|
||||||
if dep.description:
|
|
||||||
logger.info(f" 说明: {dep.description}")
|
|
||||||
|
|
||||||
if not auto_install:
|
|
||||||
# 这里可以添加用户确认逻辑
|
|
||||||
logger.warning("手动安装模式:请手动运行 pip install 命令安装依赖包")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 执行安装
|
|
||||||
success_count = 0
|
|
||||||
for dep in dependencies:
|
|
||||||
if self._install_single_package(dep):
|
|
||||||
success_count += 1
|
|
||||||
else:
|
|
||||||
self.failed_installs[dep.package_name] = f"安装失败: {dep.get_pip_requirement()}"
|
|
||||||
|
|
||||||
logger.info(f"依赖安装完成: {success_count}/{len(dependencies)} 个成功")
|
|
||||||
return success_count == len(dependencies)
|
|
||||||
|
|
||||||
def _install_single_package(self, dependency: PythonDependency) -> bool:
|
|
||||||
"""安装单个包"""
|
|
||||||
pip_requirement = dependency.get_pip_requirement()
|
|
||||||
|
|
||||||
try:
|
|
||||||
logger.info(f"正在安装: {pip_requirement}")
|
|
||||||
|
|
||||||
# 使用subprocess安装包
|
|
||||||
cmd = [sys.executable, "-m", "pip", "install", pip_requirement]
|
|
||||||
result = subprocess.run(
|
|
||||||
cmd,
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
timeout=300, # 5分钟超时
|
|
||||||
)
|
|
||||||
|
|
||||||
if result.returncode == 0:
|
|
||||||
logger.info(f"✅ 成功安装: {pip_requirement}")
|
|
||||||
self.install_log.append(f"成功安装: {pip_requirement}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
logger.error(f"❌ 安装失败: {pip_requirement}")
|
|
||||||
logger.error(f"错误输出: {result.stderr}")
|
|
||||||
self.install_log.append(f"安装失败: {pip_requirement} - {result.stderr}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
except subprocess.TimeoutExpired:
|
|
||||||
logger.error(f"❌ 安装超时: {pip_requirement}")
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"❌ 安装异常: {pip_requirement} - {str(e)}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def generate_requirements_file(
|
|
||||||
self, plugins_dependencies: List[List[PythonDependency]], output_path: str = "plugin_requirements.txt"
|
|
||||||
) -> bool:
|
|
||||||
"""生成插件依赖的requirements文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
plugins_dependencies: 所有插件的依赖列表
|
|
||||||
output_path: 输出文件路径
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 生成是否成功
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
all_deps = {}
|
|
||||||
|
|
||||||
# 合并所有插件的依赖
|
|
||||||
for plugin_deps in plugins_dependencies:
|
|
||||||
for dep in plugin_deps:
|
|
||||||
key = dep.install_name
|
|
||||||
if key in all_deps:
|
|
||||||
# 如果已存在,可以添加版本兼容性检查逻辑
|
|
||||||
existing = all_deps[key]
|
|
||||||
if dep.version and existing.version != dep.version:
|
|
||||||
logger.warning(f"依赖版本冲突: {key} ({existing.version} vs {dep.version})")
|
|
||||||
else:
|
|
||||||
all_deps[key] = dep
|
|
||||||
|
|
||||||
# 写入requirements文件
|
|
||||||
with open(output_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write("# 插件依赖包自动生成\n")
|
|
||||||
f.write("# Auto-generated plugin dependencies\n\n")
|
|
||||||
|
|
||||||
# 按包名排序
|
|
||||||
sorted_deps = sorted(all_deps.values(), key=lambda x: x.install_name)
|
|
||||||
|
|
||||||
for dep in sorted_deps:
|
|
||||||
requirement = dep.get_pip_requirement()
|
|
||||||
if dep.description:
|
|
||||||
f.write(f"# {dep.description}\n")
|
|
||||||
if dep.optional:
|
|
||||||
f.write("# Optional dependency\n")
|
|
||||||
f.write(f"{requirement}\n\n")
|
|
||||||
|
|
||||||
logger.info(f"已生成插件依赖文件: {output_path} ({len(all_deps)} 个包)")
|
|
||||||
return True
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"生成requirements文件失败: {str(e)}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_install_summary(self) -> Dict[str, Any]:
|
|
||||||
"""获取安装摘要"""
|
|
||||||
return {
|
|
||||||
"install_log": self.install_log.copy(),
|
|
||||||
"failed_installs": self.failed_installs.copy(),
|
|
||||||
"total_attempts": len(self.install_log),
|
|
||||||
"failed_count": len(self.failed_installs),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# 全局依赖管理器实例
|
|
||||||
dependency_manager = DependencyManager()
|
|
||||||
@@ -8,10 +8,9 @@ from pathlib import Path
|
|||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.plugin_system.base.plugin_base import PluginBase
|
from src.plugin_system.base.plugin_base import PluginBase
|
||||||
from src.plugin_system.base.component_types import ComponentType, PythonDependency
|
from src.plugin_system.base.component_types import ComponentType
|
||||||
from src.plugin_system.utils.manifest_utils import VersionComparator
|
from src.plugin_system.utils.manifest_utils import VersionComparator
|
||||||
from .component_registry import component_registry
|
from .component_registry import component_registry
|
||||||
from .dependency_manager import dependency_manager
|
|
||||||
|
|
||||||
logger = get_logger("plugin_manager")
|
logger = get_logger("plugin_manager")
|
||||||
|
|
||||||
@@ -207,104 +206,6 @@ class PluginManager:
|
|||||||
"""
|
"""
|
||||||
return self.loaded_plugins.get(plugin_name)
|
return self.loaded_plugins.get(plugin_name)
|
||||||
|
|
||||||
def check_all_dependencies(self, auto_install: bool = False) -> Dict[str, Any]:
|
|
||||||
"""检查所有插件的Python依赖包
|
|
||||||
|
|
||||||
Args:
|
|
||||||
auto_install: 是否自动安装缺失的依赖包
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, any]: 检查结果摘要
|
|
||||||
"""
|
|
||||||
logger.info("开始检查所有插件的Python依赖包...")
|
|
||||||
|
|
||||||
all_required_missing: List[PythonDependency] = []
|
|
||||||
all_optional_missing: List[PythonDependency] = []
|
|
||||||
plugin_status = {}
|
|
||||||
|
|
||||||
for plugin_name in self.loaded_plugins:
|
|
||||||
plugin_info = component_registry.get_plugin_info(plugin_name)
|
|
||||||
if not plugin_info or not plugin_info.python_dependencies:
|
|
||||||
plugin_status[plugin_name] = {"status": "no_dependencies", "missing": []}
|
|
||||||
continue
|
|
||||||
|
|
||||||
logger.info(f"检查插件 {plugin_name} 的依赖...")
|
|
||||||
|
|
||||||
missing_required, missing_optional = dependency_manager.check_dependencies(plugin_info.python_dependencies)
|
|
||||||
|
|
||||||
if missing_required:
|
|
||||||
all_required_missing.extend(missing_required)
|
|
||||||
plugin_status[plugin_name] = {
|
|
||||||
"status": "missing_required",
|
|
||||||
"missing": [dep.package_name for dep in missing_required],
|
|
||||||
"optional_missing": [dep.package_name for dep in missing_optional],
|
|
||||||
}
|
|
||||||
logger.error(f"插件 {plugin_name} 缺少必需依赖: {[dep.package_name for dep in missing_required]}")
|
|
||||||
elif missing_optional:
|
|
||||||
all_optional_missing.extend(missing_optional)
|
|
||||||
plugin_status[plugin_name] = {
|
|
||||||
"status": "missing_optional",
|
|
||||||
"missing": [],
|
|
||||||
"optional_missing": [dep.package_name for dep in missing_optional],
|
|
||||||
}
|
|
||||||
logger.warning(f"插件 {plugin_name} 缺少可选依赖: {[dep.package_name for dep in missing_optional]}")
|
|
||||||
else:
|
|
||||||
plugin_status[plugin_name] = {"status": "ok", "missing": []}
|
|
||||||
logger.info(f"插件 {plugin_name} 依赖检查通过")
|
|
||||||
|
|
||||||
# 汇总结果
|
|
||||||
total_missing = len({dep.package_name for dep in all_required_missing})
|
|
||||||
total_optional_missing = len({dep.package_name for dep in all_optional_missing})
|
|
||||||
|
|
||||||
logger.info(f"依赖检查完成 - 缺少必需包: {total_missing}个, 缺少可选包: {total_optional_missing}个")
|
|
||||||
|
|
||||||
# 如果需要自动安装
|
|
||||||
install_success = True
|
|
||||||
if auto_install and all_required_missing:
|
|
||||||
unique_required = {dep.package_name: dep for dep in all_required_missing}
|
|
||||||
logger.info(f"开始自动安装 {len(unique_required)} 个必需依赖包...")
|
|
||||||
install_success = dependency_manager.install_dependencies(list(unique_required.values()), auto_install=True)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"total_plugins_checked": len(plugin_status),
|
|
||||||
"plugins_with_missing_required": len(
|
|
||||||
[p for p in plugin_status.values() if p["status"] == "missing_required"]
|
|
||||||
),
|
|
||||||
"plugins_with_missing_optional": len(
|
|
||||||
[p for p in plugin_status.values() if p["status"] == "missing_optional"]
|
|
||||||
),
|
|
||||||
"total_missing_required": total_missing,
|
|
||||||
"total_missing_optional": total_optional_missing,
|
|
||||||
"plugin_status": plugin_status,
|
|
||||||
"auto_install_attempted": auto_install and bool(all_required_missing),
|
|
||||||
"auto_install_success": install_success,
|
|
||||||
"install_summary": dependency_manager.get_install_summary(),
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_plugin_requirements(self, output_path: str = "plugin_requirements.txt") -> bool:
|
|
||||||
"""生成所有插件依赖的requirements文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
output_path: 输出文件路径
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 生成是否成功
|
|
||||||
"""
|
|
||||||
logger.info("开始生成插件依赖requirements文件...")
|
|
||||||
|
|
||||||
all_dependencies = []
|
|
||||||
|
|
||||||
for plugin_name in self.loaded_plugins:
|
|
||||||
plugin_info = component_registry.get_plugin_info(plugin_name)
|
|
||||||
if plugin_info and plugin_info.python_dependencies:
|
|
||||||
all_dependencies.append(plugin_info.python_dependencies)
|
|
||||||
|
|
||||||
if not all_dependencies:
|
|
||||||
logger.info("没有找到任何插件依赖")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return dependency_manager.generate_requirements_file(all_dependencies, output_path)
|
|
||||||
|
|
||||||
# === 查询方法 ===
|
# === 查询方法 ===
|
||||||
def list_loaded_plugins(self) -> List[str]:
|
def list_loaded_plugins(self) -> List[str]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user