ErisPulse 2.1.13rc3__py3-none-any.whl → 2.1.14__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 +8 -6
- ErisPulse/Core/adapter.py +18 -15
- ErisPulse/Core/config.py +0 -98
- ErisPulse/Core/env.py +1 -32
- ErisPulse/Core/erispulse_config.py +105 -0
- ErisPulse/Core/exceptions.py +108 -0
- ErisPulse/Core/logger.py +74 -1
- ErisPulse/Core/{server.py → router.py} +51 -70
- ErisPulse/__init__.py +20 -9
- ErisPulse/__main__.py +108 -26
- {erispulse-2.1.13rc3.dist-info → erispulse-2.1.14.dist-info}/METADATA +31 -16
- erispulse-2.1.14.dist-info/RECORD +16 -0
- ErisPulse/Core/raiserr.py +0 -181
- ErisPulse/Core/util.py +0 -123
- erispulse-2.1.13rc3.dist-info/RECORD +0 -16
- {erispulse-2.1.13rc3.dist-info → erispulse-2.1.14.dist-info}/WHEEL +0 -0
- {erispulse-2.1.13rc3.dist-info → erispulse-2.1.14.dist-info}/entry_points.txt +0 -0
- {erispulse-2.1.13rc3.dist-info → erispulse-2.1.14.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
# router.py (新文件名)
|
|
1
2
|
"""
|
|
2
|
-
ErisPulse
|
|
3
|
-
|
|
3
|
+
ErisPulse 路由系统
|
|
4
|
+
|
|
5
|
+
提供统一的HTTP和WebSocket路由管理,支持多适配器路由注册和生命周期管理。
|
|
4
6
|
|
|
5
7
|
{!--< tips >!--}
|
|
6
8
|
1. 适配器只需注册路由,无需自行管理服务器
|
|
@@ -19,9 +21,9 @@ from hypercorn.config import Config
|
|
|
19
21
|
from hypercorn.asyncio import serve
|
|
20
22
|
|
|
21
23
|
|
|
22
|
-
class
|
|
24
|
+
class RouterManager:
|
|
23
25
|
"""
|
|
24
|
-
|
|
26
|
+
路由管理器
|
|
25
27
|
|
|
26
28
|
{!--< tips >!--}
|
|
27
29
|
核心功能:
|
|
@@ -33,18 +35,18 @@ class AdapterServer:
|
|
|
33
35
|
|
|
34
36
|
def __init__(self):
|
|
35
37
|
"""
|
|
36
|
-
|
|
38
|
+
初始化路由管理器
|
|
37
39
|
|
|
38
40
|
{!--< tips >!--}
|
|
39
41
|
会自动创建FastAPI实例并设置核心路由
|
|
40
42
|
{!--< /tips >!--}
|
|
41
43
|
"""
|
|
42
44
|
self.app = FastAPI(
|
|
43
|
-
title="ErisPulse
|
|
44
|
-
description="
|
|
45
|
+
title="ErisPulse Router",
|
|
46
|
+
description="统一路由管理入口点",
|
|
45
47
|
version="1.0.0"
|
|
46
48
|
)
|
|
47
|
-
self.
|
|
49
|
+
self._http_routes: Dict[str, Dict[str, Callable]] = defaultdict(dict)
|
|
48
50
|
self._websocket_routes: Dict[str, Dict[str, Tuple[Callable, Optional[Callable]]]] = defaultdict(dict)
|
|
49
51
|
self.base_url = ""
|
|
50
52
|
self._server_task: Optional[asyncio.Task] = None
|
|
@@ -66,7 +68,7 @@ class AdapterServer:
|
|
|
66
68
|
:return:
|
|
67
69
|
Dict[str, str]: 包含服务状态的字典
|
|
68
70
|
"""
|
|
69
|
-
return {"status": "ok", "service": "ErisPulse
|
|
71
|
+
return {"status": "ok", "service": "ErisPulse Router"}
|
|
70
72
|
|
|
71
73
|
@self.app.get("/routes")
|
|
72
74
|
async def list_routes() -> Dict[str, Any]:
|
|
@@ -74,36 +76,23 @@ class AdapterServer:
|
|
|
74
76
|
列出所有已注册路由
|
|
75
77
|
|
|
76
78
|
:return:
|
|
77
|
-
Dict[str, Any]:
|
|
78
|
-
{
|
|
79
|
-
"http_routes": [
|
|
80
|
-
{
|
|
81
|
-
"path": "/adapter1/route1",
|
|
82
|
-
"adapter": "adapter1",
|
|
83
|
-
"methods": ["POST"]
|
|
84
|
-
},
|
|
85
|
-
...
|
|
86
|
-
],
|
|
87
|
-
"websocket_routes": [
|
|
88
|
-
{
|
|
89
|
-
"path": "/adapter1/ws",
|
|
90
|
-
"adapter": "adapter1",
|
|
91
|
-
"requires_auth": true
|
|
92
|
-
},
|
|
93
|
-
...
|
|
94
|
-
],
|
|
95
|
-
"base_url": self.base_url
|
|
96
|
-
}
|
|
79
|
+
Dict[str, Any]: 包含所有路由信息的字典
|
|
97
80
|
"""
|
|
98
81
|
http_routes = []
|
|
99
|
-
for adapter, routes in self.
|
|
82
|
+
for adapter, routes in self._http_routes.items():
|
|
100
83
|
for path, handler in routes.items():
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
# 查找对应的路由对象
|
|
85
|
+
route_obj = None
|
|
86
|
+
for route in self.app.router.routes:
|
|
87
|
+
if isinstance(route, APIRoute) and route.path == path:
|
|
88
|
+
route_obj = route
|
|
89
|
+
break
|
|
90
|
+
|
|
91
|
+
if route_obj:
|
|
103
92
|
http_routes.append({
|
|
104
93
|
"path": path,
|
|
105
94
|
"adapter": adapter,
|
|
106
|
-
"methods":
|
|
95
|
+
"methods": list(route_obj.methods)
|
|
107
96
|
})
|
|
108
97
|
|
|
109
98
|
websocket_routes = []
|
|
@@ -121,9 +110,9 @@ class AdapterServer:
|
|
|
121
110
|
"base_url": self.base_url
|
|
122
111
|
}
|
|
123
112
|
|
|
124
|
-
def
|
|
113
|
+
def register_http_route(
|
|
125
114
|
self,
|
|
126
|
-
|
|
115
|
+
module_name: str,
|
|
127
116
|
path: str,
|
|
128
117
|
handler: Callable,
|
|
129
118
|
methods: List[str] = ["POST"]
|
|
@@ -131,35 +120,37 @@ class AdapterServer:
|
|
|
131
120
|
"""
|
|
132
121
|
注册HTTP路由
|
|
133
122
|
|
|
134
|
-
:param
|
|
135
|
-
:param path: str 路由路径
|
|
123
|
+
:param module_name: str 模块名称
|
|
124
|
+
:param path: str 路由路径
|
|
136
125
|
:param handler: Callable 处理函数
|
|
137
126
|
:param methods: List[str] HTTP方法列表(默认["POST"])
|
|
138
127
|
|
|
139
128
|
:raises ValueError: 当路径已注册时抛出
|
|
140
|
-
|
|
141
|
-
{!--< tips >!--}
|
|
142
|
-
路径会自动添加适配器前缀,如:/adapter_name/path
|
|
143
|
-
{!--< /tips >!--}
|
|
144
129
|
"""
|
|
145
|
-
full_path = f"/{
|
|
130
|
+
full_path = f"/{module_name}{path}"
|
|
146
131
|
|
|
147
|
-
if full_path in self.
|
|
132
|
+
if full_path in self._http_routes[module_name]:
|
|
148
133
|
raise ValueError(f"路径 {full_path} 已注册")
|
|
149
134
|
|
|
150
135
|
route = APIRoute(
|
|
151
136
|
path=full_path,
|
|
152
137
|
endpoint=handler,
|
|
153
138
|
methods=methods,
|
|
154
|
-
name=f"{
|
|
139
|
+
name=f"{module_name}_{path.replace('/', '_')}"
|
|
155
140
|
)
|
|
156
141
|
self.app.router.routes.append(route)
|
|
157
|
-
self.
|
|
142
|
+
self._http_routes[module_name][full_path] = handler
|
|
158
143
|
logger.info(f"注册HTTP路由: {self.base_url}{full_path} 方法: {methods}")
|
|
159
144
|
|
|
145
|
+
def register_webhook(self, *args, **kwargs) -> None:
|
|
146
|
+
"""
|
|
147
|
+
兼容性方法:注册HTTP路由(适配器旧接口)
|
|
148
|
+
"""
|
|
149
|
+
return self.register_http_route(*args, **kwargs)
|
|
150
|
+
|
|
160
151
|
def register_websocket(
|
|
161
152
|
self,
|
|
162
|
-
|
|
153
|
+
module_name: str,
|
|
163
154
|
path: str,
|
|
164
155
|
handler: Callable[[WebSocket], Awaitable[Any]],
|
|
165
156
|
auth_handler: Optional[Callable[[WebSocket], Awaitable[bool]]] = None,
|
|
@@ -167,29 +158,21 @@ class AdapterServer:
|
|
|
167
158
|
"""
|
|
168
159
|
注册WebSocket路由
|
|
169
160
|
|
|
170
|
-
:param
|
|
171
|
-
:param path: str WebSocket路径
|
|
161
|
+
:param module_name: str 模块名称
|
|
162
|
+
:param path: str WebSocket路径
|
|
172
163
|
:param handler: Callable[[WebSocket], Awaitable[Any]] 主处理函数
|
|
173
164
|
:param auth_handler: Optional[Callable[[WebSocket], Awaitable[bool]]] 认证函数
|
|
174
165
|
|
|
175
166
|
:raises ValueError: 当路径已注册时抛出
|
|
176
|
-
|
|
177
|
-
{!--< tips >!--}
|
|
178
|
-
认证函数应返回布尔值,False将拒绝连接
|
|
179
|
-
{!--< /tips >!--}
|
|
180
167
|
"""
|
|
181
|
-
full_path = f"/{
|
|
168
|
+
full_path = f"/{module_name}{path}"
|
|
182
169
|
|
|
183
|
-
if full_path in self._websocket_routes[
|
|
170
|
+
if full_path in self._websocket_routes[module_name]:
|
|
184
171
|
raise ValueError(f"WebSocket路径 {full_path} 已注册")
|
|
185
172
|
|
|
186
173
|
async def websocket_endpoint(websocket: WebSocket) -> None:
|
|
187
174
|
"""
|
|
188
175
|
WebSocket端点包装器
|
|
189
|
-
|
|
190
|
-
{!--< internal-use >!--}
|
|
191
|
-
处理连接生命周期和错误处理
|
|
192
|
-
{!--< /internal-use >!--}
|
|
193
176
|
"""
|
|
194
177
|
await websocket.accept()
|
|
195
178
|
|
|
@@ -209,17 +192,16 @@ class AdapterServer:
|
|
|
209
192
|
self.app.add_api_websocket_route(
|
|
210
193
|
path=full_path,
|
|
211
194
|
endpoint=websocket_endpoint,
|
|
212
|
-
name=f"{
|
|
195
|
+
name=f"{module_name}_{path.replace('/', '_')}"
|
|
213
196
|
)
|
|
214
|
-
self._websocket_routes[
|
|
197
|
+
self._websocket_routes[module_name][full_path] = (handler, auth_handler)
|
|
215
198
|
logger.info(f"注册WebSocket: {self.base_url}{full_path} {'(需认证)' if auth_handler else ''}")
|
|
216
199
|
|
|
217
200
|
def get_app(self) -> FastAPI:
|
|
218
201
|
"""
|
|
219
202
|
获取FastAPI应用实例
|
|
220
203
|
|
|
221
|
-
:return:
|
|
222
|
-
FastAPI: FastAPI应用实例
|
|
204
|
+
:return: FastAPI应用实例
|
|
223
205
|
"""
|
|
224
206
|
return self.app
|
|
225
207
|
|
|
@@ -231,7 +213,7 @@ class AdapterServer:
|
|
|
231
213
|
ssl_keyfile: Optional[str] = None
|
|
232
214
|
) -> None:
|
|
233
215
|
"""
|
|
234
|
-
|
|
216
|
+
启动路由服务器
|
|
235
217
|
|
|
236
218
|
:param host: str 监听地址(默认"0.0.0.0")
|
|
237
219
|
:param port: int 监听端口(默认8000)
|
|
@@ -252,25 +234,24 @@ class AdapterServer:
|
|
|
252
234
|
config.keyfile = ssl_keyfile
|
|
253
235
|
|
|
254
236
|
self.base_url = f"http{'s' if ssl_certfile else ''}://{host}:{port}"
|
|
255
|
-
logger.info(f"
|
|
237
|
+
logger.info(f"启动路由服务器 {self.base_url}")
|
|
256
238
|
|
|
257
239
|
self._server_task = asyncio.create_task(serve(self.app, config))
|
|
258
240
|
|
|
259
241
|
async def stop(self) -> None:
|
|
260
242
|
"""
|
|
261
243
|
停止服务器
|
|
262
|
-
|
|
263
|
-
{!--< tips >!--}
|
|
264
|
-
会等待所有连接正常关闭
|
|
265
|
-
{!--< /tips >!--}
|
|
266
244
|
"""
|
|
267
245
|
if self._server_task:
|
|
268
246
|
self._server_task.cancel()
|
|
269
247
|
try:
|
|
270
248
|
await self._server_task
|
|
271
249
|
except asyncio.CancelledError:
|
|
272
|
-
logger.info("
|
|
250
|
+
logger.info("路由服务器已停止")
|
|
273
251
|
self._server_task = None
|
|
274
252
|
|
|
253
|
+
# 主要实例
|
|
254
|
+
router = RouterManager()
|
|
275
255
|
|
|
276
|
-
|
|
256
|
+
# 兼容性实例
|
|
257
|
+
adapter_server = router
|
ErisPulse/__init__.py
CHANGED
|
@@ -10,37 +10,47 @@ ErisPulse SDK 主模块
|
|
|
10
10
|
{!--< /tips >!--}
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
__version__ = "2.1.14"
|
|
14
|
+
__author__ = "ErisPulse"
|
|
15
|
+
|
|
13
16
|
import os
|
|
14
17
|
import sys
|
|
15
18
|
import importlib
|
|
19
|
+
import asyncio
|
|
16
20
|
import inspect
|
|
17
21
|
import importlib.metadata
|
|
18
22
|
from typing import Dict, List, Tuple, Type, Any
|
|
19
23
|
from pathlib import Path
|
|
20
24
|
|
|
21
25
|
# BaseModules: SDK核心模块
|
|
22
|
-
from .Core import util
|
|
23
|
-
from .Core import raiserr
|
|
24
26
|
from .Core import logger
|
|
25
27
|
from .Core import env
|
|
26
28
|
from .Core import mods
|
|
27
29
|
from .Core import adapter, AdapterFather, SendDSL
|
|
28
|
-
from .Core import adapter_server
|
|
30
|
+
from .Core import router, adapter_server
|
|
31
|
+
from .Core import exceptions
|
|
32
|
+
from .Core import config
|
|
29
33
|
|
|
30
34
|
sdk = sys.modules[__name__]
|
|
31
35
|
|
|
32
36
|
BaseModules = {
|
|
33
|
-
"util": util,
|
|
34
37
|
"logger": logger,
|
|
35
|
-
"
|
|
38
|
+
"config": config,
|
|
39
|
+
"exceptions": exceptions,
|
|
36
40
|
"env": env,
|
|
37
41
|
"mods": mods,
|
|
38
42
|
"adapter": adapter,
|
|
43
|
+
"router": router,
|
|
44
|
+
"adapter_server": adapter_server,
|
|
39
45
|
"SendDSL": SendDSL,
|
|
40
46
|
"AdapterFather": AdapterFather,
|
|
41
47
|
"BaseAdapter": AdapterFather
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
asyncio_loop = asyncio.get_event_loop()
|
|
51
|
+
|
|
52
|
+
exceptions.setup_async_loop(asyncio_loop)
|
|
53
|
+
|
|
44
54
|
for module, moduleObj in BaseModules.items():
|
|
45
55
|
setattr(sdk, module, moduleObj)
|
|
46
56
|
|
|
@@ -664,23 +674,24 @@ def _prepare_environment() -> bool:
|
|
|
664
674
|
{!--< internal-use >!--}
|
|
665
675
|
准备运行环境
|
|
666
676
|
|
|
667
|
-
|
|
668
|
-
2. 加载环境变量配置
|
|
677
|
+
初始化项目环境文件
|
|
669
678
|
|
|
670
679
|
:return: bool 环境准备是否成功
|
|
671
680
|
"""
|
|
672
681
|
logger.info("[Init] 准备初始化环境...")
|
|
673
682
|
try:
|
|
683
|
+
from .Core.erispulse_config import get_erispulse_config
|
|
684
|
+
get_erispulse_config()
|
|
685
|
+
logger.info("[Init] 配置文件已加载")
|
|
686
|
+
|
|
674
687
|
main_init = init_progress()
|
|
675
688
|
if main_init:
|
|
676
689
|
logger.info("[Init] 项目入口已生成, 你可以在 main.py 中编写一些代码")
|
|
677
|
-
env.load_env_file()
|
|
678
690
|
return True
|
|
679
691
|
except Exception as e:
|
|
680
692
|
logger.error(f"环境准备失败: {e}")
|
|
681
693
|
return False
|
|
682
694
|
|
|
683
|
-
|
|
684
695
|
def init() -> bool:
|
|
685
696
|
"""
|
|
686
697
|
SDK初始化入口
|
ErisPulse/__main__.py
CHANGED
|
@@ -19,7 +19,6 @@ import json
|
|
|
19
19
|
import asyncio
|
|
20
20
|
from urllib.parse import urlparse
|
|
21
21
|
from typing import List, Dict, Tuple, Optional, Callable, Any
|
|
22
|
-
from importlib.metadata import version, PackageNotFoundError
|
|
23
22
|
from watchdog.observers import Observer
|
|
24
23
|
from watchdog.events import FileSystemEventHandler
|
|
25
24
|
|
|
@@ -188,26 +187,38 @@ class PackageManager:
|
|
|
188
187
|
|
|
189
188
|
try:
|
|
190
189
|
# 查找模块和适配器
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
190
|
+
entry_points = importlib.metadata.entry_points()
|
|
191
|
+
|
|
192
|
+
# 处理模块
|
|
193
|
+
if hasattr(entry_points, 'select'):
|
|
194
|
+
module_entries = entry_points.select(group='erispulse.module')
|
|
195
|
+
else:
|
|
196
|
+
module_entries = entry_points.get('erispulse.module', [])
|
|
197
|
+
|
|
198
|
+
for entry in module_entries:
|
|
199
|
+
dist = entry.dist
|
|
200
|
+
packages["modules"][entry.name] = {
|
|
201
|
+
"package": dist.metadata["Name"],
|
|
202
|
+
"version": dist.version,
|
|
203
|
+
"summary": dist.metadata["Summary"],
|
|
204
|
+
"enabled": self._is_module_enabled(entry.name)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# 处理适配器
|
|
208
|
+
if hasattr(entry_points, 'select'):
|
|
209
|
+
adapter_entries = entry_points.select(group='erispulse.adapter')
|
|
210
|
+
else:
|
|
211
|
+
adapter_entries = entry_points.get('erispulse.adapter', [])
|
|
212
|
+
|
|
213
|
+
for entry in adapter_entries:
|
|
214
|
+
dist = entry.dist
|
|
215
|
+
packages["adapters"][entry.name] = {
|
|
216
|
+
"package": dist.metadata["Name"],
|
|
217
|
+
"version": dist.version,
|
|
218
|
+
"summary": dist.metadata["Summary"]
|
|
219
|
+
}
|
|
208
220
|
|
|
209
221
|
# 查找CLI扩展
|
|
210
|
-
entry_points = importlib.metadata.entry_points()
|
|
211
222
|
if hasattr(entry_points, 'select'):
|
|
212
223
|
cli_entries = entry_points.select(group='erispulse.cli')
|
|
213
224
|
else:
|
|
@@ -222,9 +233,9 @@ class PackageManager:
|
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
except Exception as e:
|
|
225
|
-
|
|
236
|
+
print(f"[error] 获取已安装包信息失败: {e}")
|
|
226
237
|
import traceback
|
|
227
|
-
|
|
238
|
+
print(traceback.format_exc())
|
|
228
239
|
|
|
229
240
|
return packages
|
|
230
241
|
|
|
@@ -443,12 +454,44 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
443
454
|
|
|
444
455
|
def _handle_reload(self, event, reason: str):
|
|
445
456
|
"""
|
|
446
|
-
|
|
447
|
-
|
|
457
|
+
处理热重载逻辑
|
|
448
458
|
:param event: 文件系统事件
|
|
449
|
-
:param reason:
|
|
459
|
+
:param reason: 重载原因
|
|
450
460
|
"""
|
|
451
|
-
|
|
461
|
+
from ErisPulse.Core import adapter, logger
|
|
462
|
+
# 在重载前确保所有适配器正确停止
|
|
463
|
+
try:
|
|
464
|
+
# 检查适配器是否正在运行
|
|
465
|
+
if hasattr(adapter, '_started_instances') and adapter._started_instances:
|
|
466
|
+
logger.info("正在停止适配器...")
|
|
467
|
+
# 创建新的事件循环来运行异步停止操作
|
|
468
|
+
import asyncio
|
|
469
|
+
import threading
|
|
470
|
+
|
|
471
|
+
# 如果在主线程中
|
|
472
|
+
if threading.current_thread() is threading.main_thread():
|
|
473
|
+
try:
|
|
474
|
+
# 尝试获取当前事件循环
|
|
475
|
+
loop = asyncio.get_running_loop()
|
|
476
|
+
# 在新线程中运行适配器停止
|
|
477
|
+
stop_thread = threading.Thread(target=lambda: asyncio.run(adapter.shutdown()))
|
|
478
|
+
stop_thread.start()
|
|
479
|
+
stop_thread.join(timeout=10) # 最多等待10秒
|
|
480
|
+
except RuntimeError:
|
|
481
|
+
# 没有运行中的事件循环
|
|
482
|
+
asyncio.run(adapter.shutdown())
|
|
483
|
+
else:
|
|
484
|
+
# 在非主线程中,创建新的事件循环
|
|
485
|
+
new_loop = asyncio.new_event_loop()
|
|
486
|
+
asyncio.set_event_loop(new_loop)
|
|
487
|
+
new_loop.run_until_complete(adapter.shutdown())
|
|
488
|
+
|
|
489
|
+
logger.info("适配器已停止")
|
|
490
|
+
except Exception as e:
|
|
491
|
+
logger.warning(f"停止适配器时出错: {e}")
|
|
492
|
+
|
|
493
|
+
# 原有的重载逻辑
|
|
494
|
+
logger.info(f"检测到文件变更 ({reason}),正在重启...")
|
|
452
495
|
self._terminate_process()
|
|
453
496
|
self.start_process()
|
|
454
497
|
|
|
@@ -1035,9 +1078,10 @@ class CLI:
|
|
|
1035
1078
|
|
|
1036
1079
|
try:
|
|
1037
1080
|
while True:
|
|
1038
|
-
time.sleep(
|
|
1081
|
+
time.sleep(0.5)
|
|
1039
1082
|
except KeyboardInterrupt:
|
|
1040
1083
|
console.print("\n[info]正在安全关闭...[/]")
|
|
1084
|
+
self._cleanup_adapters()
|
|
1041
1085
|
self._cleanup()
|
|
1042
1086
|
console.print("[success]已安全退出[/]")
|
|
1043
1087
|
|
|
@@ -1076,6 +1120,44 @@ class CLI:
|
|
|
1076
1120
|
console.print(traceback.format_exc())
|
|
1077
1121
|
self._cleanup()
|
|
1078
1122
|
sys.exit(1)
|
|
1123
|
+
|
|
1124
|
+
def _cleanup_adapters(self):
|
|
1125
|
+
"""
|
|
1126
|
+
清理适配器资源
|
|
1127
|
+
"""
|
|
1128
|
+
from ErisPulse import adapter, logger
|
|
1129
|
+
try:
|
|
1130
|
+
import asyncio
|
|
1131
|
+
import threading
|
|
1132
|
+
|
|
1133
|
+
# 检查是否有正在运行的适配器
|
|
1134
|
+
if (hasattr(adapter, '_started_instances') and
|
|
1135
|
+
adapter._started_instances):
|
|
1136
|
+
|
|
1137
|
+
logger.info("正在停止所有适配器...")
|
|
1138
|
+
|
|
1139
|
+
if threading.current_thread() is threading.main_thread():
|
|
1140
|
+
try:
|
|
1141
|
+
loop = asyncio.get_running_loop()
|
|
1142
|
+
if loop.is_running():
|
|
1143
|
+
# 在新线程中运行
|
|
1144
|
+
stop_thread = threading.Thread(
|
|
1145
|
+
target=lambda: asyncio.run(adapter.shutdown())
|
|
1146
|
+
)
|
|
1147
|
+
stop_thread.start()
|
|
1148
|
+
stop_thread.join(timeout=5)
|
|
1149
|
+
else:
|
|
1150
|
+
asyncio.run(adapter.shutdown())
|
|
1151
|
+
except RuntimeError:
|
|
1152
|
+
asyncio.run(adapter.shutdown())
|
|
1153
|
+
else:
|
|
1154
|
+
new_loop = asyncio.new_event_loop()
|
|
1155
|
+
asyncio.set_event_loop(new_loop)
|
|
1156
|
+
new_loop.run_until_complete(adapter.shutdown())
|
|
1157
|
+
|
|
1158
|
+
logger.info("适配器已全部停止")
|
|
1159
|
+
except Exception as e:
|
|
1160
|
+
logger.error(f"清理适配器资源时出错: {e}")
|
|
1079
1161
|
|
|
1080
1162
|
def main():
|
|
1081
1163
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.14
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -68,17 +68,26 @@ Description-Content-Type: text/markdown
|
|
|
68
68
|
[](https://github.com/FramerOrg)
|
|
69
69
|
[](https://pypi.org/project/ErisPulse/)
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
[](https://www.erisdev.com/docs.html)
|
|
73
|
-
[](https://erispulse.pages.dev/docs.html)
|
|
74
|
-
[](https://erispulse.github.io/docs.html)
|
|
75
|
-
[](https://erispulse.netlify.app/docs.htm)
|
|
71
|
+
## 文档站
|
|
76
72
|
|
|
73
|
+
[](https://www.erisdev.com/#docs)
|
|
74
|
+
[](https://erispulse.pages.dev/#docs)
|
|
75
|
+
[](https://erispulse.github.io/#docs)
|
|
76
|
+
[](https://erispulse.netlify.app/#docs)
|
|
77
|
+
|
|
78
|
+
## 模块市场
|
|
79
|
+
|
|
80
|
+
[](https://www.erisdev.com/#market)
|
|
81
|
+
[](https://erispulse.pages.dev/#market)
|
|
82
|
+
[](https://erispulse.github.io/#market)
|
|
83
|
+
[](https://erispulse.netlify.app/#market)
|
|
84
|
+
|
|
85
|
+
---
|
|
77
86
|
|
|
78
87
|
## 核心特性
|
|
79
88
|
|
|
80
89
|
| 特性 | 描述 |
|
|
81
|
-
|
|
90
|
+
|:-----|:-----|
|
|
82
91
|
| **异步架构** | 完全基于 async/await 的异步设计 |
|
|
83
92
|
| **模块化系统** | 灵活的插件和模块管理 |
|
|
84
93
|
| **热重载** | 开发时自动重载,无需重启 |
|
|
@@ -100,6 +109,7 @@ irm https://get.erisdev.com/install.ps1 -OutFile install.ps1; powershell -Execut
|
|
|
100
109
|
```
|
|
101
110
|
|
|
102
111
|
#### macOS/Linux:
|
|
112
|
+
|
|
103
113
|
```bash
|
|
104
114
|
curl -sSL https://get.erisdev.com/install.sh | tee install.sh >/dev/null && chmod +x install.sh && ./install.sh
|
|
105
115
|
```
|
|
@@ -108,14 +118,14 @@ curl -sSL https://get.erisdev.com/install.sh | tee install.sh >/dev/null && chmo
|
|
|
108
118
|
|
|
109
119
|
## 测试与开发
|
|
110
120
|
|
|
111
|
-
### 克隆项目并进入目录
|
|
121
|
+
### 1. 克隆项目并进入目录
|
|
112
122
|
|
|
113
123
|
```bash
|
|
114
124
|
git clone -b Develop/v2 https://github.com/ErisPulse/ErisPulse.git
|
|
115
125
|
cd ErisPulse
|
|
116
126
|
```
|
|
117
127
|
|
|
118
|
-
### 使用 `uv` 同步项目环境
|
|
128
|
+
### 2. 使用 `uv` 同步项目环境
|
|
119
129
|
|
|
120
130
|
```bash
|
|
121
131
|
uv sync
|
|
@@ -127,15 +137,15 @@ source .venv/bin/activate
|
|
|
127
137
|
|
|
128
138
|
> `ErisPulse` 目前正在使用 `python3.13` 进行开发(所以您同步环境时会自动安装 `3.13`),但也可以使用其他版本(版本不应低于 `3.10`)。
|
|
129
139
|
|
|
130
|
-
### 安装依赖并开始
|
|
140
|
+
### 3. 安装依赖并开始
|
|
131
141
|
|
|
132
142
|
```bash
|
|
133
143
|
uv pip install -e .
|
|
134
144
|
```
|
|
135
145
|
|
|
136
|
-
|
|
146
|
+
这将以"开发模式"安装 SDK,所有本地修改都会立即生效。
|
|
137
147
|
|
|
138
|
-
### 验证安装
|
|
148
|
+
### 4. 验证安装
|
|
139
149
|
|
|
140
150
|
运行以下命令确认 SDK 正常加载:
|
|
141
151
|
|
|
@@ -143,7 +153,7 @@ uv pip install -e .
|
|
|
143
153
|
python -c "from ErisPulse import sdk; sdk.init()"
|
|
144
154
|
```
|
|
145
155
|
|
|
146
|
-
### 运行测试
|
|
156
|
+
### 5. 运行测试
|
|
147
157
|
|
|
148
158
|
我们提供了一个交互式测试脚本,可以帮助您快速验证SDK功能:
|
|
149
159
|
|
|
@@ -158,14 +168,15 @@ uv run devs/test.py
|
|
|
158
168
|
- 工具函数测试
|
|
159
169
|
- 适配器功能测试
|
|
160
170
|
|
|
161
|
-
### 开发模式 (热重载)
|
|
171
|
+
### 6. 开发模式 (热重载)
|
|
172
|
+
|
|
162
173
|
```bash
|
|
163
174
|
epsdk run your_script.py --reload
|
|
164
175
|
```
|
|
165
176
|
|
|
166
177
|
---
|
|
167
178
|
|
|
168
|
-
##
|
|
179
|
+
## 贡献指南
|
|
169
180
|
|
|
170
181
|
我们欢迎各种形式的贡献,包括但不限于:
|
|
171
182
|
|
|
@@ -181,6 +192,10 @@ epsdk run your_script.py --reload
|
|
|
181
192
|
4. **文档改进**
|
|
182
193
|
帮助完善文档和示例代码
|
|
183
194
|
|
|
195
|
+
[加入社区讨论 →](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
196
|
+
|
|
184
197
|
---
|
|
185
198
|
|
|
186
|
-
[
|
|
199
|
+
[](https://starchart.cc/ErisPulse/ErisPulse)
|
|
200
|
+
|
|
201
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=PfRuqOSlKcneLN1KrqcW5wFsgnVmpzrDOWrYYweUBjM,26423
|
|
2
|
+
ErisPulse/__main__.py,sha256=sWlvqyvt3LGrkp7WBSsOSbuPrujqgJvLzt5nA5Bdf4w,40790
|
|
3
|
+
ErisPulse/Core/__init__.py,sha256=hX2yEt9VSD3JubiofoQdcY4v1lnQUU02dhuVADkMTVo,437
|
|
4
|
+
ErisPulse/Core/adapter.py,sha256=oBJOp6SS8sm8NgIxQwetGsHu24wHNXz7ESQ5yKJSo7Q,18234
|
|
5
|
+
ErisPulse/Core/config.py,sha256=2BRWINOqKtHSCP4KfhuiRpGwR96jWGKV7gjZSi_VQHE,2397
|
|
6
|
+
ErisPulse/Core/env.py,sha256=U45f9WtriVyd3tW1N8to-ZvpzcF9gD8DJzNTC1jY2cM,17665
|
|
7
|
+
ErisPulse/Core/erispulse_config.py,sha256=QDx401hNX9JcSHqCSVK33X6VTubl6HI1znAK3T_J0K0,3034
|
|
8
|
+
ErisPulse/Core/exceptions.py,sha256=zuTREGczwGzbYT4Z6dACqHwgNRpiJeLFR8aCxFdOg7k,3667
|
|
9
|
+
ErisPulse/Core/logger.py,sha256=8hdbF6x3JpsQbbeEjvbSej134q5oV0wafagpWq3Nbeg,11223
|
|
10
|
+
ErisPulse/Core/mods.py,sha256=2yIq8t9Ca9CBPRiZU0yr8Lc0XGmmkB7LlH-5FWqXjw4,7023
|
|
11
|
+
ErisPulse/Core/router.py,sha256=66hT8VC2dVNX-dANldoOPDcqQ94hidFkNnvKgAPemGQ,8491
|
|
12
|
+
erispulse-2.1.14.dist-info/METADATA,sha256=_vPGvuW0ovuDT71VdSXESB73bvagY1rpskOrPaGY964,6888
|
|
13
|
+
erispulse-2.1.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
14
|
+
erispulse-2.1.14.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
|
|
15
|
+
erispulse-2.1.14.dist-info/licenses/LICENSE,sha256=4jyqikiB0G0n06CEEMMTzTXjE4IShghSlB74skMSPQs,1464
|
|
16
|
+
erispulse-2.1.14.dist-info/RECORD,,
|