ErisPulse 2.1.0__py3-none-any.whl → 2.1.2__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/__init__.py +14 -0
- ErisPulse/Core/adapter.py +32 -103
- ErisPulse/Core/logger.py +55 -0
- ErisPulse/Core/server.py +1 -1
- ErisPulse/__init__.py +157 -114
- ErisPulse/__main__.py +115 -43
- {erispulse-2.1.0.dist-info → erispulse-2.1.2.dist-info}/METADATA +1 -1
- erispulse-2.1.2.dist-info/RECORD +16 -0
- erispulse-2.1.0.dist-info/RECORD +0 -16
- {erispulse-2.1.0.dist-info → erispulse-2.1.2.dist-info}/WHEEL +0 -0
- {erispulse-2.1.0.dist-info → erispulse-2.1.2.dist-info}/entry_points.txt +0 -0
- {erispulse-2.1.0.dist-info → erispulse-2.1.2.dist-info}/licenses/LICENSE +0 -0
ErisPulse/Core/__init__.py
CHANGED
|
@@ -17,3 +17,17 @@ __all__ = [
|
|
|
17
17
|
'util',
|
|
18
18
|
'adapter_server'
|
|
19
19
|
]
|
|
20
|
+
|
|
21
|
+
_config = env.getConfig("ErisPulse")
|
|
22
|
+
|
|
23
|
+
if _config is None:
|
|
24
|
+
defaultConfig = {
|
|
25
|
+
"server": {
|
|
26
|
+
"host": "0.0.0.0",
|
|
27
|
+
"port": 8000,
|
|
28
|
+
"ssl_certfile": None,
|
|
29
|
+
"ssl_keyfile": None
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
env.setConfig("ErisPulse", defaultConfig)
|
|
33
|
+
_config = defaultConfig
|
ErisPulse/Core/adapter.py
CHANGED
|
@@ -34,20 +34,22 @@ class SendDSLBase:
|
|
|
34
34
|
{!--< /tips >!--}
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
def __init__(self, adapter: 'BaseAdapter', target_type: Optional[str] = None, target_id: Optional[str] = None):
|
|
37
|
+
def __init__(self, adapter: 'BaseAdapter', target_type: Optional[str] = None, target_id: Optional[str] = None, account_id: Optional[str] = None):
|
|
38
38
|
"""
|
|
39
39
|
初始化DSL发送器
|
|
40
40
|
|
|
41
41
|
:param adapter: 所属适配器实例
|
|
42
42
|
:param target_type: 目标类型(可选)
|
|
43
43
|
:param target_id: 目标ID(可选)
|
|
44
|
+
:param _account_id: 发送账号(可选)
|
|
44
45
|
"""
|
|
45
46
|
self._adapter = adapter
|
|
46
47
|
self._target_type = target_type
|
|
47
48
|
self._target_id = target_id
|
|
48
49
|
self._target_to = target_id
|
|
50
|
+
self._account = account_id
|
|
49
51
|
|
|
50
|
-
def To(self, target_type: str = None, target_id: str = None) -> 'SendDSL':
|
|
52
|
+
def To(self, target_type: str = None, target_id: Union[str, int] = None) -> 'SendDSL':
|
|
51
53
|
"""
|
|
52
54
|
设置消息目标
|
|
53
55
|
|
|
@@ -63,7 +65,20 @@ class SendDSLBase:
|
|
|
63
65
|
target_id = target_type
|
|
64
66
|
target_type = None
|
|
65
67
|
|
|
66
|
-
return self.__class__(self._adapter, target_type, target_id)
|
|
68
|
+
return self.__class__(self._adapter, target_type, target_id, self._account_id)
|
|
69
|
+
|
|
70
|
+
def Using(self, account_id: Union[str, int]) -> 'SendDSL':
|
|
71
|
+
"""
|
|
72
|
+
设置发送账号
|
|
73
|
+
|
|
74
|
+
:param _account_id: 发送账号
|
|
75
|
+
:return: SendDSL实例
|
|
76
|
+
|
|
77
|
+
:example:
|
|
78
|
+
>>> adapter.Send.Using("bot1").To("123").Text("Hello")
|
|
79
|
+
>>> adapter.Send.To("123").Using("bot1").Text("Hello") # 支持乱序
|
|
80
|
+
"""
|
|
81
|
+
return self.__class__(self._adapter, self._target_type, self._target_id, account_id)
|
|
67
82
|
|
|
68
83
|
|
|
69
84
|
class BaseAdapter:
|
|
@@ -90,56 +105,40 @@ class BaseAdapter:
|
|
|
90
105
|
{!--< /tips >!--}
|
|
91
106
|
"""
|
|
92
107
|
|
|
93
|
-
def
|
|
108
|
+
def Example(self, text: str) -> Awaitable[Any]:
|
|
94
109
|
"""
|
|
95
|
-
|
|
110
|
+
示例消息发送方法
|
|
96
111
|
|
|
97
112
|
:param text: 文本内容
|
|
98
113
|
:return: 异步任务
|
|
99
|
-
|
|
100
114
|
:example:
|
|
101
|
-
>>> await adapter.Send.To("123").
|
|
115
|
+
>>> await adapter.Send.To("123").Example("Hello")
|
|
102
116
|
"""
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
content=text,
|
|
107
|
-
recvId=self._target_id,
|
|
108
|
-
recvType=self._target_type
|
|
109
|
-
)
|
|
110
|
-
)
|
|
117
|
+
from .logger import logger
|
|
118
|
+
logger.debug(f"适配器 {self._adapter.__class__.__name__} 发送了实例类型的消息: {text}")
|
|
119
|
+
|
|
111
120
|
|
|
112
121
|
def __init__(self):
|
|
113
122
|
"""
|
|
114
123
|
初始化适配器
|
|
115
124
|
"""
|
|
116
|
-
self._handlers = defaultdict(list)
|
|
117
|
-
self._onebot_handlers = defaultdict(list) # OneBot12事件处理器
|
|
125
|
+
self._handlers = defaultdict(list)
|
|
118
126
|
self._middlewares = []
|
|
119
127
|
self.Send = self.__class__.Send(self)
|
|
120
128
|
|
|
121
|
-
def on(self, event_type: str = "*"
|
|
129
|
+
def on(self, event_type: str = "*") -> Callable[[Callable], Callable]:
|
|
122
130
|
"""
|
|
123
131
|
适配器事件监听装饰器
|
|
124
132
|
|
|
125
133
|
:param event_type: 事件类型
|
|
126
|
-
:param onebot12: 是否监听OneBot12协议事件
|
|
127
134
|
:return: 装饰器函数
|
|
128
|
-
|
|
129
|
-
:example:
|
|
130
|
-
>>> @adapter.on("message")
|
|
131
|
-
>>> async def handle_message(data):
|
|
132
|
-
>>> print(f"收到消息: {data}")
|
|
133
135
|
"""
|
|
134
136
|
def decorator(func: Callable) -> Callable:
|
|
135
137
|
@functools.wraps(func)
|
|
136
138
|
async def wrapper(*args, **kwargs):
|
|
137
139
|
return await func(*args, **kwargs)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
self._onebot_handlers[event_type].append(wrapper)
|
|
141
|
-
else:
|
|
142
|
-
self._handlers[event_type].append(wrapper)
|
|
140
|
+
self._handlers[event_type].append(wrapper)
|
|
141
|
+
|
|
143
142
|
return wrapper
|
|
144
143
|
return decorator
|
|
145
144
|
|
|
@@ -166,7 +165,6 @@ class BaseAdapter:
|
|
|
166
165
|
:param endpoint: API端点
|
|
167
166
|
:param params: API参数
|
|
168
167
|
:return: API调用结果
|
|
169
|
-
|
|
170
168
|
:raises NotImplementedError: 必须由子类实现
|
|
171
169
|
"""
|
|
172
170
|
raise NotImplementedError("适配器必须实现call_api方法")
|
|
@@ -186,36 +184,6 @@ class BaseAdapter:
|
|
|
186
184
|
:raises NotImplementedError: 必须由子类实现
|
|
187
185
|
"""
|
|
188
186
|
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
187
|
|
|
220
188
|
async def emit(self, event_type: str, data: Any) -> None:
|
|
221
189
|
"""
|
|
@@ -240,35 +208,6 @@ class BaseAdapter:
|
|
|
240
208
|
for handler in self._handlers.get("*", []):
|
|
241
209
|
await handler(data)
|
|
242
210
|
|
|
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
211
|
async def send(self, target_type: str, target_id: str, message: Any, **kwargs: Any) -> Any:
|
|
273
212
|
"""
|
|
274
213
|
发送消息的便捷方法
|
|
@@ -467,18 +406,8 @@ class AdapterManager:
|
|
|
467
406
|
from .logger import logger
|
|
468
407
|
from .server import adapter_server
|
|
469
408
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if server_config is None:
|
|
473
|
-
server_config = {
|
|
474
|
-
"host": "0.0.0.0",
|
|
475
|
-
"port": 8000,
|
|
476
|
-
"ssl_certfile": None,
|
|
477
|
-
"ssl_keyfile": None
|
|
478
|
-
}
|
|
479
|
-
env.setConfig("Server", server_config)
|
|
480
|
-
logger.info("已创建服务器配置")
|
|
481
|
-
|
|
409
|
+
from . import _config
|
|
410
|
+
server_config = _config.get("server", {})
|
|
482
411
|
host = server_config["host"]
|
|
483
412
|
port = server_config["port"]
|
|
484
413
|
ssl_cert = server_config.get("ssl_certfile", None)
|
|
@@ -565,7 +494,7 @@ class AdapterManager:
|
|
|
565
494
|
await adapter.shutdown()
|
|
566
495
|
|
|
567
496
|
from .server import adapter_server
|
|
568
|
-
adapter_server.stop()
|
|
497
|
+
await adapter_server.stop()
|
|
569
498
|
|
|
570
499
|
def get(self, platform: str) -> Optional[BaseAdapter]:
|
|
571
500
|
"""
|
|
@@ -616,4 +545,4 @@ class AdapterManager:
|
|
|
616
545
|
|
|
617
546
|
AdapterFather = BaseAdapter
|
|
618
547
|
adapter = AdapterManager()
|
|
619
|
-
SendDSL = SendDSLBase
|
|
548
|
+
SendDSL = SendDSLBase
|
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/Core/server.py
CHANGED
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
|
|
|
@@ -426,23 +490,20 @@ class ModuleInitializer:
|
|
|
426
490
|
module_objs, enabled_modules, disabled_modules = ModuleLoader.load()
|
|
427
491
|
logger.info(f"[Init] 加载了 {len(enabled_modules)} 个模块, {len(disabled_modules)} 个模块被禁用")
|
|
428
492
|
|
|
429
|
-
|
|
493
|
+
modules_dir = os.path.join(os.path.dirname(__file__), "modules")
|
|
494
|
+
if os.path.exists(modules_dir) and os.listdir(modules_dir):
|
|
430
495
|
logger.warning("[Warning] 你的项目使用了已经弃用的模块加载方式, 请尽快使用 PyPI 模块加载方式代替")
|
|
431
496
|
|
|
432
497
|
if not enabled_modules and not enabled_adapters:
|
|
433
498
|
logger.warning("[Init] 没有找到可用的模块和适配器")
|
|
434
499
|
return True
|
|
435
500
|
|
|
436
|
-
# 3.
|
|
437
|
-
logger.debug("[Init] 正在预记录模块信息...")
|
|
438
|
-
ModuleInitializer._pre_register_modules(enabled_modules, module_objs)
|
|
439
|
-
|
|
440
|
-
# 4. 注册适配器
|
|
501
|
+
# 3. 注册适配器
|
|
441
502
|
logger.debug("[Init] 正在注册适配器...")
|
|
442
503
|
if not ModuleInitializer._register_adapters(enabled_adapters, adapter_objs):
|
|
443
504
|
return False
|
|
444
505
|
|
|
445
|
-
#
|
|
506
|
+
# 4. 初始化模块
|
|
446
507
|
logger.debug("[Init] 正在初始化模块...")
|
|
447
508
|
success = ModuleInitializer._initialize_modules(enabled_modules, module_objs)
|
|
448
509
|
logger.info(f"[Init] SDK初始化{'成功' if success else '失败'}")
|
|
@@ -454,20 +515,81 @@ class ModuleInitializer:
|
|
|
454
515
|
return False
|
|
455
516
|
|
|
456
517
|
@staticmethod
|
|
457
|
-
def
|
|
518
|
+
def _initialize_modules(modules: List[str], module_objs: Dict[str, Any]) -> bool:
|
|
458
519
|
"""
|
|
459
|
-
|
|
520
|
+
{!--< internal-use >!--}
|
|
521
|
+
初始化模块
|
|
522
|
+
|
|
523
|
+
:param modules: List[str] 模块名称列表
|
|
524
|
+
:param module_objs: Dict[str, Any] 模块对象字典
|
|
460
525
|
|
|
461
|
-
|
|
526
|
+
:return: bool 模块初始化是否成功
|
|
462
527
|
"""
|
|
528
|
+
# 第一阶段:将所有模块挂载到LazyModule代理上
|
|
463
529
|
for module_name in modules:
|
|
464
530
|
module_obj = module_objs[module_name]
|
|
465
531
|
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
466
532
|
|
|
467
|
-
|
|
468
|
-
|
|
533
|
+
if hasattr(sdk, meta_name):
|
|
534
|
+
continue
|
|
535
|
+
|
|
536
|
+
try:
|
|
537
|
+
entry_points = importlib.metadata.entry_points()
|
|
538
|
+
if hasattr(entry_points, 'select'):
|
|
539
|
+
module_entries = entry_points.select(group='erispulse.module')
|
|
540
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
541
|
+
else:
|
|
542
|
+
module_entries = entry_points.get('erispulse.module', [])
|
|
543
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
544
|
+
|
|
545
|
+
entry_point = module_entry_map.get(meta_name)
|
|
546
|
+
if entry_point:
|
|
547
|
+
module_class = entry_point.load()
|
|
548
|
+
|
|
549
|
+
# 创建LazyModule代理
|
|
550
|
+
lazy_module = LazyModule(meta_name, module_class, sdk, module_obj.moduleInfo)
|
|
551
|
+
setattr(sdk, meta_name, lazy_module)
|
|
552
|
+
|
|
553
|
+
logger.debug(f"预注册模块: {meta_name}")
|
|
554
|
+
|
|
555
|
+
except Exception as e:
|
|
556
|
+
logger.error(f"预注册模块 {meta_name} 失败: {e}")
|
|
469
557
|
setattr(sdk, meta_name, None)
|
|
470
|
-
|
|
558
|
+
return False
|
|
559
|
+
|
|
560
|
+
# 第二阶段:检查并初始化需要立即加载的模块
|
|
561
|
+
for module_name in modules:
|
|
562
|
+
module_obj = module_objs[module_name]
|
|
563
|
+
meta_name = module_obj.moduleInfo["meta"]["name"]
|
|
564
|
+
|
|
565
|
+
if not hasattr(sdk, meta_name):
|
|
566
|
+
continue
|
|
567
|
+
|
|
568
|
+
try:
|
|
569
|
+
entry_points = importlib.metadata.entry_points()
|
|
570
|
+
if hasattr(entry_points, 'select'):
|
|
571
|
+
module_entries = entry_points.select(group='erispulse.module')
|
|
572
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
573
|
+
else:
|
|
574
|
+
module_entries = entry_points.get('erispulse.module', [])
|
|
575
|
+
module_entry_map = {entry.name: entry for entry in module_entries}
|
|
576
|
+
|
|
577
|
+
entry_point = module_entry_map.get(meta_name)
|
|
578
|
+
if entry_point:
|
|
579
|
+
module_class = entry_point.load()
|
|
580
|
+
|
|
581
|
+
# 检查是否需要立即加载
|
|
582
|
+
lazy_load = ModuleLoader._should_lazy_load(module_class)
|
|
583
|
+
if not lazy_load:
|
|
584
|
+
# 触发LazyModule的初始化
|
|
585
|
+
getattr(sdk, meta_name)._initialize()
|
|
586
|
+
logger.debug(f"立即初始化模块: {meta_name}")
|
|
587
|
+
|
|
588
|
+
except Exception as e:
|
|
589
|
+
logger.error(f"初始化模块 {meta_name} 失败: {e}")
|
|
590
|
+
return False
|
|
591
|
+
|
|
592
|
+
return True
|
|
471
593
|
|
|
472
594
|
@staticmethod
|
|
473
595
|
def _register_adapters(adapters: List[str], adapter_objs: Dict[str, Any]) -> bool:
|
|
@@ -475,10 +597,10 @@ class ModuleInitializer:
|
|
|
475
597
|
{!--< internal-use >!--}
|
|
476
598
|
注册适配器
|
|
477
599
|
|
|
478
|
-
:param adapters: 适配器名称列表
|
|
479
|
-
:param adapter_objs: 适配器对象字典
|
|
600
|
+
:param adapters: List[str] 适配器名称列表
|
|
601
|
+
:param adapter_objs: Dict[str, Any] 适配器对象字典
|
|
480
602
|
|
|
481
|
-
:return: bool
|
|
603
|
+
:return: bool 适配器注册是否成功
|
|
482
604
|
"""
|
|
483
605
|
success = True
|
|
484
606
|
# 存储平台名到适配器类的映射
|
|
@@ -519,85 +641,6 @@ class ModuleInitializer:
|
|
|
519
641
|
success = False
|
|
520
642
|
return success
|
|
521
643
|
|
|
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
644
|
def init_progress() -> bool:
|
|
602
645
|
"""
|
|
603
646
|
初始化项目环境文件
|
|
@@ -605,7 +648,7 @@ def init_progress() -> bool:
|
|
|
605
648
|
1. 检查并创建main.py入口文件
|
|
606
649
|
2. 确保基础目录结构存在
|
|
607
650
|
|
|
608
|
-
:return: bool
|
|
651
|
+
:return: bool 是否创建了新的main.py文件
|
|
609
652
|
|
|
610
653
|
{!--< tips >!--}
|
|
611
654
|
1. 如果main.py已存在则不会覆盖
|
|
@@ -658,7 +701,7 @@ def _prepare_environment() -> bool:
|
|
|
658
701
|
1. 初始化项目环境文件
|
|
659
702
|
2. 加载环境变量配置
|
|
660
703
|
|
|
661
|
-
:return: bool
|
|
704
|
+
:return: bool 环境准备是否成功
|
|
662
705
|
"""
|
|
663
706
|
logger.info("[Init] 准备初始化环境...")
|
|
664
707
|
try:
|
|
@@ -680,7 +723,7 @@ def init() -> bool:
|
|
|
680
723
|
1. 准备运行环境
|
|
681
724
|
2. 初始化所有模块和适配器
|
|
682
725
|
|
|
683
|
-
:return: bool
|
|
726
|
+
:return: bool SDK初始化是否成功
|
|
684
727
|
|
|
685
728
|
{!--< tips >!--}
|
|
686
729
|
1. 这是SDK的主要入口函数
|
|
@@ -700,8 +743,8 @@ def load_module(module_name: str) -> bool:
|
|
|
700
743
|
"""
|
|
701
744
|
手动加载指定模块
|
|
702
745
|
|
|
703
|
-
:param module_name: 要加载的模块名称
|
|
704
|
-
:return: bool
|
|
746
|
+
:param module_name: str 要加载的模块名称
|
|
747
|
+
:return: bool 加载是否成功
|
|
705
748
|
|
|
706
749
|
{!--< tips >!--}
|
|
707
750
|
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())
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=kQr2n1oeThlJVy7NY_ZMXgDsgZuhsBC5V6ysl2I59PQ,27471
|
|
2
|
+
ErisPulse/__main__.py,sha256=yxlB-rCQKy7D5XMWqP5id1sOQbzLuPx048NNpLaCDf8,19851
|
|
3
|
+
ErisPulse/Core/__init__.py,sha256=Wnkb4rI5kZhg8cdJE1sBfNRch55Aqp56npQ0DGXyHMA,675
|
|
4
|
+
ErisPulse/Core/adapter.py,sha256=zeil2mhlZoUg8Wg6GlEMVnQ7f5rJWZNxc0c1g3c4W0w,18146
|
|
5
|
+
ErisPulse/Core/env.py,sha256=9WYNadD9h2jP_2wxOVBJEhH1uDzbctW7eB4Ba9RSjA4,20409
|
|
6
|
+
ErisPulse/Core/logger.py,sha256=40vDe_D3L6ople-RZX8eGXntrZpFGHHzSl3pYxK-FLI,7196
|
|
7
|
+
ErisPulse/Core/mods.py,sha256=5SPutuzbMrA-VZwiXeNxYWfrdbpLRdYfQ0RvEkFuQgg,7308
|
|
8
|
+
ErisPulse/Core/raiserr.py,sha256=QLQ3r7p4iFP86XBLq9mtf1wv1xSlgny35i8t5-l4DXo,4620
|
|
9
|
+
ErisPulse/Core/server.py,sha256=FkDTeLuHD5IBnWVxvYU8pHb6yCt8GzyvC1bpOiJ7G7I,9217
|
|
10
|
+
ErisPulse/Core/shellprint.py,sha256=-BFoyFho_D3XEhxIoKt6x5gO4C62LKwmJWKDUGiPjNY,5908
|
|
11
|
+
ErisPulse/Core/util.py,sha256=kyydBAJHHG9I7rMRzKWtLAQMZoJyBqHiBAweqcraFkU,4001
|
|
12
|
+
erispulse-2.1.2.dist-info/METADATA,sha256=weSMmPWTDHy4mx36qhkHle7Wj9dC7dKv7qO_IX19vOI,6161
|
|
13
|
+
erispulse-2.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
erispulse-2.1.2.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
|
|
15
|
+
erispulse-2.1.2.dist-info/licenses/LICENSE,sha256=lBYj7Nk4urLvByj4HvQFxu8j9hThhFF6OGfyxAZBP9Q,1451
|
|
16
|
+
erispulse-2.1.2.dist-info/RECORD,,
|
erispulse-2.1.0.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=4jcAocMDQt4xdE9B3di8lGCtrU47Y4DY2ipW_9diYbU,26210
|
|
2
|
-
ErisPulse/__main__.py,sha256=DeMBevz0KyLywnd5HccgZDwVfv-EKQOpo1fsVypKwiE,18525
|
|
3
|
-
ErisPulse/Core/__init__.py,sha256=CIxWFdB6-0D8YJz7IdW052A4YllDsklEONCRj7mOpkQ,362
|
|
4
|
-
ErisPulse/Core/adapter.py,sha256=gzryZjrOy0uowE5oU4MaVTtGPW7HP4StfvP5ECC4rUk,20510
|
|
5
|
-
ErisPulse/Core/env.py,sha256=9WYNadD9h2jP_2wxOVBJEhH1uDzbctW7eB4Ba9RSjA4,20409
|
|
6
|
-
ErisPulse/Core/logger.py,sha256=035II2YvRmcAvbbCEsmMxjvAJhdLvQlUqaDXAufWk_E,5782
|
|
7
|
-
ErisPulse/Core/mods.py,sha256=5SPutuzbMrA-VZwiXeNxYWfrdbpLRdYfQ0RvEkFuQgg,7308
|
|
8
|
-
ErisPulse/Core/raiserr.py,sha256=QLQ3r7p4iFP86XBLq9mtf1wv1xSlgny35i8t5-l4DXo,4620
|
|
9
|
-
ErisPulse/Core/server.py,sha256=H8dUUj8mxBLEd3ick7btbWEE_m58Y6277Zo5FY7Ild4,9217
|
|
10
|
-
ErisPulse/Core/shellprint.py,sha256=-BFoyFho_D3XEhxIoKt6x5gO4C62LKwmJWKDUGiPjNY,5908
|
|
11
|
-
ErisPulse/Core/util.py,sha256=kyydBAJHHG9I7rMRzKWtLAQMZoJyBqHiBAweqcraFkU,4001
|
|
12
|
-
erispulse-2.1.0.dist-info/METADATA,sha256=Ip9gteaaYMG0Qkzxq_DbFnmvCYNC8ULMPxCZ6jYimeA,6161
|
|
13
|
-
erispulse-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
-
erispulse-2.1.0.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
|
|
15
|
-
erispulse-2.1.0.dist-info/licenses/LICENSE,sha256=lBYj7Nk4urLvByj4HvQFxu8j9hThhFF6OGfyxAZBP9Q,1451
|
|
16
|
-
erispulse-2.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|