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/plugin.py CHANGED
@@ -1,27 +1,52 @@
1
1
  """
2
2
  ### PyPlugin - Python 高级插件库
3
- ##### 概述\n
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 typing import Type, List, Dict, Any, overload, Union
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 = 10
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(pkg)
100
+ module = importlib.import_module(pkg_name)
75
101
 
76
102
  # 检查版本要求
77
- if version_spec:
78
- if hasattr(module, "__version__"):
79
- installed_version = module.__version__
80
-
81
- # 解析版本规范
82
- if ">=" in version_spec:
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
- invalid.append(f"{pkg} (Module not found)")
111
+ missing.append(pkg_name)
113
112
  except ImportError:
114
- missing.append(pkg)
113
+ invalid.append(f"{pkg_name} (导入失败)")
115
114
  except Exception as e:
116
- invalid.append(f"{pkg} ({str(e)})")
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
- if version_issues:
133
- error_msgs.append(
134
- f"版本不兼容的依赖包: {', '.join(version_issues)}\n"
135
- f"请使用命令升级: pip install --upgrade {' '.join([v.split(' ')[0] for v in version_issues])}"
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 error_msgs:
139
- raise ImportError("\n\n".join(error_msgs))
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
- _PLUGIN_REGISTRY: List[PluginBase] = []
182
-
183
-
184
-
185
- def load_plugin(
186
- plugin_class: Type[PluginBase],
187
- priority: int = None,
188
- verbose: bool = False
189
- ) -> bool:
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
- plugin_class: 必须继承自PluginBase的类
195
- priority: 执行优先级(覆盖类默认值)
196
- verbose: 是否显示加载详情
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
- bool: 是否成功加载
200
- """
201
- try:
202
- # 类型验证
203
- if not issubclass(plugin_class, PluginBase):
204
- raise TypeError("必须继承自PluginBase")
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
- if priority is not None:
211
- instance.priority = priority
212
- else:
213
- instance.priority = plugin_class.priority
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
- insert_pos = 0
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
- _PLUGIN_REGISTRY.insert(insert_pos, instance)
222
-
223
- if verbose:
224
- meta = plugin_class.get_metadata()
225
- print(
226
- f"✅ 成功加载插件: {meta['name']} v{meta['version']}\n"
227
- f" 作者: {meta['author']}\n"
228
- f" 优先级: {instance.priority}\n"
229
- f" 依赖: {plugin_class.required_packages}"
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
- return True
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
- except Exception as e:
235
- if verbose:
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
- filepath: Python文件路径
280
- data: 传递给插件的数据
281
- plugin_class_name: 指定要执行的插件类名(可选)
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
- ValueError: 当文件包含多个插件但未指定执行方式时
289
- ImportError: 如果文件加载失败
290
- """
291
- import os
292
- import importlib.util
293
- from collections import OrderedDict
294
-
295
- # 验证文件存在
296
- if not os.path.exists(filepath):
297
- raise FileNotFoundError(f"插件文件不存在: {filepath}")
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
- module_name = os.path.splitext(os.path.basename(filepath))[0]
301
- spec = importlib.util.spec_from_file_location(module_name, filepath)
302
- if spec is None:
303
- raise ImportError(f"无效的Python模块: {filepath}")
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
- module = importlib.util.module_from_spec(spec)
306
- spec.loader.exec_module(module)
345
+ def get_all_plugins(self) -> List[PluginBase]:
346
+ """获取所有已加载插件"""
347
+ return self._plugins.copy()
307
348
 
308
- # 查找所有合法插件类
309
- plugin_classes = OrderedDict()
310
- for name, obj in module.__dict__.items():
311
- if (isinstance(obj, type) and
312
- issubclass(obj, PluginBase) and
313
- obj != PluginBase):
314
- plugin_classes[name] = obj
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
- if not plugin_classes:
318
- raise AttributeError(f"文件中未找到有效的插件类(需继承PluginBase): {filepath}")
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
- if len(plugin_classes) > 1:
322
- if not (plugin_class_name or execute_all):
323
- raise ValueError(
324
- f"文件中发现多个插件类: {list(plugin_classes.keys())}\n"
325
- "请指定 plugin_class_name 或设置 execute_all=True"
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
- if plugin_class_name:
330
- if plugin_class_name not in plugin_classes:
331
- raise KeyError(f"插件类 '{plugin_class_name}' 不存在,可选: {list(plugin_classes.keys())}")
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
- plugin_class = plugin_classes[plugin_class_name]
334
- if load_plugin(plugin_class, verbose=True):
335
- return plugin_class().execute(data)
336
- raise RuntimeError(f"插件加载失败: {plugin_class_name}")
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
- results = {}
340
- for name, plugin_class in plugin_classes.items():
341
- try:
342
- if load_plugin(plugin_class, verbose=True):
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
- return results
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)