ErisPulse 2.1.0__py3-none-any.whl → 2.1.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.
- ErisPulse/Core/adapter.py +4 -75
- ErisPulse/Core/logger.py +55 -0
- ErisPulse/__init__.py +132 -113
- ErisPulse/__main__.py +115 -43
- {erispulse-2.1.0.dist-info → erispulse-2.1.1.dist-info}/METADATA +1 -1
- {erispulse-2.1.0.dist-info → erispulse-2.1.1.dist-info}/RECORD +9 -9
- {erispulse-2.1.0.dist-info → erispulse-2.1.1.dist-info}/WHEEL +0 -0
- {erispulse-2.1.0.dist-info → erispulse-2.1.1.dist-info}/entry_points.txt +0 -0
- {erispulse-2.1.0.dist-info → erispulse-2.1.1.dist-info}/licenses/LICENSE +0 -0
ErisPulse/Core/adapter.py
CHANGED
|
@@ -96,7 +96,6 @@ class BaseAdapter:
|
|
|
96
96
|
|
|
97
97
|
:param text: 文本内容
|
|
98
98
|
:return: 异步任务
|
|
99
|
-
|
|
100
99
|
:example:
|
|
101
100
|
>>> await adapter.Send.To("123").Text("Hello")
|
|
102
101
|
"""
|
|
@@ -113,33 +112,23 @@ class BaseAdapter:
|
|
|
113
112
|
"""
|
|
114
113
|
初始化适配器
|
|
115
114
|
"""
|
|
116
|
-
self._handlers = defaultdict(list)
|
|
117
|
-
self._onebot_handlers = defaultdict(list) # OneBot12事件处理器
|
|
115
|
+
self._handlers = defaultdict(list)
|
|
118
116
|
self._middlewares = []
|
|
119
117
|
self.Send = self.__class__.Send(self)
|
|
120
118
|
|
|
121
|
-
def on(self, event_type: str = "*"
|
|
119
|
+
def on(self, event_type: str = "*") -> Callable[[Callable], Callable]:
|
|
122
120
|
"""
|
|
123
121
|
适配器事件监听装饰器
|
|
124
122
|
|
|
125
123
|
:param event_type: 事件类型
|
|
126
|
-
:param onebot12: 是否监听OneBot12协议事件
|
|
127
124
|
:return: 装饰器函数
|
|
128
|
-
|
|
129
|
-
:example:
|
|
130
|
-
>>> @adapter.on("message")
|
|
131
|
-
>>> async def handle_message(data):
|
|
132
|
-
>>> print(f"收到消息: {data}")
|
|
133
125
|
"""
|
|
134
126
|
def decorator(func: Callable) -> Callable:
|
|
135
127
|
@functools.wraps(func)
|
|
136
128
|
async def wrapper(*args, **kwargs):
|
|
137
129
|
return await func(*args, **kwargs)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
self._onebot_handlers[event_type].append(wrapper)
|
|
141
|
-
else:
|
|
142
|
-
self._handlers[event_type].append(wrapper)
|
|
130
|
+
self._handlers[event_type].append(wrapper)
|
|
131
|
+
|
|
143
132
|
return wrapper
|
|
144
133
|
return decorator
|
|
145
134
|
|
|
@@ -166,7 +155,6 @@ class BaseAdapter:
|
|
|
166
155
|
:param endpoint: API端点
|
|
167
156
|
:param params: API参数
|
|
168
157
|
:return: API调用结果
|
|
169
|
-
|
|
170
158
|
:raises NotImplementedError: 必须由子类实现
|
|
171
159
|
"""
|
|
172
160
|
raise NotImplementedError("适配器必须实现call_api方法")
|
|
@@ -186,36 +174,6 @@ class BaseAdapter:
|
|
|
186
174
|
:raises NotImplementedError: 必须由子类实现
|
|
187
175
|
"""
|
|
188
176
|
raise NotImplementedError("适配器必须实现shutdown方法")
|
|
189
|
-
|
|
190
|
-
def add_handler(self, *args: Any) -> None:
|
|
191
|
-
"""
|
|
192
|
-
添加事件处理器
|
|
193
|
-
|
|
194
|
-
:param args: 参数列表
|
|
195
|
-
- 1个参数: 处理器函数(监听所有事件)
|
|
196
|
-
- 2个参数: 事件类型和处理器函数
|
|
197
|
-
|
|
198
|
-
:raises TypeError: 当参数数量无效时抛出
|
|
199
|
-
|
|
200
|
-
:example:
|
|
201
|
-
>>> # 监听所有事件
|
|
202
|
-
>>> adapter.add_handler(handle_all_events)
|
|
203
|
-
>>> # 监听特定事件
|
|
204
|
-
>>> adapter.add_handler("message", handle_message)
|
|
205
|
-
"""
|
|
206
|
-
if len(args) == 1:
|
|
207
|
-
event_type = "*"
|
|
208
|
-
handler = args[0]
|
|
209
|
-
elif len(args) == 2:
|
|
210
|
-
event_type, handler = args
|
|
211
|
-
else:
|
|
212
|
-
raise TypeError("add_handler() 接受 1 个(监听所有事件)或 2 个参数(指定事件类型)")
|
|
213
|
-
|
|
214
|
-
@functools.wraps(handler)
|
|
215
|
-
async def wrapper(*handler_args, **handler_kwargs):
|
|
216
|
-
return await handler(*handler_args, **handler_kwargs)
|
|
217
|
-
|
|
218
|
-
self._handlers[event_type].append(wrapper)
|
|
219
177
|
|
|
220
178
|
async def emit(self, event_type: str, data: Any) -> None:
|
|
221
179
|
"""
|
|
@@ -240,35 +198,6 @@ class BaseAdapter:
|
|
|
240
198
|
for handler in self._handlers.get("*", []):
|
|
241
199
|
await handler(data)
|
|
242
200
|
|
|
243
|
-
async def emit_onebot12(self, event_type: str, onebot_data: Dict) -> None:
|
|
244
|
-
"""
|
|
245
|
-
提交OneBot12协议事件
|
|
246
|
-
|
|
247
|
-
:param event_type: OneBot12事件类型
|
|
248
|
-
:param onebot_data: 符合OneBot12标准的事件数据
|
|
249
|
-
|
|
250
|
-
:example:
|
|
251
|
-
>>> await adapter.emit_onebot12("message", {
|
|
252
|
-
>>> "id": "123",
|
|
253
|
-
>>> "time": 1620000000,
|
|
254
|
-
>>> "type": "message",
|
|
255
|
-
>>> "detail_type": "private",
|
|
256
|
-
>>> "message": [{"type": "text", "data": {"text": "Hello"}}]
|
|
257
|
-
>>> })
|
|
258
|
-
"""
|
|
259
|
-
# 先执行中间件
|
|
260
|
-
for middleware in self._middlewares:
|
|
261
|
-
onebot_data = await middleware(onebot_data)
|
|
262
|
-
|
|
263
|
-
# 触发OneBot12事件处理器
|
|
264
|
-
if event_type in self._onebot_handlers:
|
|
265
|
-
for handler in self._onebot_handlers[event_type]:
|
|
266
|
-
await handler(onebot_data)
|
|
267
|
-
|
|
268
|
-
# 触发通配符 "*" 的处理器
|
|
269
|
-
for handler in self._onebot_handlers.get("*", []):
|
|
270
|
-
await handler(onebot_data)
|
|
271
|
-
|
|
272
201
|
async def send(self, target_type: str, target_id: str, message: Any, **kwargs: Any) -> Any:
|
|
273
202
|
"""
|
|
274
203
|
发送消息的便捷方法
|
ErisPulse/Core/logger.py
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ErisPulse 日志系统
|
|
3
|
+
|
|
4
|
+
提供模块化日志记录功能,支持多级日志、模块过滤和内存存储。
|
|
5
|
+
|
|
6
|
+
{!--< tips >!--}
|
|
7
|
+
1. 支持按模块设置不同日志级别
|
|
8
|
+
2. 日志可存储在内存中供后续分析
|
|
9
|
+
3. 自动识别调用模块名称
|
|
10
|
+
{!--< /tips >!--}
|
|
11
|
+
"""
|
|
12
|
+
|
|
1
13
|
import logging
|
|
2
14
|
import inspect
|
|
3
15
|
import datetime
|
|
4
16
|
from typing import List, Dict, Any, Optional, Union, Type, Set, Tuple, FrozenSet
|
|
5
17
|
|
|
6
18
|
class Logger:
|
|
19
|
+
"""
|
|
20
|
+
日志管理器
|
|
21
|
+
|
|
22
|
+
提供模块化日志记录和存储功能
|
|
23
|
+
|
|
24
|
+
{!--< tips >!--}
|
|
25
|
+
1. 使用set_module_level设置模块日志级别
|
|
26
|
+
2. 使用get_logs获取历史日志
|
|
27
|
+
3. 支持标准日志级别(DEBUG, INFO等)
|
|
28
|
+
{!--< /tips >!--}
|
|
29
|
+
"""
|
|
7
30
|
def __init__(self):
|
|
8
31
|
self._logs = {}
|
|
9
32
|
self._module_levels = {}
|
|
@@ -16,6 +39,12 @@ class Logger:
|
|
|
16
39
|
self._logger.addHandler(console_handler)
|
|
17
40
|
|
|
18
41
|
def set_level(self, level: str) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
设置全局日志级别
|
|
44
|
+
|
|
45
|
+
:param level: 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
|
46
|
+
:return: bool 设置是否成功
|
|
47
|
+
"""
|
|
19
48
|
try:
|
|
20
49
|
level = level.upper()
|
|
21
50
|
if hasattr(logging, level):
|
|
@@ -27,6 +56,13 @@ class Logger:
|
|
|
27
56
|
return False
|
|
28
57
|
|
|
29
58
|
def set_module_level(self, module_name: str, level: str) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
设置指定模块日志级别
|
|
61
|
+
|
|
62
|
+
:param module_name: 模块名称
|
|
63
|
+
:param level: 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
|
|
64
|
+
:return: bool 设置是否成功
|
|
65
|
+
"""
|
|
30
66
|
from .env import env
|
|
31
67
|
if not env.get_module_status(module_name):
|
|
32
68
|
self._logger.warning(f"模块 {module_name} 未启用,无法设置日志等级。")
|
|
@@ -41,6 +77,12 @@ class Logger:
|
|
|
41
77
|
return False
|
|
42
78
|
|
|
43
79
|
def set_output_file(self, path) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
设置日志输出
|
|
82
|
+
|
|
83
|
+
:param path: 日志文件路径 Str/List
|
|
84
|
+
:return: bool 设置是否成功
|
|
85
|
+
"""
|
|
44
86
|
if self._file_handler:
|
|
45
87
|
self._logger.removeHandler(self._file_handler)
|
|
46
88
|
self._file_handler.close()
|
|
@@ -58,7 +100,14 @@ class Logger:
|
|
|
58
100
|
except Exception as e:
|
|
59
101
|
self._logger.error(f"无法设置日志文件 {p}: {e}")
|
|
60
102
|
return False
|
|
103
|
+
|
|
61
104
|
def save_logs(self, path) -> bool:
|
|
105
|
+
"""
|
|
106
|
+
保存所有在内存中记录的日志
|
|
107
|
+
|
|
108
|
+
:param path: 日志文件路径 Str/List
|
|
109
|
+
:return: bool 设置是否成功
|
|
110
|
+
"""
|
|
62
111
|
if self._logs == None:
|
|
63
112
|
self._logger.warning("没有log记录可供保存。")
|
|
64
113
|
return False
|
|
@@ -79,6 +128,12 @@ class Logger:
|
|
|
79
128
|
return False
|
|
80
129
|
|
|
81
130
|
def get_logs(self, module_name: str = None) -> dict:
|
|
131
|
+
"""
|
|
132
|
+
获取日志内容
|
|
133
|
+
|
|
134
|
+
:param module_name (可选): 模块名称
|
|
135
|
+
:return: dict 日志内容
|
|
136
|
+
"""
|
|
82
137
|
if module_name:
|
|
83
138
|
return {module_name: self._logs.get(module_name, [])}
|
|
84
139
|
return {k: v.copy() for k, v in self._logs.items()}
|
ErisPulse/__init__.py
CHANGED
|
@@ -13,11 +13,9 @@ ErisPulse SDK 主模块
|
|
|
13
13
|
import os
|
|
14
14
|
import sys
|
|
15
15
|
import importlib
|
|
16
|
-
import asyncio
|
|
17
|
-
import toml
|
|
18
16
|
import inspect
|
|
19
17
|
import importlib.metadata
|
|
20
|
-
from typing import Dict, List, Tuple,
|
|
18
|
+
from typing import Dict, List, Tuple, Type, Any
|
|
21
19
|
from pathlib import Path
|
|
22
20
|
|
|
23
21
|
# BaseModules: SDK核心模块
|
|
@@ -73,14 +71,14 @@ class LazyModule:
|
|
|
73
71
|
{!--< /tips >!--}
|
|
74
72
|
"""
|
|
75
73
|
|
|
76
|
-
def __init__(self, module_name: str, module_class: Type, sdk_ref: Any, module_info: Dict[str, Any]):
|
|
74
|
+
def __init__(self, module_name: str, module_class: Type, sdk_ref: Any, module_info: Dict[str, Any]) -> None:
|
|
77
75
|
"""
|
|
78
76
|
初始化懒加载包装器
|
|
79
77
|
|
|
80
|
-
:param module_name: 模块名称
|
|
81
|
-
:param module_class: 模块类
|
|
82
|
-
:param sdk_ref: SDK引用
|
|
83
|
-
:param module_info: 模块信息字典
|
|
78
|
+
:param module_name: str 模块名称
|
|
79
|
+
:param module_class: Type 模块类
|
|
80
|
+
:param sdk_ref: Any SDK引用
|
|
81
|
+
:param module_info: Dict[str, Any] 模块信息字典
|
|
84
82
|
"""
|
|
85
83
|
self._module_name = module_name
|
|
86
84
|
self._module_class = module_class
|
|
@@ -90,7 +88,11 @@ class LazyModule:
|
|
|
90
88
|
self._initialized = False
|
|
91
89
|
|
|
92
90
|
def _initialize(self):
|
|
93
|
-
"""
|
|
91
|
+
"""
|
|
92
|
+
实际初始化模块
|
|
93
|
+
|
|
94
|
+
:raises LazyLoadError: 当模块初始化失败时抛出
|
|
95
|
+
"""
|
|
94
96
|
try:
|
|
95
97
|
# 获取类的__init__参数信息
|
|
96
98
|
init_signature = inspect.signature(self._module_class.__init__)
|
|
@@ -110,16 +112,77 @@ class LazyModule:
|
|
|
110
112
|
raise raiserr.LazyLoadError(f"无法初始化模块 {self._module_name}: {e}")
|
|
111
113
|
|
|
112
114
|
def __getattr__(self, name: str) -> Any:
|
|
113
|
-
"""
|
|
115
|
+
"""
|
|
116
|
+
属性访问时触发初始化
|
|
117
|
+
|
|
118
|
+
:param name: str 要访问的属性名
|
|
119
|
+
:return: Any 模块属性值
|
|
120
|
+
"""
|
|
114
121
|
if not self._initialized:
|
|
115
122
|
self._initialize()
|
|
116
123
|
return getattr(self._instance, name)
|
|
117
124
|
|
|
118
125
|
def __call__(self, *args, **kwargs) -> Any:
|
|
119
|
-
"""
|
|
126
|
+
"""
|
|
127
|
+
调用时触发初始化
|
|
128
|
+
|
|
129
|
+
:param args: 位置参数
|
|
130
|
+
:param kwargs: 关键字参数
|
|
131
|
+
:return: Any 模块调用结果
|
|
132
|
+
"""
|
|
120
133
|
if not self._initialized:
|
|
121
134
|
self._initialize()
|
|
122
135
|
return self._instance(*args, **kwargs)
|
|
136
|
+
|
|
137
|
+
def __bool__(self) -> bool:
|
|
138
|
+
"""
|
|
139
|
+
判断模块布尔值时触发初始化
|
|
140
|
+
|
|
141
|
+
:return: bool 模块布尔值
|
|
142
|
+
"""
|
|
143
|
+
if not self._initialized:
|
|
144
|
+
self._initialize()
|
|
145
|
+
return bool(self._instance)
|
|
146
|
+
|
|
147
|
+
def __str__(self) -> str:
|
|
148
|
+
"""
|
|
149
|
+
转换为字符串时触发初始化
|
|
150
|
+
|
|
151
|
+
:return: str 模块字符串表示
|
|
152
|
+
"""
|
|
153
|
+
if not self._initialized:
|
|
154
|
+
self._initialize()
|
|
155
|
+
return str(self._instance)
|
|
156
|
+
return str(self._instance)
|
|
157
|
+
|
|
158
|
+
# 确保模块在被赋值给变量后仍然能正确工作
|
|
159
|
+
def __getattribute__(self, name: str) -> Any:
|
|
160
|
+
try:
|
|
161
|
+
# 首先尝试获取常规属性
|
|
162
|
+
return super().__getattribute__(name)
|
|
163
|
+
except AttributeError:
|
|
164
|
+
# 如果常规属性不存在,触发初始化
|
|
165
|
+
if name != '_initialized' and not self._initialized:
|
|
166
|
+
self._initialize()
|
|
167
|
+
return getattr(self._instance, name)
|
|
168
|
+
raise
|
|
169
|
+
|
|
170
|
+
def __copy__(self):
|
|
171
|
+
"""
|
|
172
|
+
浅拷贝时返回自身,保持懒加载特性
|
|
173
|
+
|
|
174
|
+
:return: self
|
|
175
|
+
"""
|
|
176
|
+
return self
|
|
177
|
+
|
|
178
|
+
def __deepcopy__(self, memo):
|
|
179
|
+
"""
|
|
180
|
+
深拷贝时返回自身,保持懒加载特性
|
|
181
|
+
|
|
182
|
+
:param memo: memo
|
|
183
|
+
:return: self
|
|
184
|
+
"""
|
|
185
|
+
return self
|
|
123
186
|
|
|
124
187
|
|
|
125
188
|
class AdapterLoader:
|
|
@@ -375,8 +438,8 @@ class ModuleLoader:
|
|
|
375
438
|
"""
|
|
376
439
|
检查模块是否应该懒加载
|
|
377
440
|
|
|
378
|
-
:param module_class: 模块类
|
|
379
|
-
:return: bool
|
|
441
|
+
:param module_class: Type 模块类
|
|
442
|
+
:return: bool 如果返回 False,则立即加载;否则懒加载
|
|
380
443
|
"""
|
|
381
444
|
# 检查模块是否定义了 should_eager_load() 方法
|
|
382
445
|
if hasattr(module_class, "should_eager_load"):
|
|
@@ -413,7 +476,8 @@ class ModuleInitializer:
|
|
|
413
476
|
4. 注册适配器
|
|
414
477
|
5. 初始化各模块
|
|
415
478
|
|
|
416
|
-
:return: bool
|
|
479
|
+
:return: bool 初始化是否成功
|
|
480
|
+
:raises InitError: 当初始化失败时抛出
|
|
417
481
|
"""
|
|
418
482
|
logger.info("[Init] SDK 正在初始化...")
|
|
419
483
|
|
|
@@ -433,16 +497,12 @@ class ModuleInitializer:
|
|
|
433
497
|
logger.warning("[Init] 没有找到可用的模块和适配器")
|
|
434
498
|
return True
|
|
435
499
|
|
|
436
|
-
# 3.
|
|
437
|
-
logger.debug("[Init] 正在预记录模块信息...")
|
|
438
|
-
ModuleInitializer._pre_register_modules(enabled_modules, module_objs)
|
|
439
|
-
|
|
440
|
-
# 4. 注册适配器
|
|
500
|
+
# 3. 注册适配器
|
|
441
501
|
logger.debug("[Init] 正在注册适配器...")
|
|
442
502
|
if not ModuleInitializer._register_adapters(enabled_adapters, adapter_objs):
|
|
443
503
|
return False
|
|
444
504
|
|
|
445
|
-
#
|
|
505
|
+
# 4. 初始化模块
|
|
446
506
|
logger.debug("[Init] 正在初始化模块...")
|
|
447
507
|
success = ModuleInitializer._initialize_modules(enabled_modules, module_objs)
|
|
448
508
|
logger.info(f"[Init] SDK初始化{'成功' if success else '失败'}")
|
|
@@ -454,20 +514,58 @@ class ModuleInitializer:
|
|
|
454
514
|
return False
|
|
455
515
|
|
|
456
516
|
@staticmethod
|
|
457
|
-
def
|
|
517
|
+
def _initialize_modules(modules: List[str], module_objs: Dict[str, Any]) -> None:
|
|
458
518
|
"""
|
|
459
|
-
|
|
519
|
+
{!--< internal-use >!--}
|
|
520
|
+
初始化模块
|
|
521
|
+
|
|
522
|
+
:param modules: List[str] 模块名称列表
|
|
523
|
+
:param module_objs: Dict[str, Any] 模块对象字典
|
|
460
524
|
|
|
461
|
-
|
|
525
|
+
:return: bool 模块初始化是否成功
|
|
462
526
|
"""
|
|
463
527
|
for module_name in modules:
|
|
464
528
|
module_obj = module_objs[module_name]
|
|
465
529
|
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
466
530
|
|
|
467
|
-
|
|
468
|
-
|
|
531
|
+
if hasattr(sdk, meta_name):
|
|
532
|
+
continue
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
entry_points = importlib.metadata.entry_points()
|
|
536
|
+
if hasattr(entry_points, 'select'):
|
|
537
|
+
module_entries = entry_points.select(group='erispulse.module')
|
|
538
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
539
|
+
else:
|
|
540
|
+
module_entries = entry_points.get('erispulse.module', [])
|
|
541
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
542
|
+
|
|
543
|
+
entry_point = module_entry_map.get(meta_name)
|
|
544
|
+
if entry_point:
|
|
545
|
+
module_class = entry_point.load()
|
|
546
|
+
lazy_load = ModuleLoader._should_lazy_load(module_class)
|
|
547
|
+
|
|
548
|
+
if lazy_load:
|
|
549
|
+
lazy_module = LazyModule(meta_name, module_class, sdk, module_obj.moduleInfo)
|
|
550
|
+
setattr(sdk, meta_name, lazy_module)
|
|
551
|
+
else:
|
|
552
|
+
init_signature = inspect.signature(module_class.__init__)
|
|
553
|
+
if 'sdk' in init_signature.parameters:
|
|
554
|
+
instance = module_class(sdk)
|
|
555
|
+
else:
|
|
556
|
+
instance = module_class()
|
|
557
|
+
|
|
558
|
+
setattr(instance, "moduleInfo", module_obj.moduleInfo)
|
|
559
|
+
setattr(sdk, meta_name, instance)
|
|
560
|
+
|
|
561
|
+
logger.debug(f"预注册模块: {meta_name}")
|
|
562
|
+
|
|
563
|
+
return True
|
|
564
|
+
|
|
565
|
+
except Exception as e:
|
|
566
|
+
logger.error(f"预注册模块 {meta_name} 失败: {e}")
|
|
469
567
|
setattr(sdk, meta_name, None)
|
|
470
|
-
|
|
568
|
+
return False
|
|
471
569
|
|
|
472
570
|
@staticmethod
|
|
473
571
|
def _register_adapters(adapters: List[str], adapter_objs: Dict[str, Any]) -> bool:
|
|
@@ -475,10 +573,10 @@ class ModuleInitializer:
|
|
|
475
573
|
{!--< internal-use >!--}
|
|
476
574
|
注册适配器
|
|
477
575
|
|
|
478
|
-
:param adapters: 适配器名称列表
|
|
479
|
-
:param adapter_objs: 适配器对象字典
|
|
576
|
+
:param adapters: List[str] 适配器名称列表
|
|
577
|
+
:param adapter_objs: Dict[str, Any] 适配器对象字典
|
|
480
578
|
|
|
481
|
-
:return: bool
|
|
579
|
+
:return: bool 适配器注册是否成功
|
|
482
580
|
"""
|
|
483
581
|
success = True
|
|
484
582
|
# 存储平台名到适配器类的映射
|
|
@@ -519,85 +617,6 @@ class ModuleInitializer:
|
|
|
519
617
|
success = False
|
|
520
618
|
return success
|
|
521
619
|
|
|
522
|
-
@staticmethod
|
|
523
|
-
def _initialize_modules(modules: List[str], module_objs: Dict[str, Any]) -> bool:
|
|
524
|
-
"""
|
|
525
|
-
{!--< internal-use >!--}
|
|
526
|
-
初始化模块
|
|
527
|
-
|
|
528
|
-
:param modules: 模块名称列表
|
|
529
|
-
:param module_objs: 模块对象字典
|
|
530
|
-
|
|
531
|
-
:return: bool: 模块初始化是否成功
|
|
532
|
-
"""
|
|
533
|
-
success = True
|
|
534
|
-
|
|
535
|
-
try:
|
|
536
|
-
entry_points = importlib.metadata.entry_points()
|
|
537
|
-
if hasattr(entry_points, 'select'):
|
|
538
|
-
module_entries = entry_points.select(group='erispulse.module')
|
|
539
|
-
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
540
|
-
else:
|
|
541
|
-
module_entries = entry_points.get('erispulse.module', [])
|
|
542
|
-
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
543
|
-
except Exception as e:
|
|
544
|
-
logger.error(f"获取 entry_points 失败: {e}")
|
|
545
|
-
return False
|
|
546
|
-
|
|
547
|
-
# 第一次遍历:创建所有模块的占位符
|
|
548
|
-
for module_name in modules:
|
|
549
|
-
module_obj = module_objs[module_name]
|
|
550
|
-
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
551
|
-
|
|
552
|
-
if not mods.get_module_status(meta_name):
|
|
553
|
-
continue
|
|
554
|
-
|
|
555
|
-
entry_point = module_entry_map.get(meta_name)
|
|
556
|
-
if not entry_point:
|
|
557
|
-
logger.error(f"找不到模块 {meta_name} 的 entry-point")
|
|
558
|
-
success = False
|
|
559
|
-
continue
|
|
560
|
-
|
|
561
|
-
# 创建模块占位符
|
|
562
|
-
if not hasattr(sdk, meta_name):
|
|
563
|
-
setattr(sdk, meta_name, None)
|
|
564
|
-
|
|
565
|
-
# 第二次遍历:实际初始化模块
|
|
566
|
-
for module_name in modules:
|
|
567
|
-
module_obj = module_objs[module_name]
|
|
568
|
-
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
569
|
-
|
|
570
|
-
try:
|
|
571
|
-
if not mods.get_module_status(meta_name):
|
|
572
|
-
continue
|
|
573
|
-
|
|
574
|
-
entry_point = module_entry_map.get(meta_name)
|
|
575
|
-
if not entry_point:
|
|
576
|
-
continue
|
|
577
|
-
|
|
578
|
-
module_class = entry_point.load()
|
|
579
|
-
lazy_load = ModuleLoader._should_lazy_load(module_class)
|
|
580
|
-
|
|
581
|
-
if lazy_load:
|
|
582
|
-
lazy_module = LazyModule(meta_name, module_class, sdk, module_obj.moduleInfo)
|
|
583
|
-
setattr(sdk, meta_name, lazy_module)
|
|
584
|
-
else:
|
|
585
|
-
init_signature = inspect.signature(module_class.__init__)
|
|
586
|
-
if 'sdk' in init_signature.parameters:
|
|
587
|
-
instance = module_class(sdk)
|
|
588
|
-
else:
|
|
589
|
-
instance = module_class()
|
|
590
|
-
|
|
591
|
-
setattr(instance, "moduleInfo", module_obj.moduleInfo)
|
|
592
|
-
setattr(sdk, meta_name, instance)
|
|
593
|
-
logger.debug(f"模块 {meta_name} 已初始化")
|
|
594
|
-
|
|
595
|
-
except Exception as e:
|
|
596
|
-
logger.error(f"初始化模块 {meta_name} 失败: {e}")
|
|
597
|
-
success = False
|
|
598
|
-
|
|
599
|
-
return success
|
|
600
|
-
|
|
601
620
|
def init_progress() -> bool:
|
|
602
621
|
"""
|
|
603
622
|
初始化项目环境文件
|
|
@@ -605,7 +624,7 @@ def init_progress() -> bool:
|
|
|
605
624
|
1. 检查并创建main.py入口文件
|
|
606
625
|
2. 确保基础目录结构存在
|
|
607
626
|
|
|
608
|
-
:return: bool
|
|
627
|
+
:return: bool 是否创建了新的main.py文件
|
|
609
628
|
|
|
610
629
|
{!--< tips >!--}
|
|
611
630
|
1. 如果main.py已存在则不会覆盖
|
|
@@ -658,7 +677,7 @@ def _prepare_environment() -> bool:
|
|
|
658
677
|
1. 初始化项目环境文件
|
|
659
678
|
2. 加载环境变量配置
|
|
660
679
|
|
|
661
|
-
:return: bool
|
|
680
|
+
:return: bool 环境准备是否成功
|
|
662
681
|
"""
|
|
663
682
|
logger.info("[Init] 准备初始化环境...")
|
|
664
683
|
try:
|
|
@@ -680,7 +699,7 @@ def init() -> bool:
|
|
|
680
699
|
1. 准备运行环境
|
|
681
700
|
2. 初始化所有模块和适配器
|
|
682
701
|
|
|
683
|
-
:return: bool
|
|
702
|
+
:return: bool SDK初始化是否成功
|
|
684
703
|
|
|
685
704
|
{!--< tips >!--}
|
|
686
705
|
1. 这是SDK的主要入口函数
|
|
@@ -700,8 +719,8 @@ def load_module(module_name: str) -> bool:
|
|
|
700
719
|
"""
|
|
701
720
|
手动加载指定模块
|
|
702
721
|
|
|
703
|
-
:param module_name: 要加载的模块名称
|
|
704
|
-
:return: bool
|
|
722
|
+
:param module_name: str 要加载的模块名称
|
|
723
|
+
:return: bool 加载是否成功
|
|
705
724
|
|
|
706
725
|
{!--< tips >!--}
|
|
707
726
|
1. 可用于手动触发懒加载模块的初始化
|
ErisPulse/__main__.py
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
## 主要命令
|
|
7
7
|
### 包管理:
|
|
8
|
-
search: 搜索PyPI上的ErisPulse模块
|
|
9
8
|
install: 安装模块/适配器包
|
|
10
9
|
uninstall: 卸载模块/适配器包
|
|
11
10
|
list: 列出已安装的模块/适配器
|
|
@@ -42,8 +41,18 @@ from watchdog.events import FileSystemEventHandler
|
|
|
42
41
|
from .Core.shellprint import shellprint, Shell_Printer
|
|
43
42
|
|
|
44
43
|
class PyPIManager:
|
|
45
|
-
"""
|
|
44
|
+
"""
|
|
45
|
+
PyPI包管理器
|
|
46
46
|
|
|
47
|
+
负责与PyPI交互,包括搜索、安装、卸载和升级ErisPulse模块/适配器
|
|
48
|
+
|
|
49
|
+
{!--< tips >!--}
|
|
50
|
+
1. 支持多个远程源作为备份
|
|
51
|
+
2. 自动区分模块和适配器
|
|
52
|
+
3. 提供详细的错误处理
|
|
53
|
+
{!--< /tips >!--}
|
|
54
|
+
"""
|
|
55
|
+
|
|
47
56
|
REMOTE_SOURCES = [
|
|
48
57
|
"https://erisdev.com/packages.json",
|
|
49
58
|
"https://raw.githubusercontent.com/ErisPulse/ErisPulse-ModuleRepo/main/packages.json"
|
|
@@ -51,6 +60,20 @@ class PyPIManager:
|
|
|
51
60
|
|
|
52
61
|
@staticmethod
|
|
53
62
|
async def get_remote_packages() -> dict:
|
|
63
|
+
"""
|
|
64
|
+
获取远程包列表
|
|
65
|
+
|
|
66
|
+
从配置的远程源获取所有可用的ErisPulse模块和适配器
|
|
67
|
+
|
|
68
|
+
:return:
|
|
69
|
+
Dict[str, Dict]: 包含模块和适配器的字典
|
|
70
|
+
- modules: 模块字典 {模块名: 模块信息}
|
|
71
|
+
- adapters: 适配器字典 {适配器名: 适配器信息}
|
|
72
|
+
|
|
73
|
+
:raises ClientError: 当网络请求失败时抛出
|
|
74
|
+
:raises asyncio.TimeoutError: 当请求超时时抛出
|
|
75
|
+
"""
|
|
76
|
+
|
|
54
77
|
import aiohttp
|
|
55
78
|
from aiohttp import ClientError, ClientTimeout
|
|
56
79
|
|
|
@@ -86,30 +109,16 @@ class PyPIManager:
|
|
|
86
109
|
shellprint.panel(f"获取远程模块列表失败: {last_error}", "错误", "error")
|
|
87
110
|
return {"modules": {}, "adapters": {}}
|
|
88
111
|
|
|
89
|
-
@staticmethod
|
|
90
|
-
def search_packages(query: str) -> List[Dict[str, str]]:
|
|
91
|
-
try:
|
|
92
|
-
result = subprocess.run(
|
|
93
|
-
[sys.executable, "-m", "pip", "search", query],
|
|
94
|
-
capture_output=True,
|
|
95
|
-
text=True
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
packages = []
|
|
99
|
-
for line in result.stdout.split('\n'):
|
|
100
|
-
if "ErisPulse-" in line:
|
|
101
|
-
parts = line.split(' ', 1)
|
|
102
|
-
if len(parts) >= 1:
|
|
103
|
-
name = parts[0].strip()
|
|
104
|
-
desc = parts[1].strip() if len(parts) > 1 else ""
|
|
105
|
-
packages.append({"name": name, "description": desc})
|
|
106
|
-
return packages
|
|
107
|
-
except Exception as e:
|
|
108
|
-
shellprint.panel(f"搜索PyPI包失败: {e}", "错误", "error")
|
|
109
|
-
return []
|
|
110
|
-
|
|
111
112
|
@staticmethod
|
|
112
113
|
def get_installed_packages() -> Dict[str, Dict[str, str]]:
|
|
114
|
+
"""
|
|
115
|
+
获取已安装的包信息
|
|
116
|
+
|
|
117
|
+
:return:
|
|
118
|
+
Dict[str, Dict[str, Dict[str, str]]]: 已安装包字典
|
|
119
|
+
- modules: 已安装模块 {模块名: 模块信息}
|
|
120
|
+
- adapters: 已安装适配器 {适配器名: 适配器信息}
|
|
121
|
+
"""
|
|
113
122
|
packages = {
|
|
114
123
|
"modules": {},
|
|
115
124
|
"adapters": {}
|
|
@@ -140,6 +149,13 @@ class PyPIManager:
|
|
|
140
149
|
|
|
141
150
|
@staticmethod
|
|
142
151
|
def install_package(package_name: str, upgrade: bool = False) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
安装指定包
|
|
154
|
+
|
|
155
|
+
:param package_name: str 要安装的包名
|
|
156
|
+
:param upgrade: bool 是否升级已安装的包 (默认: False)
|
|
157
|
+
:return: bool 安装是否成功
|
|
158
|
+
"""
|
|
143
159
|
try:
|
|
144
160
|
cmd = [sys.executable, "-m", "pip", "install"]
|
|
145
161
|
if upgrade:
|
|
@@ -158,6 +174,12 @@ class PyPIManager:
|
|
|
158
174
|
|
|
159
175
|
@staticmethod
|
|
160
176
|
def uninstall_package(package_name: str) -> bool:
|
|
177
|
+
"""
|
|
178
|
+
卸载指定包
|
|
179
|
+
|
|
180
|
+
:param package_name: str 要卸载的包名
|
|
181
|
+
:return: bool 卸载是否成功
|
|
182
|
+
"""
|
|
161
183
|
try:
|
|
162
184
|
shellprint.status(f"正在卸载 {package_name}...")
|
|
163
185
|
result = subprocess.run(
|
|
@@ -174,6 +196,16 @@ class PyPIManager:
|
|
|
174
196
|
|
|
175
197
|
@staticmethod
|
|
176
198
|
def upgrade_all() -> bool:
|
|
199
|
+
"""
|
|
200
|
+
升级所有已安装的ErisPulse包
|
|
201
|
+
|
|
202
|
+
:return: bool 升级是否成功
|
|
203
|
+
|
|
204
|
+
{!--< tips >!--}
|
|
205
|
+
1. 会先列出所有可升级的包
|
|
206
|
+
2. 需要用户确认才会执行升级
|
|
207
|
+
{!--< /tips >!--}
|
|
208
|
+
"""
|
|
177
209
|
try:
|
|
178
210
|
installed = PyPIManager.get_installed_packages()
|
|
179
211
|
all_packages = set()
|
|
@@ -205,6 +237,17 @@ class PyPIManager:
|
|
|
205
237
|
return False
|
|
206
238
|
|
|
207
239
|
class ReloadHandler(FileSystemEventHandler):
|
|
240
|
+
"""
|
|
241
|
+
热重载处理器
|
|
242
|
+
|
|
243
|
+
监控文件变化并自动重启脚本
|
|
244
|
+
|
|
245
|
+
{!--< tips >!--}
|
|
246
|
+
1. 基于watchdog实现文件监控
|
|
247
|
+
2. 有1秒的防抖延迟
|
|
248
|
+
3. 会终止旧进程并启动新进程
|
|
249
|
+
{!--< /tips >!--}
|
|
250
|
+
"""
|
|
208
251
|
def __init__(self, script_path, *args, **kwargs):
|
|
209
252
|
super().__init__(*args, **kwargs)
|
|
210
253
|
self.script_path = script_path
|
|
@@ -213,6 +256,11 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
213
256
|
self.start_process()
|
|
214
257
|
|
|
215
258
|
def start_process(self):
|
|
259
|
+
"""
|
|
260
|
+
启动/重启被监控的进程
|
|
261
|
+
|
|
262
|
+
{!--< internal-use >!--}
|
|
263
|
+
"""
|
|
216
264
|
if self.process:
|
|
217
265
|
self.process.terminate()
|
|
218
266
|
self.process.wait()
|
|
@@ -222,6 +270,11 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
222
270
|
self.last_reload = time.time()
|
|
223
271
|
|
|
224
272
|
def on_modified(self, event):
|
|
273
|
+
"""
|
|
274
|
+
文件修改事件处理
|
|
275
|
+
|
|
276
|
+
:param event: FileSystemEvent 文件系统事件对象
|
|
277
|
+
"""
|
|
225
278
|
now = time.time()
|
|
226
279
|
if now - self.last_reload < 1.0:
|
|
227
280
|
return
|
|
@@ -231,6 +284,16 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
231
284
|
self.start_process()
|
|
232
285
|
|
|
233
286
|
def start_reloader(script_path):
|
|
287
|
+
"""
|
|
288
|
+
启动热重载监控
|
|
289
|
+
|
|
290
|
+
:param script_path: str 要监控的脚本路径
|
|
291
|
+
|
|
292
|
+
{!--< tips >!--}
|
|
293
|
+
1. 监控脚本所在目录和modules目录
|
|
294
|
+
2. 按Ctrl+C可停止监控
|
|
295
|
+
{!--< /tips >!--}
|
|
296
|
+
"""
|
|
234
297
|
project_root = os.path.dirname(os.path.abspath(__file__))
|
|
235
298
|
watch_dirs = [
|
|
236
299
|
os.path.dirname(os.path.abspath(script_path)),
|
|
@@ -257,6 +320,14 @@ def start_reloader(script_path):
|
|
|
257
320
|
observer.join()
|
|
258
321
|
|
|
259
322
|
def run_script(script_path: str, reload: bool = False):
|
|
323
|
+
"""
|
|
324
|
+
运行指定脚本
|
|
325
|
+
|
|
326
|
+
:param script_path: str 要运行的脚本路径
|
|
327
|
+
:param reload: bool 是否启用热重载 (默认: False)
|
|
328
|
+
|
|
329
|
+
:raises FileNotFoundError: 当脚本不存在时抛出
|
|
330
|
+
"""
|
|
260
331
|
if not os.path.exists(script_path):
|
|
261
332
|
shellprint.panel(f"找不到指定文件: {script_path}", "错误", "error")
|
|
262
333
|
return
|
|
@@ -270,13 +341,31 @@ def run_script(script_path: str, reload: bool = False):
|
|
|
270
341
|
runpy.run_path(script_path, run_name="__main__")
|
|
271
342
|
except KeyboardInterrupt:
|
|
272
343
|
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
344
|
+
|
|
273
345
|
def get_erispulse_version():
|
|
346
|
+
"""
|
|
347
|
+
获取当前安装的ErisPulse版本
|
|
348
|
+
|
|
349
|
+
:return: str ErisPulse版本号或"unknown version"
|
|
350
|
+
"""
|
|
274
351
|
try:
|
|
275
352
|
return version("ErisPulse")
|
|
276
353
|
except PackageNotFoundError:
|
|
277
354
|
return "unknown version"
|
|
278
355
|
|
|
279
356
|
def main():
|
|
357
|
+
"""
|
|
358
|
+
CLI主入口
|
|
359
|
+
|
|
360
|
+
解析命令行参数并执行相应命令
|
|
361
|
+
|
|
362
|
+
{!--< tips >!--}
|
|
363
|
+
1. 使用argparse处理命令行参数
|
|
364
|
+
2. 支持彩色输出和表格显示
|
|
365
|
+
3. 提供详细的错误处理
|
|
366
|
+
{!--< /tips >!--}
|
|
367
|
+
"""
|
|
368
|
+
|
|
280
369
|
parser = argparse.ArgumentParser(
|
|
281
370
|
prog="epsdk",
|
|
282
371
|
formatter_class=argparse.RawTextHelpFormatter,
|
|
@@ -293,11 +382,7 @@ def main():
|
|
|
293
382
|
metavar=f"{Shell_Printer.GREEN}<命令>{Shell_Printer.RESET}",
|
|
294
383
|
help='具体命令的帮助信息'
|
|
295
384
|
)
|
|
296
|
-
|
|
297
|
-
# 搜索命令
|
|
298
|
-
search_parser = subparsers.add_parser('search', help='搜索PyPI上的ErisPulse模块')
|
|
299
|
-
search_parser.add_argument('query', type=str, help='搜索关键词')
|
|
300
|
-
|
|
385
|
+
|
|
301
386
|
# 安装命令
|
|
302
387
|
install_parser = subparsers.add_parser('install', help='安装模块/适配器包')
|
|
303
388
|
install_parser.add_argument('package', type=str, help='要安装的包名')
|
|
@@ -336,20 +421,7 @@ def main():
|
|
|
336
421
|
return
|
|
337
422
|
|
|
338
423
|
try:
|
|
339
|
-
if args.command == "
|
|
340
|
-
packages = PyPIManager.search_packages(args.query)
|
|
341
|
-
if packages:
|
|
342
|
-
rows = [
|
|
343
|
-
[
|
|
344
|
-
f"{Shell_Printer.BLUE}{pkg['name']}{Shell_Printer.RESET}",
|
|
345
|
-
pkg['description']
|
|
346
|
-
] for pkg in packages
|
|
347
|
-
]
|
|
348
|
-
shellprint.table(["包名", "描述"], rows, "搜索结果", "info")
|
|
349
|
-
else:
|
|
350
|
-
shellprint.panel(f"未找到匹配 '{args.query}' 的ErisPulse包", "提示", "info")
|
|
351
|
-
|
|
352
|
-
elif args.command == "install":
|
|
424
|
+
if args.command == "install":
|
|
353
425
|
import asyncio
|
|
354
426
|
# 首先检查是否是远程模块/适配器的简称
|
|
355
427
|
remote_packages = asyncio.run(PyPIManager.get_remote_packages())
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=
|
|
2
|
-
ErisPulse/__main__.py,sha256=
|
|
1
|
+
ErisPulse/__init__.py,sha256=JLcJpPdgwBzPVEurGM6hLGxok4wajh9PF8wp6VAeKGo,26329
|
|
2
|
+
ErisPulse/__main__.py,sha256=yxlB-rCQKy7D5XMWqP5id1sOQbzLuPx048NNpLaCDf8,19851
|
|
3
3
|
ErisPulse/Core/__init__.py,sha256=CIxWFdB6-0D8YJz7IdW052A4YllDsklEONCRj7mOpkQ,362
|
|
4
|
-
ErisPulse/Core/adapter.py,sha256=
|
|
4
|
+
ErisPulse/Core/adapter.py,sha256=2tHaN2wvPeOHN4o3BroHqeA0T-u097-HFlY8qxHvyLs,17943
|
|
5
5
|
ErisPulse/Core/env.py,sha256=9WYNadD9h2jP_2wxOVBJEhH1uDzbctW7eB4Ba9RSjA4,20409
|
|
6
|
-
ErisPulse/Core/logger.py,sha256=
|
|
6
|
+
ErisPulse/Core/logger.py,sha256=40vDe_D3L6ople-RZX8eGXntrZpFGHHzSl3pYxK-FLI,7196
|
|
7
7
|
ErisPulse/Core/mods.py,sha256=5SPutuzbMrA-VZwiXeNxYWfrdbpLRdYfQ0RvEkFuQgg,7308
|
|
8
8
|
ErisPulse/Core/raiserr.py,sha256=QLQ3r7p4iFP86XBLq9mtf1wv1xSlgny35i8t5-l4DXo,4620
|
|
9
9
|
ErisPulse/Core/server.py,sha256=H8dUUj8mxBLEd3ick7btbWEE_m58Y6277Zo5FY7Ild4,9217
|
|
10
10
|
ErisPulse/Core/shellprint.py,sha256=-BFoyFho_D3XEhxIoKt6x5gO4C62LKwmJWKDUGiPjNY,5908
|
|
11
11
|
ErisPulse/Core/util.py,sha256=kyydBAJHHG9I7rMRzKWtLAQMZoJyBqHiBAweqcraFkU,4001
|
|
12
|
-
erispulse-2.1.
|
|
13
|
-
erispulse-2.1.
|
|
14
|
-
erispulse-2.1.
|
|
15
|
-
erispulse-2.1.
|
|
16
|
-
erispulse-2.1.
|
|
12
|
+
erispulse-2.1.1.dist-info/METADATA,sha256=QYngGAOCd2sYXMr-Ezfeift5FyZPqBEWKBI8Kf76S_0,6161
|
|
13
|
+
erispulse-2.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
erispulse-2.1.1.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
|
|
15
|
+
erispulse-2.1.1.dist-info/licenses/LICENSE,sha256=lBYj7Nk4urLvByj4HvQFxu8j9hThhFF6OGfyxAZBP9Q,1451
|
|
16
|
+
erispulse-2.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|