tretool 0.2.1__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 +27 -0
- tretool/config.py +262 -0
- tretool/encoding.py +92 -0
- tretool/jsonlib.py +299 -0
- tretool/markfunc.py +152 -0
- tretool/mathlib.py +620 -0
- tretool/memorizeTools.py +24 -0
- tretool/path.py +1139 -0
- tretool/platformlib.py +332 -0
- tretool/plugin/plu.py +0 -0
- tretool/plugin.py +348 -0
- tretool/timelib.py +518 -0
- tretool/transform/__init__.py +1 -0
- tretool/transform/pdf.py +396 -0
- tretool/writeLog.py +69 -0
- tretool-0.2.1.dist-info/METADATA +28 -0
- tretool-0.2.1.dist-info/RECORD +20 -0
- tretool-0.2.1.dist-info/WHEEL +5 -0
- tretool-0.2.1.dist-info/licenses/LICENSE +19 -0
- tretool-0.2.1.dist-info/top_level.txt +1 -0
tretool/platformlib.py
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
import os
|
2
|
+
import sys
|
3
|
+
import ctypes
|
4
|
+
import platform
|
5
|
+
from typing import (Any, Dict, List, Union, SupportsIndex)
|
6
|
+
|
7
|
+
|
8
|
+
def get_python_dll() -> ctypes.PyDLL:
|
9
|
+
"""内部函数:获取 Python DLL 句柄"""
|
10
|
+
is_64bit = ctypes.sizeof(ctypes.c_void_p) == 8
|
11
|
+
dll_suffix = '_d' if hasattr(sys, 'gettotalrefcount') else ''
|
12
|
+
dll_name = f"python{''.join(map(str, sys.version_info[:2]))}{dll_suffix}.dll"
|
13
|
+
return ctypes.PyDLL(dll_name)
|
14
|
+
|
15
|
+
|
16
|
+
def get_python_version() -> str:
|
17
|
+
"""
|
18
|
+
获取 Python 版本号
|
19
|
+
返回格式:'major.minor.micro'
|
20
|
+
"""
|
21
|
+
try:
|
22
|
+
dll = get_python_dll()
|
23
|
+
Py_GetVersion = dll.Py_GetVersion
|
24
|
+
Py_GetVersion.restype = ctypes.c_char_p
|
25
|
+
return Py_GetVersion().decode('ascii').split()[0]
|
26
|
+
except Exception as e:
|
27
|
+
return f'Error: {str(e)}'
|
28
|
+
|
29
|
+
|
30
|
+
def get_python_build() -> str:
|
31
|
+
"""
|
32
|
+
获取 Python 构建信息
|
33
|
+
返回格式:如 "v3.9.7:8d82e9e9b3, Aug 31 2021, 13:28:12"
|
34
|
+
"""
|
35
|
+
try:
|
36
|
+
dll = get_python_dll()
|
37
|
+
Py_GetVersion = dll.Py_GetVersion
|
38
|
+
Py_GetVersion.restype = ctypes.c_char_p
|
39
|
+
version_info = Py_GetVersion().decode('ascii')
|
40
|
+
return f"v{version_info.split('(', 1)[1].split(')')[0]}"
|
41
|
+
except Exception as e:
|
42
|
+
return f'Error: {str(e)}'
|
43
|
+
|
44
|
+
|
45
|
+
def get_python_compiler() -> str:
|
46
|
+
"""
|
47
|
+
获取 Python 编译器信息
|
48
|
+
返回格式:如 "[GCC 8.4.0]"
|
49
|
+
"""
|
50
|
+
try:
|
51
|
+
dll = get_python_dll()
|
52
|
+
Py_GetVersion = dll.Py_GetVersion
|
53
|
+
Py_GetVersion.restype = ctypes.c_char_p
|
54
|
+
version_info = Py_GetVersion().decode('ascii')
|
55
|
+
return version_info.split('[')[1].split(']')[0]
|
56
|
+
except Exception as e:
|
57
|
+
return f'Error: {str(e)}'
|
58
|
+
|
59
|
+
|
60
|
+
def get_python_path() -> str:
|
61
|
+
"""
|
62
|
+
获取 Python 安装根目录(如 C:\\Python39)
|
63
|
+
"""
|
64
|
+
try:
|
65
|
+
return sys.executable.rsplit('\\', 1)[0]
|
66
|
+
|
67
|
+
except Exception as e:
|
68
|
+
return f'Error: {str(e)}'
|
69
|
+
|
70
|
+
|
71
|
+
def get_python_executable() -> str:
|
72
|
+
"""
|
73
|
+
获取 Python 解释器路径
|
74
|
+
"""
|
75
|
+
try:
|
76
|
+
return sys.executable
|
77
|
+
except Exception as e:
|
78
|
+
return f'Error: {str(e)}'
|
79
|
+
|
80
|
+
|
81
|
+
def get_python_flags() -> Dict[str, bool]:
|
82
|
+
"""
|
83
|
+
获取 Python 编译标志
|
84
|
+
返回字典包含:
|
85
|
+
- debug: 是否是调试版本
|
86
|
+
- utf8_mode: 是否启用UTF-8模式
|
87
|
+
"""
|
88
|
+
try:
|
89
|
+
flags = {
|
90
|
+
'debug': hasattr(sys, 'gettotalrefcount'),
|
91
|
+
'utf8_mode': sys.flags.utf8_mode
|
92
|
+
}
|
93
|
+
return flags
|
94
|
+
except Exception as e:
|
95
|
+
return {'error': str(e)}
|
96
|
+
|
97
|
+
|
98
|
+
def get_python_implementation() -> str:
|
99
|
+
"""
|
100
|
+
获取更友好的实现名称
|
101
|
+
"""
|
102
|
+
impl = sys.implementation.name
|
103
|
+
return {
|
104
|
+
'cpython': 'CPython',
|
105
|
+
'pypy': 'PyPy',
|
106
|
+
'ironpython': 'IronPython',
|
107
|
+
'jython': 'Jython'
|
108
|
+
}.get(impl, impl.capitalize())
|
109
|
+
|
110
|
+
|
111
|
+
def get_full_python_info() -> Dict[str, str]:
|
112
|
+
"""
|
113
|
+
获取完整的 Python 信息
|
114
|
+
返回包含所有信息的字典
|
115
|
+
"""
|
116
|
+
return {
|
117
|
+
'version': get_python_version(),
|
118
|
+
'build': get_python_build(),
|
119
|
+
'compiler': get_python_compiler(),
|
120
|
+
'path': get_python_path(),
|
121
|
+
'executable': get_python_executable(),
|
122
|
+
'flags': get_python_flags(),
|
123
|
+
'implementation': get_python_implementation()
|
124
|
+
}
|
125
|
+
|
126
|
+
|
127
|
+
def uname():
|
128
|
+
return platform.uname()
|
129
|
+
|
130
|
+
|
131
|
+
def get_system() -> str:
|
132
|
+
"""
|
133
|
+
获取操作系统类型(增强版)
|
134
|
+
返回:
|
135
|
+
'Windows'/'Linux'/'Darwin'/'Java' 等友好名称
|
136
|
+
"""
|
137
|
+
system = platform.uname().system
|
138
|
+
return {
|
139
|
+
'Linux': 'Linux',
|
140
|
+
'Darwin': 'macOS',
|
141
|
+
'Windows': 'Windows',
|
142
|
+
'Java': 'Java'
|
143
|
+
}.get(system, system)
|
144
|
+
|
145
|
+
|
146
|
+
def release() -> str:
|
147
|
+
"""
|
148
|
+
获取操作系统版本号(跨平台)
|
149
|
+
返回:
|
150
|
+
Linux: 内核版本 (如 '5.4.0-80-generic')
|
151
|
+
Windows: 版本号 (如 '10')
|
152
|
+
macOS: Darwin 版本 (如 '21.1.0')
|
153
|
+
"""
|
154
|
+
try:
|
155
|
+
if hasattr(os, 'uname'):
|
156
|
+
return os.uname().release
|
157
|
+
return platform.release()
|
158
|
+
except Exception:
|
159
|
+
return "unknown"
|
160
|
+
|
161
|
+
|
162
|
+
def machine() -> str:
|
163
|
+
"""
|
164
|
+
获取系统架构(跨平台)
|
165
|
+
返回:
|
166
|
+
'x86_64'/'AMD64'/'arm64' 等标准架构名称
|
167
|
+
"""
|
168
|
+
try:
|
169
|
+
if hasattr(os, 'uname'):
|
170
|
+
return os.uname().machine
|
171
|
+
return platform.machine()
|
172
|
+
except Exception:
|
173
|
+
return "unknown"
|
174
|
+
|
175
|
+
|
176
|
+
def get_byteorder() -> str:
|
177
|
+
"""
|
178
|
+
智能检测字节顺序(优先使用标准库)
|
179
|
+
"""
|
180
|
+
try:
|
181
|
+
import sys
|
182
|
+
return sys.byteorder
|
183
|
+
except ImportError:
|
184
|
+
try:
|
185
|
+
import ctypes
|
186
|
+
num = 0x12345678
|
187
|
+
buf = (ctypes.c_byte * 4).from_buffer_copy(ctypes.c_uint32(num))
|
188
|
+
return 'big' if buf[0] == 0x12 else 'little'
|
189
|
+
except:
|
190
|
+
return 'unknown'
|
191
|
+
|
192
|
+
|
193
|
+
def get_args(index: SupportsIndex = None) -> Union[List[str], str]:
|
194
|
+
"""
|
195
|
+
获取命令行参数(支持索引访问)
|
196
|
+
|
197
|
+
参数:
|
198
|
+
index - 可选参数索引(支持负数索引)
|
199
|
+
|
200
|
+
返回:
|
201
|
+
当index为None时:返回完整参数列表['script.py', 'arg1', ...]
|
202
|
+
当指定index时:返回对应位置的参数值
|
203
|
+
|
204
|
+
异常:
|
205
|
+
IndexError - 当索引超出范围时引发
|
206
|
+
"""
|
207
|
+
args = sys.argv
|
208
|
+
|
209
|
+
if index is None:
|
210
|
+
return args.copy() # 返回副本避免外部修改
|
211
|
+
|
212
|
+
try:
|
213
|
+
return args[index] # 自动支持SupportsIndex类型
|
214
|
+
except IndexError:
|
215
|
+
raise IndexError(f"参数索引 {index} 超出范围 (参数总数: {len(args)})") from None
|
216
|
+
|
217
|
+
|
218
|
+
def get_all_flags() -> Dict[str, Dict[str, Any]]:
|
219
|
+
"""
|
220
|
+
获取Python解释器的完整标志信息
|
221
|
+
|
222
|
+
返回:
|
223
|
+
包含三个子字典的字典:
|
224
|
+
- 'runtime_flags': 运行时标志(来自sys.flags)
|
225
|
+
- 'build_info': 构建配置信息
|
226
|
+
- 'unicode_info': Unicode相关配置
|
227
|
+
|
228
|
+
标志说明:
|
229
|
+
runtime_flags:
|
230
|
+
debug: 调试模式
|
231
|
+
inspect: 交互模式后进入检查模式
|
232
|
+
interactive: 交互模式
|
233
|
+
optimize: 优化级别 (0/1/2)
|
234
|
+
dont_write_bytecode: 不生成.pyc文件
|
235
|
+
no_user_site: 忽略用户site-packages
|
236
|
+
no_site: 忽略所有site-packages
|
237
|
+
ignore_environment: 忽略环境变量
|
238
|
+
verbose: 详细输出
|
239
|
+
bytes_warning: 字节警告
|
240
|
+
quiet: 安静模式
|
241
|
+
hash_randomization: 哈希随机化
|
242
|
+
isolated: 隔离模式
|
243
|
+
dev_mode: 开发模式
|
244
|
+
utf8_mode: UTF-8模式
|
245
|
+
warn_default_encoding: 默认编码警告
|
246
|
+
safe_path: 安全路径模式
|
247
|
+
int_max_str_digits: 整数字符串转换最大位数
|
248
|
+
"""
|
249
|
+
return {
|
250
|
+
'runtime_flags': get_runtime_flags(),
|
251
|
+
'build_info': get_build_info(),
|
252
|
+
'unicode_info': get_unicode_info()
|
253
|
+
}
|
254
|
+
|
255
|
+
|
256
|
+
def get_runtime_flags() -> Dict[str, Any]:
|
257
|
+
"""获取sys.flags中的所有运行时标志"""
|
258
|
+
flags = sys.flags
|
259
|
+
return {
|
260
|
+
'debug': flags.debug,
|
261
|
+
'inspect': flags.inspect,
|
262
|
+
'interactive': flags.interactive,
|
263
|
+
'optimize': flags.optimize,
|
264
|
+
'dont_write_bytecode': flags.dont_write_bytecode,
|
265
|
+
'no_user_site': flags.no_user_site,
|
266
|
+
'no_site': flags.no_site,
|
267
|
+
'ignore_environment': flags.ignore_environment,
|
268
|
+
'verbose': flags.verbose,
|
269
|
+
'bytes_warning': flags.bytes_warning,
|
270
|
+
'quiet': flags.quiet,
|
271
|
+
'hash_randomization': flags.hash_randomization,
|
272
|
+
'isolated': flags.isolated,
|
273
|
+
'dev_mode': flags.dev_mode,
|
274
|
+
'utf8_mode': flags.utf8_mode,
|
275
|
+
'warn_default_encoding': getattr(flags, 'warn_default_encoding', 0),
|
276
|
+
'safe_path': getattr(flags, 'safe_path', False),
|
277
|
+
'int_max_str_digits': getattr(flags, 'int_max_str_digits', 0)
|
278
|
+
}
|
279
|
+
|
280
|
+
|
281
|
+
def get_build_info() -> Dict[str, Any]:
|
282
|
+
"""获取构建配置信息"""
|
283
|
+
return {
|
284
|
+
'debug_build': hasattr(sys, 'gettotalrefcount'),
|
285
|
+
'compiler': get_python_compiler(),
|
286
|
+
'build_options': parse_build_options(),
|
287
|
+
'platform': get_system(),
|
288
|
+
'implementation': get_python_implementation()
|
289
|
+
}
|
290
|
+
|
291
|
+
|
292
|
+
def parse_build_options() -> Dict[str, bool]:
|
293
|
+
"""从构建字符串解析编译选项"""
|
294
|
+
build_str = sys.version
|
295
|
+
options = {}
|
296
|
+
|
297
|
+
# 常见构建选项检测
|
298
|
+
for opt in ['WITH_PYMALLOC', 'WITH_THREAD', 'PYTHONFRAMEWORK']:
|
299
|
+
options[opt] = opt in build_str
|
300
|
+
|
301
|
+
# 检测内存分配器
|
302
|
+
options['PYMALLOC'] = 'pymalloc' in build_str.lower()
|
303
|
+
return options
|
304
|
+
|
305
|
+
|
306
|
+
def get_unicode_info() -> Dict[str, Any]:
|
307
|
+
"""获取Unicode配置信息"""
|
308
|
+
return {
|
309
|
+
'maxunicode': sys.maxunicode,
|
310
|
+
'wide_build': sys.maxunicode > 0xFFFF,
|
311
|
+
'default_encoding': sys.getdefaultencoding(),
|
312
|
+
'filesystem_encoding': sys.getfilesystemencoding()
|
313
|
+
}
|
314
|
+
|
315
|
+
|
316
|
+
def get_environment_report() -> Dict[str, Dict[str, str]]:
|
317
|
+
"""生成完整的环境报告"""
|
318
|
+
return {
|
319
|
+
'python': {
|
320
|
+
'version': get_python_version(),
|
321
|
+
'build': get_python_build(),
|
322
|
+
'implementation': get_python_implementation(),
|
323
|
+
'compiler': get_python_compiler(),
|
324
|
+
'path': get_python_path(),
|
325
|
+
'executable': get_python_executable()
|
326
|
+
},
|
327
|
+
'runtime': {
|
328
|
+
'byteorder': get_byteorder(),
|
329
|
+
'flags': get_all_flags(),
|
330
|
+
'argv': get_args()
|
331
|
+
}
|
332
|
+
}
|
tretool/plugin/plu.py
ADDED
File without changes
|
tretool/plugin.py
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
"""
|
2
|
+
### PyPlugin - Python 高级插件库
|
3
|
+
##### 概述\n
|
4
|
+
PyPlugin 是一个功能强大的 Python 插件系统框架,提供了完整的插件开发、加载和执行解决方案。该系统特别适合构建可扩展的应用程序,支持:
|
5
|
+
|
6
|
+
- 插件生命周期管理:自动处理插件的加载、验证和执行
|
7
|
+
|
8
|
+
- 智能依赖检查:支持版本规范的依赖管理 (package>=1.2.0)
|
9
|
+
|
10
|
+
- 元数据验证:自动检查插件元数据的完整性和合理性
|
11
|
+
|
12
|
+
- 优先级系统:数值越小优先级越高(1-100范围)
|
13
|
+
|
14
|
+
- 彩色终端输出:使用 Rich 库提供直观的状态反馈
|
15
|
+
"""
|
16
|
+
|
17
|
+
import importlib
|
18
|
+
|
19
|
+
from abc import ABC, abstractmethod
|
20
|
+
from typing import Type, List, Dict, Any, overload, Union
|
21
|
+
|
22
|
+
from rich import print as rich_print
|
23
|
+
from packaging.version import parse as parse_version
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
class PluginBase(ABC):
|
28
|
+
"""插件基类,支持高级扩展功能"""
|
29
|
+
|
30
|
+
# 类属性定义元数据(子类可覆盖)
|
31
|
+
metadata = {
|
32
|
+
"name": "Unnamed Plugin",
|
33
|
+
"version": "1.0.0",
|
34
|
+
"author": "Anonymous",
|
35
|
+
"description": "No description provided"
|
36
|
+
}
|
37
|
+
|
38
|
+
# 定义插件依赖包(子类可覆盖)
|
39
|
+
required_packages: List[str] = []
|
40
|
+
|
41
|
+
# 默认执行优先级(数值越小优先级越高)
|
42
|
+
priority: int = 10
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
def __init__(self):
|
47
|
+
"""初始化时自动检查依赖"""
|
48
|
+
self._check_metadata()
|
49
|
+
|
50
|
+
self._check_dependencies()
|
51
|
+
|
52
|
+
def _check_dependencies(self):
|
53
|
+
"""验证依赖包是否已安装,并检查版本要求"""
|
54
|
+
missing = []
|
55
|
+
invalid = []
|
56
|
+
version_issues = []
|
57
|
+
|
58
|
+
# 处理特殊的依赖格式 (如 "package>=1.0")
|
59
|
+
dependencies = []
|
60
|
+
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
|
+
try:
|
73
|
+
# 尝试导入模块
|
74
|
+
module = importlib.import_module(pkg)
|
75
|
+
|
76
|
+
# 检查版本要求
|
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})")
|
106
|
+
|
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
|
+
except ModuleNotFoundError:
|
112
|
+
invalid.append(f"{pkg} (Module not found)")
|
113
|
+
except ImportError:
|
114
|
+
missing.append(pkg)
|
115
|
+
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
|
+
)
|
131
|
+
|
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
|
+
)
|
137
|
+
|
138
|
+
if error_msgs:
|
139
|
+
raise ImportError("\n\n".join(error_msgs))
|
140
|
+
|
141
|
+
def _check_metadata(self):
|
142
|
+
"""检查元数据是否被正确覆盖"""
|
143
|
+
default_meta = PluginBase.metadata
|
144
|
+
current_meta = self.metadata
|
145
|
+
|
146
|
+
# 检查所有默认元数据字段是否存在
|
147
|
+
missing_fields = [field for field in default_meta if field not in current_meta]
|
148
|
+
|
149
|
+
# 检查是否有字段使用默认值
|
150
|
+
default_values = [field for field in default_meta if current_meta.get(field) == default_meta[field]]
|
151
|
+
|
152
|
+
if missing_fields:
|
153
|
+
rich_print(f"[yellow]⚠️ 元数据字段缺失: {', '.join(missing_fields)}[/yellow]")
|
154
|
+
|
155
|
+
if default_values:
|
156
|
+
rich_print(f"[yellow]⚠️ 使用默认元数据值: {', '.join(default_values)}[/yellow]")
|
157
|
+
|
158
|
+
# 检查关键字段是否有效
|
159
|
+
if current_meta.get("name") == "Unnamed Plugin":
|
160
|
+
rich_print("[yellow]⚠️ 插件名称未定义,使用默认名称[/yellow]")
|
161
|
+
|
162
|
+
if current_meta.get("version") == "1.0.0":
|
163
|
+
rich_print("[yellow]⚠️ 插件版本未定义,使用默认版本[/yellow]")
|
164
|
+
|
165
|
+
def get_plugin_name(self):
|
166
|
+
return self.metadata['name']
|
167
|
+
|
168
|
+
@abstractmethod
|
169
|
+
def execute(self, data: Any) -> Any:
|
170
|
+
"""插件核心处理方法"""
|
171
|
+
pass
|
172
|
+
|
173
|
+
@classmethod
|
174
|
+
def get_metadata(cls) -> Dict[str, str]:
|
175
|
+
"""获取插件元数据"""
|
176
|
+
return cls.metadata.copy()
|
177
|
+
|
178
|
+
|
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
|
+
加载并验证插件类
|
192
|
+
|
193
|
+
参数:
|
194
|
+
plugin_class: 必须继承自PluginBase的类
|
195
|
+
priority: 执行优先级(覆盖类默认值)
|
196
|
+
verbose: 是否显示加载详情
|
197
|
+
|
198
|
+
返回:
|
199
|
+
bool: 是否成功加载
|
200
|
+
"""
|
201
|
+
try:
|
202
|
+
# 类型验证
|
203
|
+
if not issubclass(plugin_class, PluginBase):
|
204
|
+
raise TypeError("必须继承自PluginBase")
|
205
|
+
|
206
|
+
# 实例化插件(自动触发依赖检查)
|
207
|
+
instance = plugin_class()
|
208
|
+
|
209
|
+
# 设置优先级
|
210
|
+
if priority is not None:
|
211
|
+
instance.priority = priority
|
212
|
+
else:
|
213
|
+
instance.priority = plugin_class.priority
|
214
|
+
|
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
|
220
|
+
|
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
|
+
)
|
231
|
+
|
232
|
+
return True
|
233
|
+
|
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文件加载并执行插件
|
277
|
+
|
278
|
+
参数:
|
279
|
+
filepath: Python文件路径
|
280
|
+
data: 传递给插件的数据
|
281
|
+
plugin_class_name: 指定要执行的插件类名(可选)
|
282
|
+
execute_all: 是否执行文件中所有插件(默认False)
|
283
|
+
|
284
|
+
返回:
|
285
|
+
单个插件结果 或 字典格式的{插件类名: 执行结果}
|
286
|
+
|
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}")
|
298
|
+
|
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}")
|
304
|
+
|
305
|
+
module = importlib.util.module_from_spec(spec)
|
306
|
+
spec.loader.exec_module(module)
|
307
|
+
|
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
|
315
|
+
|
316
|
+
# 处理插件类发现情况
|
317
|
+
if not plugin_classes:
|
318
|
+
raise AttributeError(f"文件中未找到有效的插件类(需继承PluginBase): {filepath}")
|
319
|
+
|
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"
|
326
|
+
)
|
327
|
+
|
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())}")
|
332
|
+
|
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}")
|
337
|
+
|
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)}
|
347
|
+
|
348
|
+
return results
|