tretool 0.2.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tretool/__init__.py +39 -12
- tretool/config.py +406 -170
- tretool/decoratorlib.py +423 -0
- tretool/encoding.py +404 -75
- tretool/httplib.py +730 -0
- tretool/jsonlib.py +619 -151
- tretool/logger.py +712 -0
- tretool/mathlib.py +0 -33
- tretool/path.py +19 -0
- tretool/platformlib.py +469 -314
- tretool/plugin.py +437 -237
- tretool/smartCache.py +569 -0
- tretool/tasklib.py +730 -0
- tretool/transform/docx.py +544 -0
- tretool/transform/pdf.py +273 -95
- tretool/ziplib.py +664 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/METADATA +11 -5
- tretool-1.0.0.dist-info/RECORD +24 -0
- tretool/markfunc.py +0 -152
- tretool/memorizeTools.py +0 -24
- tretool/writeLog.py +0 -69
- tretool-0.2.1.dist-info/RECORD +0 -20
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/WHEEL +0 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {tretool-0.2.1.dist-info → tretool-1.0.0.dist-info}/top_level.txt +0 -0
tretool/plugin.py
CHANGED
@@ -1,27 +1,52 @@
|
|
1
1
|
"""
|
2
2
|
### PyPlugin - Python 高级插件库
|
3
|
-
#####
|
3
|
+
##### 概述
|
4
4
|
PyPlugin 是一个功能强大的 Python 插件系统框架,提供了完整的插件开发、加载和执行解决方案。该系统特别适合构建可扩展的应用程序,支持:
|
5
5
|
|
6
6
|
- 插件生命周期管理:自动处理插件的加载、验证和执行
|
7
|
-
|
8
7
|
- 智能依赖检查:支持版本规范的依赖管理 (package>=1.2.0)
|
9
|
-
|
10
8
|
- 元数据验证:自动检查插件元数据的完整性和合理性
|
11
|
-
|
12
9
|
- 优先级系统:数值越小优先级越高(1-100范围)
|
13
|
-
|
14
10
|
- 彩色终端输出:使用 Rich 库提供直观的状态反馈
|
11
|
+
- 插件隔离:可选在独立进程中运行插件
|
12
|
+
- 热重载支持:动态重新加载插件而不重启应用
|
13
|
+
- 配置系统:支持插件级别的配置管理
|
15
14
|
"""
|
16
15
|
|
17
16
|
import importlib
|
18
|
-
|
17
|
+
import os
|
18
|
+
import sys
|
19
|
+
import inspect
|
20
|
+
from collections import OrderedDict
|
21
|
+
from pathlib import Path
|
22
|
+
from multiprocessing import Process, Queue
|
23
|
+
from typing import Type, List, Dict, Any, Optional, Union, Callable, Tuple
|
24
|
+
from dataclasses import dataclass, field
|
19
25
|
from abc import ABC, abstractmethod
|
20
|
-
from
|
26
|
+
from enum import Enum, auto
|
21
27
|
|
22
28
|
from rich import print as rich_print
|
29
|
+
from rich.table import Table
|
30
|
+
from rich.panel import Panel
|
23
31
|
from packaging.version import parse as parse_version
|
32
|
+
import yaml
|
33
|
+
|
34
|
+
|
35
|
+
class PluginState(Enum):
|
36
|
+
"""插件状态枚举"""
|
37
|
+
UNLOADED = auto()
|
38
|
+
LOADED = auto()
|
39
|
+
INITIALIZED = auto()
|
40
|
+
READY = auto()
|
41
|
+
ERROR = auto()
|
42
|
+
|
24
43
|
|
44
|
+
@dataclass
|
45
|
+
class PluginConfig:
|
46
|
+
"""插件配置数据类"""
|
47
|
+
enabled: bool = True
|
48
|
+
priority: Optional[int] = None
|
49
|
+
config: Dict[str, Any] = field(default_factory=dict)
|
25
50
|
|
26
51
|
|
27
52
|
class PluginBase(ABC):
|
@@ -32,22 +57,28 @@ class PluginBase(ABC):
|
|
32
57
|
"name": "Unnamed Plugin",
|
33
58
|
"version": "1.0.0",
|
34
59
|
"author": "Anonymous",
|
35
|
-
"description": "No description provided"
|
60
|
+
"description": "No description provided",
|
61
|
+
"category": "uncategorized"
|
36
62
|
}
|
37
63
|
|
38
64
|
# 定义插件依赖包(子类可覆盖)
|
39
65
|
required_packages: List[str] = []
|
40
66
|
|
41
67
|
# 默认执行优先级(数值越小优先级越高)
|
42
|
-
priority: int =
|
43
|
-
|
68
|
+
priority: int = 50
|
69
|
+
|
70
|
+
# 是否在独立进程中运行
|
71
|
+
run_in_process: bool = False
|
72
|
+
|
73
|
+
# 插件状态
|
74
|
+
_state: PluginState = PluginState.UNLOADED
|
44
75
|
|
45
|
-
|
46
76
|
def __init__(self):
|
47
|
-
"""
|
77
|
+
"""初始化时自动检查依赖和元数据"""
|
48
78
|
self._check_metadata()
|
49
|
-
|
50
79
|
self._check_dependencies()
|
80
|
+
self._state = PluginState.INITIALIZED
|
81
|
+
self._config = PluginConfig()
|
51
82
|
|
52
83
|
def _check_dependencies(self):
|
53
84
|
"""验证依赖包是否已安装,并检查版本要求"""
|
@@ -55,89 +86,75 @@ class PluginBase(ABC):
|
|
55
86
|
invalid = []
|
56
87
|
version_issues = []
|
57
88
|
|
58
|
-
# 处理特殊的依赖格式 (如 "package>=1.0")
|
59
|
-
dependencies = []
|
60
89
|
for dep in self.required_packages:
|
61
|
-
if isinstance(dep, str) and any(op in dep for op in [">", "<", "=", "!"]):
|
62
|
-
# 处理版本要求
|
63
|
-
try:
|
64
|
-
pkg_name = dep.split('>')[0].split('<')[0].split('=')[0].split('!')[0].strip()
|
65
|
-
dependencies.append((pkg_name, dep))
|
66
|
-
except Exception:
|
67
|
-
dependencies.append((dep, None))
|
68
|
-
else:
|
69
|
-
dependencies.append((dep, None))
|
70
|
-
|
71
|
-
for pkg, version_spec in dependencies:
|
72
90
|
try:
|
91
|
+
# 处理版本规范
|
92
|
+
if any(op in dep for op in [">", "<", "=", "!"]):
|
93
|
+
pkg_name = dep.split('>')[0].split('<')[0].split('=')[0].split('!')[0].strip()
|
94
|
+
version_spec = dep[len(pkg_name):].strip()
|
95
|
+
else:
|
96
|
+
pkg_name = dep
|
97
|
+
version_spec = None
|
98
|
+
|
73
99
|
# 尝试导入模块
|
74
|
-
module = importlib.import_module(
|
100
|
+
module = importlib.import_module(pkg_name)
|
75
101
|
|
76
102
|
# 检查版本要求
|
77
|
-
if version_spec:
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
min_version = version_spec.split(">=")[1].strip()
|
84
|
-
if parse_version(installed_version) < parse_version(min_version):
|
85
|
-
version_issues.append(f"{pkg} (需要版本 >= {min_version}, 当前版本 {installed_version})")
|
86
|
-
|
87
|
-
elif "<=" in version_spec:
|
88
|
-
max_version = version_spec.split("<=")[1].strip()
|
89
|
-
if parse_version(installed_version) > parse_version(max_version):
|
90
|
-
version_issues.append(f"{pkg} (需要版本 <= {max_version}, 当前版本 {installed_version})")
|
91
|
-
|
92
|
-
elif "==" in version_spec:
|
93
|
-
exact_version = version_spec.split("==")[1].strip()
|
94
|
-
if parse_version(installed_version) != parse_version(exact_version):
|
95
|
-
version_issues.append(f"{pkg} (需要版本 == {exact_version}, 当前版本 {installed_version})")
|
96
|
-
|
97
|
-
elif "!=" in version_spec:
|
98
|
-
exclude_version = version_spec.split("!=")[1].strip()
|
99
|
-
if parse_version(installed_version) == parse_version(exclude_version):
|
100
|
-
version_issues.append(f"{pkg} (需要版本 != {exclude_version}, 当前版本 {installed_version})")
|
101
|
-
|
102
|
-
elif ">" in version_spec:
|
103
|
-
min_version = version_spec.split(">")[1].strip()
|
104
|
-
if parse_version(installed_version) <= parse_version(min_version):
|
105
|
-
version_issues.append(f"{pkg} (需要版本 > {min_version}, 当前版本 {installed_version})")
|
103
|
+
if version_spec and hasattr(module, "__version__"):
|
104
|
+
installed_version = module.__version__
|
105
|
+
if not self._check_version_constraint(installed_version, version_spec):
|
106
|
+
version_issues.append(
|
107
|
+
f"{pkg_name} (需要 {version_spec}, 当前 {installed_version})"
|
108
|
+
)
|
106
109
|
|
107
|
-
elif "<" in version_spec:
|
108
|
-
max_version = version_spec.split("<")[1].strip()
|
109
|
-
if parse_version(installed_version) >= parse_version(max_version):
|
110
|
-
version_issues.append(f"{pkg} (需要版本 < {max_version}, 当前版本 {installed_version})")
|
111
110
|
except ModuleNotFoundError:
|
112
|
-
|
111
|
+
missing.append(pkg_name)
|
113
112
|
except ImportError:
|
114
|
-
|
113
|
+
invalid.append(f"{pkg_name} (导入失败)")
|
115
114
|
except Exception as e:
|
116
|
-
invalid.append(f"{
|
117
|
-
|
118
|
-
# 构建错误消息
|
119
|
-
error_msgs = []
|
120
|
-
if missing:
|
121
|
-
error_msgs.append(
|
122
|
-
f"缺少必要依赖包: {', '.join(missing)}\n"
|
123
|
-
f"请使用命令安装: pip install {' '.join(missing)}"
|
124
|
-
)
|
125
|
-
|
126
|
-
if invalid:
|
127
|
-
error_msgs.append(
|
128
|
-
f"无效的依赖包: {', '.join(invalid)}\n"
|
129
|
-
f"请检查包名是否正确或尝试重新安装"
|
130
|
-
)
|
115
|
+
invalid.append(f"{pkg_name} ({str(e)})")
|
131
116
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
117
|
+
# 如果有错误则抛出异常
|
118
|
+
if missing or invalid or version_issues:
|
119
|
+
error_msg = []
|
120
|
+
if missing:
|
121
|
+
error_msg.append(
|
122
|
+
f"缺少依赖: {', '.join(missing)}\n"
|
123
|
+
f"请安装: pip install {' '.join(missing)}"
|
124
|
+
)
|
125
|
+
if invalid:
|
126
|
+
error_msg.append(f"无效依赖: {', '.join(invalid)}")
|
127
|
+
if version_issues:
|
128
|
+
error_msg.append(f"版本冲突: {', '.join(version_issues)}")
|
129
|
+
|
130
|
+
self._state = PluginState.ERROR
|
131
|
+
raise ImportError("\n".join(error_msg))
|
132
|
+
|
133
|
+
def _check_version_constraint(self, version: str, constraint: str) -> bool:
|
134
|
+
"""检查版本是否符合约束条件"""
|
135
|
+
current = parse_version(version)
|
137
136
|
|
138
|
-
if
|
139
|
-
|
140
|
-
|
137
|
+
if ">=" in constraint:
|
138
|
+
required = parse_version(constraint.split(">=")[1])
|
139
|
+
return current >= required
|
140
|
+
elif "<=" in constraint:
|
141
|
+
required = parse_version(constraint.split("<=")[1])
|
142
|
+
return current <= required
|
143
|
+
elif "==" in constraint:
|
144
|
+
required = parse_version(constraint.split("==")[1])
|
145
|
+
return current == required
|
146
|
+
elif "!=" in constraint:
|
147
|
+
required = parse_version(constraint.split("!=")[1])
|
148
|
+
return current != required
|
149
|
+
elif ">" in constraint:
|
150
|
+
required = parse_version(constraint.split(">")[1])
|
151
|
+
return current > required
|
152
|
+
elif "<" in constraint:
|
153
|
+
required = parse_version(constraint.split("<")[1])
|
154
|
+
return current < required
|
155
|
+
else:
|
156
|
+
return True
|
157
|
+
|
141
158
|
def _check_metadata(self):
|
142
159
|
"""检查元数据是否被正确覆盖"""
|
143
160
|
default_meta = PluginBase.metadata
|
@@ -162,12 +179,30 @@ class PluginBase(ABC):
|
|
162
179
|
if current_meta.get("version") == "1.0.0":
|
163
180
|
rich_print("[yellow]⚠️ 插件版本未定义,使用默认版本[/yellow]")
|
164
181
|
|
165
|
-
def get_plugin_name(self):
|
182
|
+
def get_plugin_name(self) -> str:
|
183
|
+
"""获取插件名称"""
|
166
184
|
return self.metadata['name']
|
167
|
-
|
185
|
+
|
186
|
+
def get_state(self) -> PluginState:
|
187
|
+
"""获取当前插件状态"""
|
188
|
+
return self._state
|
189
|
+
|
190
|
+
def configure(self, config: Dict[str, Any]):
|
191
|
+
"""配置插件"""
|
192
|
+
self._config.config.update(config)
|
193
|
+
|
194
|
+
def initialize(self):
|
195
|
+
"""初始化插件资源"""
|
196
|
+
self._state = PluginState.READY
|
197
|
+
|
198
|
+
def cleanup(self):
|
199
|
+
"""清理插件资源"""
|
200
|
+
self._state = PluginState.INITIALIZED
|
201
|
+
|
168
202
|
@abstractmethod
|
169
203
|
def execute(self, data: Any) -> Any:
|
170
204
|
"""插件核心处理方法"""
|
205
|
+
self._state = PluginState.READY
|
171
206
|
pass
|
172
207
|
|
173
208
|
@classmethod
|
@@ -176,173 +211,338 @@ class PluginBase(ABC):
|
|
176
211
|
return cls.metadata.copy()
|
177
212
|
|
178
213
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
)
|
190
|
-
"""
|
191
|
-
加载并验证插件类
|
214
|
+
class PluginManager:
|
215
|
+
"""插件管理器,负责插件的加载、管理和执行"""
|
216
|
+
|
217
|
+
def __init__(self):
|
218
|
+
self._plugins: List[PluginBase] = []
|
219
|
+
self._plugin_configs: Dict[str, PluginConfig] = {}
|
220
|
+
self._plugin_classes: Dict[str, Type[PluginBase]] = {}
|
221
|
+
self._config_file = "plugins_config.yaml"
|
222
|
+
|
223
|
+
# 加载配置
|
224
|
+
self._load_config()
|
192
225
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
226
|
+
def _load_config(self):
|
227
|
+
"""从配置文件加载插件配置"""
|
228
|
+
if os.path.exists(self._config_file):
|
229
|
+
with open(self._config_file, 'r') as f:
|
230
|
+
config_data = yaml.safe_load(f) or {}
|
231
|
+
for plugin_name, config in config_data.items():
|
232
|
+
self._plugin_configs[plugin_name] = PluginConfig(**config)
|
233
|
+
|
234
|
+
def _save_config(self):
|
235
|
+
"""保存插件配置到文件"""
|
236
|
+
config_data = {}
|
237
|
+
for name, config in self._plugin_configs.items():
|
238
|
+
config_data[name] = {
|
239
|
+
"enabled": config.enabled,
|
240
|
+
"priority": config.priority,
|
241
|
+
"config": config.config
|
242
|
+
}
|
197
243
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
# 实例化插件(自动触发依赖检查)
|
207
|
-
instance = plugin_class()
|
244
|
+
with open(self._config_file, 'w') as f:
|
245
|
+
yaml.safe_dump(config_data, f)
|
246
|
+
|
247
|
+
def discover_plugins(self, directory: str = "plugins") -> List[str]:
|
248
|
+
"""发现指定目录下的所有插件"""
|
249
|
+
plugin_dir = Path(directory)
|
250
|
+
if not plugin_dir.exists():
|
251
|
+
return []
|
208
252
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
253
|
+
plugin_files = []
|
254
|
+
for file in plugin_dir.glob("**/*.py"):
|
255
|
+
if file.name.startswith("_"):
|
256
|
+
continue
|
257
|
+
plugin_files.append(str(file))
|
258
|
+
|
259
|
+
return plugin_files
|
260
|
+
|
261
|
+
def load_plugin_from_file(self, filepath: str) -> bool:
|
262
|
+
"""从文件加载插件"""
|
263
|
+
try:
|
264
|
+
# 动态加载模块
|
265
|
+
module_name = os.path.splitext(os.path.basename(filepath))[0]
|
266
|
+
spec = importlib.util.spec_from_file_location(module_name, filepath)
|
267
|
+
if spec is None:
|
268
|
+
raise ImportError(f"无效的Python模块: {filepath}")
|
214
269
|
|
215
|
-
|
216
|
-
|
217
|
-
while (insert_pos < len(_PLUGIN_REGISTRY) and
|
218
|
-
_PLUGIN_REGISTRY[insert_pos].priority <= instance.priority):
|
219
|
-
insert_pos += 1
|
270
|
+
module = importlib.util.module_from_spec(spec)
|
271
|
+
spec.loader.exec_module(module)
|
220
272
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
273
|
+
# 查找所有合法插件类
|
274
|
+
loaded = False
|
275
|
+
for name, obj in inspect.getmembers(module, inspect.isclass):
|
276
|
+
if (issubclass(obj, PluginBase) and
|
277
|
+
obj != PluginBase and
|
278
|
+
obj.__module__ == module.__name__):
|
279
|
+
|
280
|
+
self._plugin_classes[name] = obj
|
281
|
+
if name not in self._plugin_configs:
|
282
|
+
self._plugin_configs[name] = PluginConfig()
|
283
|
+
loaded = True
|
231
284
|
|
232
|
-
|
285
|
+
return loaded
|
286
|
+
|
287
|
+
except Exception as e:
|
288
|
+
rich_print(f"[red]加载插件 {filepath} 失败: {str(e)}[/red]")
|
289
|
+
return False
|
290
|
+
|
291
|
+
def load_all_plugins(self, directory: str = "plugins") -> int:
|
292
|
+
"""加载目录下的所有插件"""
|
293
|
+
plugin_files = self.discover_plugins(directory)
|
294
|
+
count = 0
|
295
|
+
for file in plugin_files:
|
296
|
+
if self.load_plugin_from_file(file):
|
297
|
+
count += 1
|
233
298
|
|
234
|
-
|
235
|
-
|
236
|
-
print(f"❌ 加载失败: {str(e)}")
|
237
|
-
return False
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
@overload
|
242
|
-
def execute_pipeline(data: List[Any] = None):
|
243
|
-
"""按优先级顺序执行所有插件"""
|
244
|
-
...
|
245
|
-
|
246
|
-
|
247
|
-
@overload
|
248
|
-
def execute_pipeline(data: Dict[str, Any] = None):
|
249
|
-
"""按优先级顺序执行所有插件"""
|
250
|
-
...
|
251
|
-
|
252
|
-
|
253
|
-
def execute_pipeline(data: Any = None) -> dict:
|
254
|
-
"""按优先级顺序执行所有插件"""
|
255
|
-
if isinstance(data, list):
|
256
|
-
res_dict = {}
|
257
|
-
for index, plugin in enumerate(_PLUGIN_REGISTRY):
|
258
|
-
result = plugin.execute(data[index])
|
259
|
-
res_dict[plugin.get_plugin_name()] = result
|
260
|
-
return res_dict
|
261
|
-
else:
|
262
|
-
res_dict = {}
|
263
|
-
for index, plugin in enumerate(_PLUGIN_REGISTRY):
|
264
|
-
result = plugin.execute(data[plugin.get_plugin_name()])
|
265
|
-
res_dict[plugin.get_plugin_name()] = result
|
266
|
-
return res_dict
|
267
|
-
|
268
|
-
|
269
|
-
def execute_plugin_by_file(
|
270
|
-
filepath: str,
|
271
|
-
data: Any = None,
|
272
|
-
plugin_class_name: str = None,
|
273
|
-
execute_all: bool = False
|
274
|
-
) -> Union[Any, Dict[str, Any]]:
|
275
|
-
"""
|
276
|
-
从Python文件加载并执行插件
|
299
|
+
rich_print(f"[green]成功加载 {count}/{len(plugin_files)} 个插件[/green]")
|
300
|
+
return count
|
277
301
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
execute_all: 是否执行文件中所有插件(默认False)
|
302
|
+
def instantiate_plugin(self, plugin_name: str) -> Optional[PluginBase]:
|
303
|
+
"""实例化指定插件"""
|
304
|
+
if plugin_name not in self._plugin_classes:
|
305
|
+
return None
|
283
306
|
|
284
|
-
|
285
|
-
|
307
|
+
plugin_class = self._plugin_classes[plugin_name]
|
308
|
+
config = self._plugin_configs.get(plugin_name, PluginConfig())
|
286
309
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
310
|
+
try:
|
311
|
+
instance = plugin_class()
|
312
|
+
|
313
|
+
# 应用配置
|
314
|
+
if config.priority is not None:
|
315
|
+
instance.priority = config.priority
|
316
|
+
if config.config:
|
317
|
+
instance.configure(config.config)
|
318
|
+
|
319
|
+
# 初始化插件
|
320
|
+
instance.initialize()
|
321
|
+
|
322
|
+
# 插入到插件列表并保持排序
|
323
|
+
self._insert_plugin_sorted(instance)
|
324
|
+
|
325
|
+
return instance
|
326
|
+
except Exception as e:
|
327
|
+
rich_print(f"[red]实例化插件 {plugin_name} 失败: {str(e)}[/red]")
|
328
|
+
return None
|
329
|
+
|
330
|
+
def _insert_plugin_sorted(self, plugin: PluginBase):
|
331
|
+
"""按优先级将插件插入到正确位置"""
|
332
|
+
for i, p in enumerate(self._plugins):
|
333
|
+
if p.priority > plugin.priority:
|
334
|
+
self._plugins.insert(i, plugin)
|
335
|
+
return
|
336
|
+
self._plugins.append(plugin)
|
298
337
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
338
|
+
def get_plugin(self, plugin_name: str) -> Optional[PluginBase]:
|
339
|
+
"""获取已加载的插件实例"""
|
340
|
+
for plugin in self._plugins:
|
341
|
+
if plugin.get_plugin_name() == plugin_name:
|
342
|
+
return plugin
|
343
|
+
return None
|
304
344
|
|
305
|
-
|
306
|
-
|
345
|
+
def get_all_plugins(self) -> List[PluginBase]:
|
346
|
+
"""获取所有已加载插件"""
|
347
|
+
return self._plugins.copy()
|
307
348
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
if
|
312
|
-
|
313
|
-
|
314
|
-
|
349
|
+
def execute_plugin(self, plugin_name: str, data: Any) -> Any:
|
350
|
+
"""执行指定插件"""
|
351
|
+
plugin = self.get_plugin(plugin_name)
|
352
|
+
if plugin is None:
|
353
|
+
raise ValueError(f"插件 {plugin_name} 未加载")
|
354
|
+
|
355
|
+
if plugin.run_in_process:
|
356
|
+
return self._execute_in_process(plugin, data)
|
357
|
+
else:
|
358
|
+
return plugin.execute(data)
|
359
|
+
|
360
|
+
def _execute_in_process(self, plugin: PluginBase, data: Any) -> Any:
|
361
|
+
"""在独立进程中执行插件"""
|
362
|
+
def _worker(plugin_class: Type[PluginBase], data: Any, queue: Queue):
|
363
|
+
try:
|
364
|
+
instance = plugin_class()
|
365
|
+
instance.initialize()
|
366
|
+
result = instance.execute(data)
|
367
|
+
queue.put(("success", result))
|
368
|
+
except Exception as e:
|
369
|
+
queue.put(("error", str(e)))
|
370
|
+
|
371
|
+
q = Queue()
|
372
|
+
p = Process(target=_worker, args=(plugin.__class__, data, q))
|
373
|
+
p.start()
|
374
|
+
p.join()
|
375
|
+
|
376
|
+
status, result = q.get()
|
377
|
+
if status == "error":
|
378
|
+
raise RuntimeError(f"插件执行失败: {result}")
|
379
|
+
return result
|
315
380
|
|
316
|
-
|
317
|
-
|
318
|
-
|
381
|
+
def execute_pipeline(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
382
|
+
"""按优先级顺序执行所有插件,支持插件特定的输入数据
|
383
|
+
|
384
|
+
参数:
|
385
|
+
data: 字典格式,键为插件名称,值为该插件的输入数据
|
386
|
+
|
387
|
+
返回:
|
388
|
+
字典格式的执行结果,键为插件名称,值为执行结果或错误信息
|
389
|
+
|
390
|
+
示例:
|
391
|
+
>>> data = {
|
392
|
+
... "Plugin1": {"param1": "value1"},
|
393
|
+
... "Plugin2": [1, 2, 3]
|
394
|
+
... }
|
395
|
+
>>> manager.execute_pipeline(data)
|
396
|
+
{
|
397
|
+
"Plugin1": {"result": "success"},
|
398
|
+
"Plugin2": {"error": "Invalid input"}
|
399
|
+
}
|
400
|
+
"""
|
401
|
+
results = {}
|
402
|
+
for plugin in self._plugins:
|
403
|
+
plugin_name = plugin.get_plugin_name()
|
404
|
+
|
405
|
+
# 检查插件是否启用
|
406
|
+
if not self._plugin_configs.get(plugin_name, PluginConfig()).enabled:
|
407
|
+
continue
|
408
|
+
|
409
|
+
try:
|
410
|
+
# 获取该插件的输入数据,如果没有则为None
|
411
|
+
plugin_data = data.get(plugin_name)
|
412
|
+
|
413
|
+
# 执行插件
|
414
|
+
results[plugin_name] = self.execute_plugin(plugin_name, plugin_data)
|
415
|
+
|
416
|
+
except Exception as e:
|
417
|
+
rich_print(f"[red]插件 {plugin_name} 执行失败: {str(e)}[/red]")
|
418
|
+
results[plugin_name] = {
|
419
|
+
"error": str(e),
|
420
|
+
"type": type(e).__name__,
|
421
|
+
"plugin": plugin_name
|
422
|
+
}
|
423
|
+
|
424
|
+
return results
|
425
|
+
|
426
|
+
def reload_plugin(self, plugin_name: str) -> bool:
|
427
|
+
"""重新加载插件"""
|
428
|
+
if plugin_name not in self._plugin_classes:
|
429
|
+
return False
|
430
|
+
|
431
|
+
# 移除现有实例
|
432
|
+
self._plugins = [p for p in self._plugins if p.get_plugin_name() != plugin_name]
|
433
|
+
|
434
|
+
# 重新实例化
|
435
|
+
return self.instantiate_plugin(plugin_name) is not None
|
319
436
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
437
|
+
def show_plugin_info(self):
|
438
|
+
"""显示所有插件信息"""
|
439
|
+
table = Table(title="已加载插件", show_header=True, header_style="bold magenta")
|
440
|
+
table.add_column("名称", style="cyan")
|
441
|
+
table.add_column("版本", style="green")
|
442
|
+
table.add_column("作者")
|
443
|
+
table.add_column("描述")
|
444
|
+
table.add_column("优先级")
|
445
|
+
table.add_column("状态")
|
446
|
+
|
447
|
+
for plugin in self._plugins:
|
448
|
+
meta = plugin.metadata
|
449
|
+
table.add_row(
|
450
|
+
meta["name"],
|
451
|
+
meta["version"],
|
452
|
+
meta["author"],
|
453
|
+
meta["description"],
|
454
|
+
str(plugin.priority),
|
455
|
+
plugin.get_state().name
|
326
456
|
)
|
457
|
+
|
458
|
+
rich_print(table)
|
327
459
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
460
|
+
def set_plugin_priority(self, plugin_name: str, priority: int):
|
461
|
+
"""设置插件优先级并重新排序"""
|
462
|
+
plugin = self.get_plugin(plugin_name)
|
463
|
+
if plugin is None:
|
464
|
+
raise ValueError(f"插件 {plugin_name} 未找到")
|
465
|
+
|
466
|
+
plugin.priority = priority
|
467
|
+
self._plugins.remove(plugin)
|
468
|
+
self._insert_plugin_sorted(plugin)
|
332
469
|
|
333
|
-
|
334
|
-
if
|
335
|
-
|
336
|
-
|
470
|
+
# 更新配置
|
471
|
+
if plugin_name in self._plugin_configs:
|
472
|
+
self._plugin_configs[plugin_name].priority = priority
|
473
|
+
self._save_config()
|
337
474
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
results[name] = plugin_class().execute(data)
|
344
|
-
except Exception as e:
|
345
|
-
rich_print(f"[red]❌ 插件 {name} 执行失败: {str(e)}[/red]")
|
346
|
-
results[name] = {"error": str(e)}
|
475
|
+
def enable_plugin(self, plugin_name: str, enable: bool = True):
|
476
|
+
"""启用或禁用插件"""
|
477
|
+
if plugin_name in self._plugin_configs:
|
478
|
+
self._plugin_configs[plugin_name].enabled = enable
|
479
|
+
self._save_config()
|
347
480
|
|
348
|
-
|
481
|
+
def get_plugin_config(self, plugin_name: str) -> Optional[PluginConfig]:
|
482
|
+
"""获取插件配置"""
|
483
|
+
return self._plugin_configs.get(plugin_name)
|
484
|
+
|
485
|
+
def update_plugin_config(self, plugin_name: str, config: Dict[str, Any]):
|
486
|
+
"""更新插件配置"""
|
487
|
+
if plugin_name in self._plugin_configs:
|
488
|
+
self._plugin_configs[plugin_name].config.update(config)
|
489
|
+
self._save_config()
|
490
|
+
|
491
|
+
# 如果插件已加载,则应用新配置
|
492
|
+
plugin = self.get_plugin(plugin_name)
|
493
|
+
if plugin:
|
494
|
+
plugin.configure(config)
|
495
|
+
|
496
|
+
|
497
|
+
# 全局插件管理器实例
|
498
|
+
_plugin_manager = PluginManager()
|
499
|
+
|
500
|
+
|
501
|
+
def get_plugin_manager() -> PluginManager:
|
502
|
+
"""获取全局插件管理器实例"""
|
503
|
+
return _plugin_manager
|
504
|
+
|
505
|
+
|
506
|
+
def load_plugins_from_directory(directory: str = "plugins") -> int:
|
507
|
+
"""从目录加载所有插件"""
|
508
|
+
return _plugin_manager.load_all_plugins(directory)
|
509
|
+
|
510
|
+
|
511
|
+
def execute_pipeline(data: Any) -> Dict[str, Any]:
|
512
|
+
"""执行插件管道"""
|
513
|
+
return _plugin_manager.execute_pipeline(data)
|
514
|
+
|
515
|
+
|
516
|
+
def show_plugin_info():
|
517
|
+
"""显示插件信息"""
|
518
|
+
_plugin_manager.show_plugin_info()
|
519
|
+
|
520
|
+
|
521
|
+
def reload_plugin(plugin_name: str) -> bool:
|
522
|
+
"""重新加载插件"""
|
523
|
+
return _plugin_manager.reload_plugin(plugin_name)
|
524
|
+
|
525
|
+
|
526
|
+
def set_plugin_priority(plugin_name: str, priority: int):
|
527
|
+
"""设置插件优先级"""
|
528
|
+
_plugin_manager.set_plugin_priority(plugin_name, priority)
|
529
|
+
|
530
|
+
|
531
|
+
def enable_plugin(plugin_name: str, enable: bool = True):
|
532
|
+
"""启用或禁用插件"""
|
533
|
+
_plugin_manager.enable_plugin(plugin_name, enable)
|
534
|
+
|
535
|
+
|
536
|
+
def get_plugin(plugin_name: str) -> Optional[PluginBase]:
|
537
|
+
"""获取插件实例"""
|
538
|
+
return _plugin_manager.get_plugin(plugin_name)
|
539
|
+
|
540
|
+
|
541
|
+
def execute_plugin(plugin_name: str, data: Any) -> Any:
|
542
|
+
"""执行单个插件"""
|
543
|
+
return _plugin_manager.execute_plugin(plugin_name, data)
|
544
|
+
|
545
|
+
|
546
|
+
def update_plugin_config(plugin_name: str, config: Dict[str, Any]):
|
547
|
+
"""更新插件配置"""
|
548
|
+
_plugin_manager.update_plugin_config(plugin_name, config)
|