ErisPulse 2.3.0.dev5__py3-none-any.whl → 2.3.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/_self_config.py +14 -2
- ErisPulse/Core/ux.py +4 -33
- ErisPulse/__init__.py +154 -10
- ErisPulse/utils/__init__.py +2 -0
- ErisPulse/utils/cli.py +81 -86
- ErisPulse/utils/console.py +53 -0
- ErisPulse/utils/package_manager.py +1 -3
- ErisPulse/utils/reload_handler.py +10 -34
- {erispulse-2.3.0.dev5.dist-info → erispulse-2.3.2.dist-info}/METADATA +1 -1
- {erispulse-2.3.0.dev5.dist-info → erispulse-2.3.2.dist-info}/RECORD +13 -12
- {erispulse-2.3.0.dev5.dist-info → erispulse-2.3.2.dist-info}/entry_points.txt +1 -1
- {erispulse-2.3.0.dev5.dist-info → erispulse-2.3.2.dist-info}/WHEEL +0 -0
- {erispulse-2.3.0.dev5.dist-info → erispulse-2.3.2.dist-info}/licenses/LICENSE +0 -0
ErisPulse/Core/_self_config.py
CHANGED
|
@@ -24,7 +24,10 @@ DEFAULT_ERISPULSE_CONFIG = {
|
|
|
24
24
|
"max_snapshot": 20
|
|
25
25
|
},
|
|
26
26
|
"modules": {},
|
|
27
|
-
"adapters": {}
|
|
27
|
+
"adapters": {},
|
|
28
|
+
"framework": {
|
|
29
|
+
"enable_lazy_loading": True
|
|
30
|
+
}
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
def _ensure_erispulse_config_structure(config_dict: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -115,4 +118,13 @@ def get_storage_config() -> Dict[str, Any]:
|
|
|
115
118
|
:return: 存储配置字典
|
|
116
119
|
"""
|
|
117
120
|
erispulse_config = get_erispulse_config()
|
|
118
|
-
return erispulse_config["storage"]
|
|
121
|
+
return erispulse_config["storage"]
|
|
122
|
+
|
|
123
|
+
def get_framework_config() -> Dict[str, Any]:
|
|
124
|
+
"""
|
|
125
|
+
获取框架配置
|
|
126
|
+
|
|
127
|
+
:return: 框架配置字典
|
|
128
|
+
"""
|
|
129
|
+
erispulse_config = get_erispulse_config()
|
|
130
|
+
return erispulse_config["framework"]
|
ErisPulse/Core/ux.py
CHANGED
|
@@ -227,7 +227,7 @@ class UXManager:
|
|
|
227
227
|
self.console.print(f"[green]创建项目目录: {project_name}[/green]")
|
|
228
228
|
|
|
229
229
|
# 创建基本目录结构
|
|
230
|
-
dirs = ["
|
|
230
|
+
dirs = ["config", "logs"]
|
|
231
231
|
for dir_name in dirs:
|
|
232
232
|
dir_path = project_path / dir_name
|
|
233
233
|
dir_path.mkdir(exist_ok=True)
|
|
@@ -288,39 +288,11 @@ class UXManager:
|
|
|
288
288
|
|
|
289
289
|
self.console.print("[green]创建主程序文件: main.py[/green]")
|
|
290
290
|
|
|
291
|
-
# 创建模块目录和示例模块
|
|
292
|
-
if adapter_list:
|
|
293
|
-
example_file = project_path / "modules" / "example.py"
|
|
294
|
-
if not example_file.exists():
|
|
295
|
-
with open(example_file, "w", encoding="utf-8") as f:
|
|
296
|
-
f.write('"""')
|
|
297
|
-
f.write("\n示例模块\n\n")
|
|
298
|
-
f.write("这是一个示例模块,展示了如何创建基本的消息处理器\n")
|
|
299
|
-
f.write('"""\n\n')
|
|
300
|
-
f.write("from ErisPulse.Core.Event import command, message\n\n")
|
|
301
|
-
f.write("class Main:\n")
|
|
302
|
-
f.write(" def __init__(self, sdk=None):\n")
|
|
303
|
-
f.write(" self.sdk = sdk or globals().get('sdk', __import__('ErisPulse').sdk)\n")
|
|
304
|
-
f.write(" self.logger = self.sdk.logger.get_child(\"Example\")\n")
|
|
305
|
-
f.write(" self.logger.info(\"示例模块已加载\")\n\n")
|
|
306
|
-
f.write(" # 注册命令处理器\n")
|
|
307
|
-
f.write(" @command(\"hello\", help=\"发送问候消息\")\n")
|
|
308
|
-
f.write(" async def hello_command(event):\n")
|
|
309
|
-
f.write(" platform = event[\"platform\"]\n")
|
|
310
|
-
f.write(" user_id = event[\"user_id\"]\n")
|
|
311
|
-
f.write(" \n")
|
|
312
|
-
f.write(" if hasattr(self.sdk.adapter, platform):\n")
|
|
313
|
-
f.write(" adapter_instance = getattr(self.sdk.adapter, platform)\n")
|
|
314
|
-
f.write(" await adapter_instance.Send.To(\"user\", user_id).Text(\"你好!这是一个示例消息。\")\n")
|
|
315
|
-
|
|
316
|
-
self.console.print("[green]创建示例模块: modules/example.py[/green]")
|
|
317
|
-
|
|
318
291
|
self.console.print("\n[bold green]项目 {} 初始化成功![/bold green]".format(project_name))
|
|
319
292
|
self.console.print("\n[cyan]接下来您可以:[/cyan]")
|
|
320
293
|
self.console.print(f"1. 编辑 {project_name}/config.toml 配置适配器")
|
|
321
|
-
self.console.print(f"2.
|
|
322
|
-
self.console.print(
|
|
323
|
-
|
|
294
|
+
self.console.print(f"2. 运行 [cyan]cd {project_name} \n ep run[/cyan] 启动项目")
|
|
295
|
+
self.console.print("\n访问 https://github.com/ErisPulse/ErisPulse/tree/main/docs 获取更多信息和文档")
|
|
324
296
|
return True
|
|
325
297
|
|
|
326
298
|
except Exception as e:
|
|
@@ -410,8 +382,7 @@ class UXManager:
|
|
|
410
382
|
# 显示下一步操作
|
|
411
383
|
self.console.print("\n[cyan]接下来您可以:[/cyan]")
|
|
412
384
|
self.console.print("1. 编辑 {}/config.toml 进一步配置".format(project_name))
|
|
413
|
-
self.console.print("2.
|
|
414
|
-
self.console.print("3. 运行 [cyan]cd {} \n ep run[/cyan] 启动项目".format(project_name))
|
|
385
|
+
self.console.print("2. 运行 [cyan]cd {} \n ep run[/cyan] 启动项目".format(project_name))
|
|
415
386
|
|
|
416
387
|
return True
|
|
417
388
|
|
ErisPulse/__init__.py
CHANGED
|
@@ -177,6 +177,88 @@ class LazyModule:
|
|
|
177
177
|
logger.error(f"懒加载模块 {object.__getattribute__(self, '_module_name')} 初始化失败: {e}")
|
|
178
178
|
raise e
|
|
179
179
|
|
|
180
|
+
def _initialize_sync(self):
|
|
181
|
+
"""
|
|
182
|
+
同步初始化模块,用于在异步上下文中进行同步调用
|
|
183
|
+
|
|
184
|
+
:raises LazyLoadError: 当模块初始化失败时抛出
|
|
185
|
+
"""
|
|
186
|
+
# 避免重复初始化
|
|
187
|
+
if object.__getattribute__(self, '_initialized'):
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
logger.debug(f"正在同步初始化懒加载模块 {object.__getattribute__(self, '_module_name')}...")
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
# 获取类的__init__参数信息
|
|
194
|
+
logger.debug(f"正在获取模块 {object.__getattribute__(self, '_module_name')} 的 __init__ 参数信息...")
|
|
195
|
+
init_signature = inspect.signature(object.__getattribute__(self, '_module_class').__init__)
|
|
196
|
+
params = init_signature.parameters
|
|
197
|
+
|
|
198
|
+
# 根据参数决定是否传入sdk
|
|
199
|
+
if 'sdk' in params:
|
|
200
|
+
logger.debug(f"模块 {object.__getattribute__(self, '_module_name')} 需要传入 sdk 参数")
|
|
201
|
+
instance = object.__getattribute__(self, '_module_class')(object.__getattribute__(self, '_sdk_ref'))
|
|
202
|
+
else:
|
|
203
|
+
logger.debug(f"模块 {object.__getattribute__(self, '_module_name')} 不需要传入 sdk 参数")
|
|
204
|
+
instance = object.__getattribute__(self, '_module_class')()
|
|
205
|
+
|
|
206
|
+
logger.debug(f"正在设置模块 {object.__getattribute__(self, '_module_name')} 的 moduleInfo 属性...")
|
|
207
|
+
setattr(instance, "moduleInfo", object.__getattribute__(self, '_module_info'))
|
|
208
|
+
|
|
209
|
+
# 使用object.__setattr__避免触发自定义的__setattr__
|
|
210
|
+
object.__setattr__(self, '_instance', instance)
|
|
211
|
+
object.__setattr__(self, '_initialized', True)
|
|
212
|
+
object.__setattr__(self, '_needs_async_init', False) # 确保清除异步初始化标志
|
|
213
|
+
|
|
214
|
+
# 注意:在同步初始化中,我们不能调用异步的 module.load 和 lifecycle.submit_event
|
|
215
|
+
# 这些将在异步上下文中延迟处理
|
|
216
|
+
|
|
217
|
+
logger.debug(f"懒加载模块 {object.__getattribute__(self, '_module_name')} 同步初始化完成")
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
logger.error(f"懒加载模块 {object.__getattribute__(self, '_module_name')} 同步初始化失败: {e}")
|
|
221
|
+
raise e
|
|
222
|
+
|
|
223
|
+
async def _complete_async_init(self):
|
|
224
|
+
"""
|
|
225
|
+
完成异步初始化部分,用于同步初始化后的异步处理
|
|
226
|
+
|
|
227
|
+
这个方法用于处理 module.load 和事件提交等异步操作
|
|
228
|
+
"""
|
|
229
|
+
if not object.__getattribute__(self, '_initialized'):
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
# 如果是 BaseModule 子类,在初始化后调用 on_load 方法
|
|
234
|
+
if object.__getattribute__(self, '_is_base_module'):
|
|
235
|
+
logger.debug(f"正在异步调用模块 {object.__getattribute__(self, '_module_name')} 的 on_load 方法...")
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
await module.load(object.__getattribute__(self, '_module_name'))
|
|
239
|
+
except Exception as e:
|
|
240
|
+
logger.error(f"异步调用模块 {object.__getattribute__(self, '_module_name')} 的 on_load 方法时出错: {e}")
|
|
241
|
+
|
|
242
|
+
await lifecycle.submit_event(
|
|
243
|
+
"module.init",
|
|
244
|
+
msg=f"模块 {object.__getattribute__(self, '_module_name')} 初始化完毕",
|
|
245
|
+
data={
|
|
246
|
+
"module_name": object.__getattribute__(self, '_module_name'),
|
|
247
|
+
"success": True,
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
logger.debug(f"懒加载模块 {object.__getattribute__(self, '_module_name')} 异步初始化部分完成")
|
|
251
|
+
except Exception as e:
|
|
252
|
+
await lifecycle.submit_event(
|
|
253
|
+
"module.init",
|
|
254
|
+
msg=f"模块初始化失败: {e}",
|
|
255
|
+
data={
|
|
256
|
+
"module_name": object.__getattribute__(self, '_module_name'),
|
|
257
|
+
"success": False,
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
logger.error(f"懒加载模块 {object.__getattribute__(self, '_module_name')} 异步初始化部分失败: {e}")
|
|
261
|
+
|
|
180
262
|
def _ensure_initialized(self) -> None:
|
|
181
263
|
"""
|
|
182
264
|
确保模块已初始化
|
|
@@ -184,15 +266,32 @@ class LazyModule:
|
|
|
184
266
|
:raises LazyLoadError: 当模块未初始化时抛出
|
|
185
267
|
"""
|
|
186
268
|
if not object.__getattribute__(self, '_initialized'):
|
|
187
|
-
#
|
|
269
|
+
# 检查当前是否在异步上下文中
|
|
188
270
|
try:
|
|
189
271
|
loop = asyncio.get_running_loop()
|
|
190
|
-
#
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
272
|
+
# 如果在异步上下文中,我们需要检查模块初始化方法是否需要异步
|
|
273
|
+
init_method = getattr(object.__getattribute__(self, '_module_class'), '__init__', None)
|
|
274
|
+
|
|
275
|
+
# 检查__init__方法是否是协程函数
|
|
276
|
+
if asyncio.iscoroutinefunction(init_method):
|
|
277
|
+
# 对于需要异步初始化的模块,我们只能设置一个标志,提示需要异步初始化
|
|
278
|
+
object.__setattr__(self, '_needs_async_init', True)
|
|
279
|
+
logger.warning(f"模块 {object.__getattribute__(self, '_module_name')} 需要异步初始化,请在异步上下文中调用")
|
|
280
|
+
return
|
|
281
|
+
else:
|
|
282
|
+
# 对于同步初始化的模块,使用同步初始化方式
|
|
283
|
+
self._initialize_sync()
|
|
284
|
+
|
|
285
|
+
# 异步处理需要在初始化后完成的事件
|
|
286
|
+
if object.__getattribute__(self, '_is_base_module'):
|
|
287
|
+
# 调度异步任务来处理 module.load 和事件提交
|
|
288
|
+
try:
|
|
289
|
+
loop = asyncio.get_running_loop()
|
|
290
|
+
loop.create_task(self._complete_async_init())
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.warning(f"无法调度异步初始化任务: {e}")
|
|
194
293
|
except RuntimeError:
|
|
195
|
-
#
|
|
294
|
+
# 没有运行中的事件循环,可以安全地创建新的事件循环
|
|
196
295
|
asyncio.run(self._initialize())
|
|
197
296
|
|
|
198
297
|
def __getattr__(self, name: str) -> Any:
|
|
@@ -204,6 +303,13 @@ class LazyModule:
|
|
|
204
303
|
"""
|
|
205
304
|
logger.debug(f"正在访问懒加载模块 {object.__getattribute__(self, '_module_name')} 的属性 {name}...")
|
|
206
305
|
|
|
306
|
+
# 检查是否需要异步初始化
|
|
307
|
+
if hasattr(self, '_needs_async_init') and object.__getattribute__(self, '_needs_async_init'):
|
|
308
|
+
raise RuntimeError(
|
|
309
|
+
f"模块 {object.__getattribute__(self, '_module_name')} 需要异步初始化,"
|
|
310
|
+
f"请使用 'await sdk.load_module(\"{object.__getattribute__(self, '_module_name')}\")' 来初始化模块"
|
|
311
|
+
)
|
|
312
|
+
|
|
207
313
|
self._ensure_initialized()
|
|
208
314
|
return getattr(object.__getattribute__(self, '_instance'), name)
|
|
209
315
|
|
|
@@ -572,6 +678,19 @@ class ModuleLoader:
|
|
|
572
678
|
|
|
573
679
|
logger.debug(f"检查模块 {module_class.__name__} 是否应该懒加载")
|
|
574
680
|
|
|
681
|
+
# 首先检查全局懒加载配置
|
|
682
|
+
try:
|
|
683
|
+
from .Core._self_config import get_framework_config
|
|
684
|
+
framework_config = get_framework_config()
|
|
685
|
+
global_lazy_loading = framework_config.get("enable_lazy_loading", True)
|
|
686
|
+
|
|
687
|
+
# 如果全局禁用懒加载,则直接返回False
|
|
688
|
+
if not global_lazy_loading:
|
|
689
|
+
logger.debug(f"全局懒加载已禁用,模块 {module_class.__name__} 将立即加载")
|
|
690
|
+
return False
|
|
691
|
+
except Exception as e:
|
|
692
|
+
logger.warning(f"获取框架配置失败: {e},将使用模块默认配置")
|
|
693
|
+
|
|
575
694
|
# 检查模块是否定义了 should_eager_load() 方法
|
|
576
695
|
if hasattr(module_class, "should_eager_load"):
|
|
577
696
|
try:
|
|
@@ -966,9 +1085,19 @@ async def init() -> bool:
|
|
|
966
1085
|
"""
|
|
967
1086
|
if not await _prepare_environment():
|
|
968
1087
|
return False
|
|
969
|
-
|
|
1088
|
+
|
|
970
1089
|
return await ModuleInitializer.init()
|
|
971
1090
|
|
|
1091
|
+
def init_sync() -> bool:
|
|
1092
|
+
"""
|
|
1093
|
+
SDK初始化入口(同步版本)
|
|
1094
|
+
|
|
1095
|
+
用于命令行直接调用,自动在事件循环中运行异步初始化
|
|
1096
|
+
|
|
1097
|
+
:return: bool SDK初始化是否成功
|
|
1098
|
+
"""
|
|
1099
|
+
return asyncio.run(init())
|
|
1100
|
+
|
|
972
1101
|
def init_task() -> asyncio.Task:
|
|
973
1102
|
"""
|
|
974
1103
|
SDK初始化入口,返回Task对象
|
|
@@ -986,6 +1115,7 @@ def init_task() -> asyncio.Task:
|
|
|
986
1115
|
loop = asyncio.new_event_loop()
|
|
987
1116
|
asyncio.set_event_loop(loop)
|
|
988
1117
|
return loop.create_task(_async_init())
|
|
1118
|
+
|
|
989
1119
|
async def uninit() -> bool:
|
|
990
1120
|
"""
|
|
991
1121
|
SDK反初始化
|
|
@@ -1085,14 +1215,28 @@ async def load_module(module_name: str) -> bool:
|
|
|
1085
1215
|
{!--< tips >!--}
|
|
1086
1216
|
1. 可用于手动触发懒加载模块的初始化
|
|
1087
1217
|
2. 如果模块不存在或已加载会返回False
|
|
1218
|
+
3. 对于需要异步初始化的模块,这是唯一的加载方式
|
|
1088
1219
|
{!--< /tips >!--}
|
|
1089
1220
|
"""
|
|
1090
1221
|
try:
|
|
1091
1222
|
module_instance = getattr(sdk, module_name, None)
|
|
1092
1223
|
if isinstance(module_instance, LazyModule):
|
|
1093
|
-
#
|
|
1094
|
-
|
|
1095
|
-
|
|
1224
|
+
# 检查模块是否需要异步初始化
|
|
1225
|
+
if hasattr(module_instance, '_needs_async_init') and object.__getattribute__(module_instance, '_needs_async_init'):
|
|
1226
|
+
# 对于需要异步初始化的模块,执行完整异步初始化
|
|
1227
|
+
await module_instance._initialize()
|
|
1228
|
+
object.__setattr__(module_instance, '_needs_async_init', False) # 清除标志
|
|
1229
|
+
return True
|
|
1230
|
+
# 检查模块是否已经同步初始化但未完成异步部分
|
|
1231
|
+
elif (object.__getattribute__(module_instance, '_initialized') and
|
|
1232
|
+
object.__getattribute__(module_instance, '_is_base_module')):
|
|
1233
|
+
# 如果是BaseModule子类且已同步初始化,只需完成异步部分
|
|
1234
|
+
await module_instance._complete_async_init()
|
|
1235
|
+
return True
|
|
1236
|
+
else:
|
|
1237
|
+
# 触发懒加载模块的完整初始化
|
|
1238
|
+
await module_instance._initialize()
|
|
1239
|
+
return True
|
|
1096
1240
|
elif module_instance is not None:
|
|
1097
1241
|
logger.warning(f"模块 {module_name} 已经加载")
|
|
1098
1242
|
return False
|
ErisPulse/utils/__init__.py
CHANGED
ErisPulse/utils/cli.py
CHANGED
|
@@ -8,58 +8,12 @@ from typing import List, Dict, Optional, Any
|
|
|
8
8
|
from watchdog.observers import Observer
|
|
9
9
|
|
|
10
10
|
# Rich console setup
|
|
11
|
-
from rich.console import Console
|
|
12
11
|
from rich.panel import Panel
|
|
13
12
|
from rich.table import Table
|
|
14
13
|
from rich.prompt import Confirm, Prompt
|
|
15
14
|
from rich.box import SIMPLE
|
|
16
|
-
from rich.theme import Theme
|
|
17
|
-
from rich.highlighter import RegexHighlighter
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
if sys.platform == "win32":
|
|
21
|
-
from colorama import init
|
|
22
|
-
init()
|
|
23
|
-
|
|
24
|
-
class CommandHighlighter(RegexHighlighter):
|
|
25
|
-
"""
|
|
26
|
-
高亮CLI命令和参数
|
|
27
|
-
|
|
28
|
-
{!--< tips >!--}
|
|
29
|
-
使用正则表达式匹配命令行参数和选项
|
|
30
|
-
{!--< /tips >!--}
|
|
31
|
-
"""
|
|
32
|
-
highlights = [
|
|
33
|
-
r"(?P<switch>\-\-?\w+)",
|
|
34
|
-
r"(?P<option>\[\w+\])",
|
|
35
|
-
r"(?P<command>\b\w+\b)",
|
|
36
|
-
]
|
|
37
|
-
|
|
38
|
-
# 主题配置
|
|
39
|
-
theme = Theme({
|
|
40
|
-
"info": "dim cyan",
|
|
41
|
-
"success": "bold green",
|
|
42
|
-
"warning": "bold yellow",
|
|
43
|
-
"error": "bold red",
|
|
44
|
-
"title": "bold magenta",
|
|
45
|
-
"default": "default",
|
|
46
|
-
"progress": "green",
|
|
47
|
-
"progress.remaining": "white",
|
|
48
|
-
"cmd": "bold blue",
|
|
49
|
-
"param": "italic cyan",
|
|
50
|
-
"switch": "bold yellow",
|
|
51
|
-
"module": "bold green",
|
|
52
|
-
"adapter": "bold yellow",
|
|
53
|
-
"cli": "bold magenta",
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
# 全局控制台实例
|
|
57
|
-
console = Console(
|
|
58
|
-
theme=theme,
|
|
59
|
-
color_system="auto",
|
|
60
|
-
force_terminal=True,
|
|
61
|
-
highlighter=CommandHighlighter()
|
|
62
|
-
)
|
|
16
|
+
from .console import console
|
|
63
17
|
|
|
64
18
|
class CLI:
|
|
65
19
|
"""
|
|
@@ -988,7 +942,8 @@ class CLI:
|
|
|
988
942
|
time.sleep(0.5)
|
|
989
943
|
except KeyboardInterrupt:
|
|
990
944
|
console.print("\n[info]正在安全关闭...[/]")
|
|
991
|
-
|
|
945
|
+
_cleanup_adapters()
|
|
946
|
+
_cleanup_modules()
|
|
992
947
|
self._cleanup()
|
|
993
948
|
console.print("[success]已安全退出[/]")
|
|
994
949
|
|
|
@@ -997,7 +952,6 @@ class CLI:
|
|
|
997
952
|
|
|
998
953
|
# 显示欢迎信息
|
|
999
954
|
try:
|
|
1000
|
-
import importlib.metadata
|
|
1001
955
|
version = importlib.metadata.version('ErisPulse')
|
|
1002
956
|
ux.welcome(version)
|
|
1003
957
|
except Exception:
|
|
@@ -1040,7 +994,7 @@ class CLI:
|
|
|
1040
994
|
cli_entries = entry_points.select(group='erispulse.cli')
|
|
1041
995
|
else:
|
|
1042
996
|
cli_entries = entry_points.get('erispulse.cli', [])
|
|
1043
|
-
|
|
997
|
+
|
|
1044
998
|
for entry in cli_entries:
|
|
1045
999
|
if entry.name == args.command:
|
|
1046
1000
|
cli_func = entry.load()
|
|
@@ -1048,8 +1002,14 @@ class CLI:
|
|
|
1048
1002
|
# 创建一个新的解析器来解析第三方命令的参数
|
|
1049
1003
|
subparser = self.parser._subparsers._group_actions[0].choices[args.command]
|
|
1050
1004
|
parsed_args = subparser.parse_args(sys.argv[2:])
|
|
1051
|
-
#
|
|
1052
|
-
parsed_args.func
|
|
1005
|
+
# 调用第三方命令处理函数(支持异步函数)
|
|
1006
|
+
handler_func = parsed_args.func
|
|
1007
|
+
if asyncio.iscoroutinefunction(handler_func):
|
|
1008
|
+
# 异步函数:使用 asyncio.run() 运行
|
|
1009
|
+
asyncio.run(handler_func(parsed_args))
|
|
1010
|
+
else:
|
|
1011
|
+
# 同步函数:直接调用
|
|
1012
|
+
handler_func(parsed_args)
|
|
1053
1013
|
break
|
|
1054
1014
|
|
|
1055
1015
|
except KeyboardInterrupt:
|
|
@@ -1062,41 +1022,76 @@ class CLI:
|
|
|
1062
1022
|
console.print(traceback.format_exc())
|
|
1063
1023
|
self._cleanup()
|
|
1064
1024
|
sys.exit(1)
|
|
1025
|
+
|
|
1026
|
+
def _cleanup_adapters():
|
|
1027
|
+
"""
|
|
1028
|
+
清理适配器资源
|
|
1029
|
+
"""
|
|
1065
1030
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
import threading
|
|
1031
|
+
from ErisPulse import adapter
|
|
1032
|
+
try:
|
|
1033
|
+
import asyncio
|
|
1034
|
+
import threading
|
|
1035
|
+
|
|
1036
|
+
# 检查是否有正在运行的适配器
|
|
1037
|
+
if adapter.list_adapters():
|
|
1074
1038
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
)
|
|
1089
|
-
stop_thread.start()
|
|
1090
|
-
stop_thread.join(timeout=5)
|
|
1091
|
-
else:
|
|
1092
|
-
asyncio.run(adapter.shutdown())
|
|
1093
|
-
except RuntimeError:
|
|
1039
|
+
console.print("[info]正在停止所有适配器...[/]")
|
|
1040
|
+
|
|
1041
|
+
if threading.current_thread() is threading.main_thread():
|
|
1042
|
+
try:
|
|
1043
|
+
loop = asyncio.get_running_loop()
|
|
1044
|
+
if loop.is_running():
|
|
1045
|
+
# 在新线程中运行
|
|
1046
|
+
stop_thread = threading.Thread(
|
|
1047
|
+
target=lambda: asyncio.run(adapter.shutdown())
|
|
1048
|
+
)
|
|
1049
|
+
stop_thread.start()
|
|
1050
|
+
stop_thread.join(timeout=5)
|
|
1051
|
+
else:
|
|
1094
1052
|
asyncio.run(adapter.shutdown())
|
|
1053
|
+
except RuntimeError:
|
|
1054
|
+
asyncio.run(adapter.shutdown())
|
|
1055
|
+
else:
|
|
1056
|
+
new_loop = asyncio.new_event_loop()
|
|
1057
|
+
asyncio.set_event_loop(new_loop)
|
|
1058
|
+
new_loop.run_until_complete(adapter.shutdown())
|
|
1059
|
+
|
|
1060
|
+
console.print("[success]适配器已全部停止[/]")
|
|
1061
|
+
else:
|
|
1062
|
+
console.print("[dim]没有需要停止的适配器[/]")
|
|
1063
|
+
except Exception as e:
|
|
1064
|
+
console.print(f"[error]清理适配器资源时出错: {e}[/]")
|
|
1065
|
+
|
|
1066
|
+
def _cleanup_modules():
|
|
1067
|
+
"""
|
|
1068
|
+
清理模块资源
|
|
1069
|
+
"""
|
|
1070
|
+
from ErisPulse import module
|
|
1071
|
+
try:
|
|
1072
|
+
import asyncio
|
|
1073
|
+
import threading
|
|
1074
|
+
|
|
1075
|
+
console.print("[info]正在卸载所有模块...[/]")
|
|
1076
|
+
|
|
1077
|
+
if threading.current_thread() is threading.main_thread():
|
|
1078
|
+
try:
|
|
1079
|
+
loop = asyncio.get_running_loop()
|
|
1080
|
+
if loop.is_running():
|
|
1081
|
+
stop_thread = threading.Thread(
|
|
1082
|
+
target=lambda: asyncio.run(module.unload())
|
|
1083
|
+
)
|
|
1084
|
+
stop_thread.start()
|
|
1085
|
+
stop_thread.join(timeout=5)
|
|
1095
1086
|
else:
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1087
|
+
asyncio.run(module.unload())
|
|
1088
|
+
except RuntimeError:
|
|
1089
|
+
asyncio.run(module.unload())
|
|
1090
|
+
else:
|
|
1091
|
+
new_loop = asyncio.new_event_loop()
|
|
1092
|
+
asyncio.set_event_loop(new_loop)
|
|
1093
|
+
new_loop.run_until_complete(module.unload())
|
|
1094
|
+
|
|
1095
|
+
console.print("[success]模块已全部卸载[/]")
|
|
1096
|
+
except Exception as e:
|
|
1097
|
+
console.print(f"[error]清理模块资源时出错: {e}[/]")
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.theme import Theme
|
|
4
|
+
from rich.highlighter import RegexHighlighter
|
|
5
|
+
|
|
6
|
+
# 确保在Windows上启用颜色
|
|
7
|
+
if sys.platform == "win32":
|
|
8
|
+
from colorama import init
|
|
9
|
+
init()
|
|
10
|
+
|
|
11
|
+
class CommandHighlighter(RegexHighlighter):
|
|
12
|
+
"""
|
|
13
|
+
高亮CLI命令和参数
|
|
14
|
+
|
|
15
|
+
{!--< tips >!--}
|
|
16
|
+
使用正则表达式匹配命令行参数和选项
|
|
17
|
+
{!--< /tips >!--}
|
|
18
|
+
"""
|
|
19
|
+
highlights = [
|
|
20
|
+
r"(?P<switch>\-\-?\w+)",
|
|
21
|
+
r"(?P<option>\[\w+\])",
|
|
22
|
+
r"(?P<command>\b\w+\b)",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# 主题配置
|
|
26
|
+
theme = Theme({
|
|
27
|
+
"info": "dim cyan",
|
|
28
|
+
"success": "bold green",
|
|
29
|
+
"warning": "bold yellow",
|
|
30
|
+
"error": "bold red",
|
|
31
|
+
"title": "bold magenta",
|
|
32
|
+
"default": "default",
|
|
33
|
+
"progress": "green",
|
|
34
|
+
"progress.remaining": "white",
|
|
35
|
+
"cmd": "bold blue",
|
|
36
|
+
"param": "italic cyan",
|
|
37
|
+
"switch": "bold yellow",
|
|
38
|
+
"module": "bold green",
|
|
39
|
+
"adapter": "bold yellow",
|
|
40
|
+
"cli": "bold magenta",
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
# 全局控制台实例
|
|
44
|
+
console = Console(
|
|
45
|
+
theme=theme,
|
|
46
|
+
color_system="auto",
|
|
47
|
+
force_terminal=True,
|
|
48
|
+
highlighter=CommandHighlighter()
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"console",
|
|
53
|
+
]
|
|
@@ -13,13 +13,11 @@ import sys
|
|
|
13
13
|
import time
|
|
14
14
|
from typing import List, Dict, Tuple, Optional, Any
|
|
15
15
|
|
|
16
|
-
from rich.console import Console
|
|
17
16
|
from rich.panel import Panel
|
|
18
17
|
from rich.progress import Progress, BarColumn, TextColumn
|
|
19
18
|
from rich.prompt import Confirm
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
console = Console()
|
|
20
|
+
from .console import console
|
|
23
21
|
|
|
24
22
|
class PackageManager:
|
|
25
23
|
"""
|
|
@@ -4,18 +4,13 @@ ErisPulse SDK 热重载处理器
|
|
|
4
4
|
实现热重载功能,监控文件变化并重启进程
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
import asyncio
|
|
8
7
|
import os
|
|
9
8
|
import subprocess
|
|
10
9
|
import sys
|
|
11
|
-
import threading
|
|
12
10
|
import time
|
|
13
11
|
from watchdog.events import FileSystemEventHandler
|
|
14
12
|
|
|
15
|
-
from
|
|
16
|
-
|
|
17
|
-
# 全局控制台实例,从CLI模块导入
|
|
18
|
-
console = Console()
|
|
13
|
+
from .console import console
|
|
19
14
|
|
|
20
15
|
|
|
21
16
|
class ReloadHandler(FileSystemEventHandler):
|
|
@@ -101,35 +96,16 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
101
96
|
:param event: 文件系统事件
|
|
102
97
|
:param reason: 重载原因
|
|
103
98
|
"""
|
|
104
|
-
from ErisPulse.Core import
|
|
105
|
-
|
|
99
|
+
from ErisPulse.Core import logger
|
|
100
|
+
|
|
106
101
|
try:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
# 如果在主线程中
|
|
113
|
-
if threading.current_thread() is threading.main_thread():
|
|
114
|
-
try:
|
|
115
|
-
# 在新线程中运行适配器停止
|
|
116
|
-
stop_thread = threading.Thread(target=lambda: asyncio.run(adapter.shutdown()))
|
|
117
|
-
stop_thread.start()
|
|
118
|
-
stop_thread.join(timeout=10) # 最多等待10秒
|
|
119
|
-
except RuntimeError:
|
|
120
|
-
# 没有运行中的事件循环
|
|
121
|
-
asyncio.run(adapter.shutdown())
|
|
122
|
-
else:
|
|
123
|
-
# 在非主线程中,创建新的事件循环
|
|
124
|
-
new_loop = asyncio.new_event_loop()
|
|
125
|
-
asyncio.set_event_loop(new_loop)
|
|
126
|
-
new_loop.run_until_complete(adapter.shutdown())
|
|
127
|
-
|
|
128
|
-
logger.info("适配器已停止")
|
|
102
|
+
from .cli import _cleanup_adapters, _cleanup_modules
|
|
103
|
+
logger.info(f"检测到文件变更 ({reason}),正在关闭适配器和模块...")
|
|
104
|
+
_cleanup_adapters()
|
|
105
|
+
_cleanup_modules()
|
|
129
106
|
except Exception as e:
|
|
130
|
-
logger.warning(f"
|
|
107
|
+
logger.warning(f"关闭适配器和模块时出错: {e}")
|
|
131
108
|
|
|
132
|
-
|
|
133
|
-
logger.info(f"检测到文件变更 ({reason}),正在重启...")
|
|
109
|
+
logger.info("正在重启...")
|
|
134
110
|
self._terminate_process()
|
|
135
|
-
self.start_process()
|
|
111
|
+
self.start_process()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=
|
|
1
|
+
ErisPulse/__init__.py,sha256=u5b96KfFq57kB4I51t_YAU_aLVTftG4rLkAqFmmYwVA,50327
|
|
2
2
|
ErisPulse/__main__.py,sha256=x1VV2mbn1B9RVRhG5uumTCrvZCLzJjGWN0wFll-G0H4,471
|
|
3
3
|
ErisPulse/Core/__init__.py,sha256=9RNwUq9Hbe3r6nA8QBvq2pvMJN4QiBfo71N0u_ywiwI,1398
|
|
4
|
-
ErisPulse/Core/_self_config.py,sha256=
|
|
4
|
+
ErisPulse/Core/_self_config.py,sha256=ReoVy2hYZxG7hPRHXUWRjLClC4nWZ2RG8VWdD-PAM7g,3549
|
|
5
5
|
ErisPulse/Core/adapter.py,sha256=9wpaE9B6LixlGTJ0p341wYe4AE3SVuczToMOWd7UkL4,19707
|
|
6
6
|
ErisPulse/Core/config.py,sha256=cV0R2eUWNECIogYLFbhNu1pYl8HStPG-DNbCe3lkY4E,5873
|
|
7
7
|
ErisPulse/Core/exceptions.py,sha256=tTOOs57SD8aSp4HXomuXtVYgHJCJtfP3AS4JIIq1LPk,3920
|
|
@@ -10,7 +10,7 @@ ErisPulse/Core/logger.py,sha256=nktXT-6cxF50NYsUErMsf8J79brzXg2faaJ4Agt2Gro,1259
|
|
|
10
10
|
ErisPulse/Core/module.py,sha256=13rI03GwEzVd66hIhZnZNybiA9wj7ITzQILi1-nCjhQ,12944
|
|
11
11
|
ErisPulse/Core/router.py,sha256=rtvUjbRHVeTTgWBBG-yxK91oEjs5Tw3ReryqCI-rMYc,12411
|
|
12
12
|
ErisPulse/Core/storage.py,sha256=WbREN-GTpzPls8Vx3t8F2U_lzmCKEsI4OcecWWmvaSk,25238
|
|
13
|
-
ErisPulse/Core/ux.py,sha256=
|
|
13
|
+
ErisPulse/Core/ux.py,sha256=jWC_v8e0Khe1LAg5ksKCVyEgmO2MJGIfmtOAPTorD90,29589
|
|
14
14
|
ErisPulse/Core/Bases/__init__.py,sha256=hHKsI2zQfBdahnEuHlMgzOX2_ygRDOVN8-VqTIrcxP4,230
|
|
15
15
|
ErisPulse/Core/Bases/adapter.py,sha256=aCupQdc8M5Ds7m5dZMCmtoyC-0Zqh5K_6F3uKle8WXE,6490
|
|
16
16
|
ErisPulse/Core/Bases/module.py,sha256=TtzZQ4x4u09S6yDibsIHa9srlGLFPXCHdn2WxZ3nmdo,1154
|
|
@@ -22,12 +22,13 @@ ErisPulse/Core/Event/message.py,sha256=wxg_GJsI1ZvPrV9611xELLxnlk2bcxMJH7EBEkoWw
|
|
|
22
22
|
ErisPulse/Core/Event/meta.py,sha256=0cs0Cg5r58kll3P4lNtnVWAKLQiL6MoXPkbkk6_IEko,3660
|
|
23
23
|
ErisPulse/Core/Event/notice.py,sha256=tU28tc3Ig-FMB2EJUDO2Z9ewfJjzEPh2O2J4lU7GYDk,4557
|
|
24
24
|
ErisPulse/Core/Event/request.py,sha256=rYjFjRJccNRZ6bkY89ig8Q2YntnoytUfg-_PGntJxXg,2956
|
|
25
|
-
ErisPulse/utils/__init__.py,sha256=
|
|
26
|
-
ErisPulse/utils/cli.py,sha256=
|
|
27
|
-
ErisPulse/utils/
|
|
28
|
-
ErisPulse/utils/
|
|
29
|
-
|
|
30
|
-
erispulse-2.3.
|
|
31
|
-
erispulse-2.3.
|
|
32
|
-
erispulse-2.3.
|
|
33
|
-
erispulse-2.3.
|
|
25
|
+
ErisPulse/utils/__init__.py,sha256=_4cDUbJyNebH-VXDUTnfdZKv8Y1MPYgsl-fOM0ZFNuw,299
|
|
26
|
+
ErisPulse/utils/cli.py,sha256=JMI3pOz_DrHj2sSscqhKzFUxGaRfa9Bs8jbvyqkZsy0,41234
|
|
27
|
+
ErisPulse/utils/console.py,sha256=IIo8kVTy0ikEp1H4V6BsokaQp_ISfBFuxlhQcRnk2vs,1145
|
|
28
|
+
ErisPulse/utils/package_manager.py,sha256=uv9Q24Qxa2bbotxLrSvhF-lX6vEmK-4KPCIwPe6ZyGc,32698
|
|
29
|
+
ErisPulse/utils/reload_handler.py,sha256=ToBE9EiXY8ZNNL8jntdsc2Dpy-Wgh73Jd3aBpedKAtg,3369
|
|
30
|
+
erispulse-2.3.2.dist-info/METADATA,sha256=jfR5AVyE-HKrvVXntwTOrCrN0pVpnwHRRS5n3lUpjao,7473
|
|
31
|
+
erispulse-2.3.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
32
|
+
erispulse-2.3.2.dist-info/entry_points.txt,sha256=NiOwT6-XQ7KIH1r6J8odjRO-uaKHfr_Vz_UIG96EWXg,187
|
|
33
|
+
erispulse-2.3.2.dist-info/licenses/LICENSE,sha256=tJjKWuY4OPWNgriiixWLvmFb8Pf8p_-4NMq_zZN3gWg,1858
|
|
34
|
+
erispulse-2.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|