ErisPulse 2.4.6.dev2__tar.gz → 2.4.6.dev4__tar.gz
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-2.4.6.dev2 → erispulse-2.4.6.dev4}/PKG-INFO +1 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/pyproject.toml +1 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/run.py +31 -9
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/run.pyi +3 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/base.py +7 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/base.pyi +2 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/command.py +1 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/adapter.py +92 -26
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/adapter.pyi +4 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/constants.py +5 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/lifecycle.py +8 -2
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/lifecycle.pyi +1 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/module.py +12 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/adapter.py +16 -2
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/module.py +35 -1
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/sdk.py +34 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/sdk.pyi +19 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/.gitignore +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/LICENSE +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/README.md +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/base.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/cli.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/cli.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/create.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/create.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/init.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/init.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/install.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/install.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/self_update.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/console.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/registry.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/display.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/display.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/adapter.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/module.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/command.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/meta.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/notice.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/request.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/wrapper.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/wrapper.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/config.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/config.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/constants.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/logger.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/logger.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/module.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/router.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/router.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/storage.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/storage.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__main__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/adapter.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/finder.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/module.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/adapter.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/module.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/strategy.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/context.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/exceptions.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/frame_config.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/frame_config.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/4xx.png +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/5xx.png +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/__init__.py +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/__init__.pyi +0 -0
- {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/unknow.png +0 -0
|
@@ -116,28 +116,50 @@ class RunCommand(Command):
|
|
|
116
116
|
else:
|
|
117
117
|
self._run_internal(reload_mode)
|
|
118
118
|
|
|
119
|
+
_RESTART_EXIT_CODE = 42
|
|
120
|
+
|
|
119
121
|
def _run_internal(self, reload_mode: bool):
|
|
120
122
|
"""
|
|
121
123
|
直接运行 SDK(不指定脚本时)
|
|
124
|
+
|
|
125
|
+
以子进程方式运行 SDK,支持硬重启:当 SDK 进程以特定退出码退出时,
|
|
126
|
+
自动重新启动新进程,确保资源完全释放。
|
|
122
127
|
"""
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
if reload_mode:
|
|
130
|
+
async def _run():
|
|
131
|
+
from ... import sdk
|
|
126
132
|
|
|
127
|
-
if reload_mode:
|
|
128
133
|
loop = asyncio.get_running_loop()
|
|
129
134
|
self._setup_watchdog(".", loop)
|
|
135
|
+
await sdk.run(keep_running=True)
|
|
130
136
|
|
|
131
|
-
|
|
137
|
+
try:
|
|
138
|
+
asyncio.run(_run())
|
|
139
|
+
except KeyboardInterrupt:
|
|
140
|
+
pass
|
|
141
|
+
finally:
|
|
142
|
+
if hasattr(self, "_observer"):
|
|
143
|
+
self._observer.stop()
|
|
144
|
+
self._observer.join()
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
cmd = [sys.executable, "-c",
|
|
148
|
+
"import asyncio; from ErisPulse import sdk; "
|
|
149
|
+
"asyncio.run(sdk.run(keep_running=True))"]
|
|
132
150
|
|
|
133
151
|
try:
|
|
134
|
-
|
|
152
|
+
while True:
|
|
153
|
+
process = subprocess.Popen(cmd)
|
|
154
|
+
process.wait()
|
|
155
|
+
|
|
156
|
+
if process.returncode == self._RESTART_EXIT_CODE:
|
|
157
|
+
console.print("[info]收到硬重启请求,正在重新启动...[/]")
|
|
158
|
+
time.sleep(0.5)
|
|
159
|
+
continue
|
|
160
|
+
break
|
|
135
161
|
except KeyboardInterrupt:
|
|
136
162
|
pass
|
|
137
|
-
finally:
|
|
138
|
-
if reload_mode and hasattr(self, "_observer"):
|
|
139
|
-
self._observer.stop()
|
|
140
|
-
self._observer.join()
|
|
141
163
|
|
|
142
164
|
def _run_script(self, script_path: str, reload_mode: bool):
|
|
143
165
|
script_path_abs = os.path.abspath(script_path)
|
|
@@ -53,6 +53,9 @@ class RunCommand(Command):
|
|
|
53
53
|
def _run_internal(self: object, reload_mode: bool) -> ...:
|
|
54
54
|
"""
|
|
55
55
|
直接运行 SDK(不指定脚本时)
|
|
56
|
+
|
|
57
|
+
以子进程方式运行 SDK,支持硬重启:当 SDK 进程以特定退出码退出时,
|
|
58
|
+
自动重新启动新进程,确保资源完全释放。
|
|
56
59
|
"""
|
|
57
60
|
...
|
|
58
61
|
def _run_script(self: object, script_path: str, reload_mode: bool) -> ...:
|
|
@@ -12,11 +12,12 @@ ErisPulse 事件处理基础模块
|
|
|
12
12
|
from .. import adapter, logger
|
|
13
13
|
from ...runtime import get_event_config
|
|
14
14
|
from ...runtime.context import current_owner
|
|
15
|
-
from ..constants import DEFAULT_HANDLER_PRIORITY, UNKNOWN_PLATFORM, EVENT_TYPE_MESSAGE
|
|
15
|
+
from ..constants import DEFAULT_HANDLER_PRIORITY, UNKNOWN_PLATFORM, EVENT_TYPE_MESSAGE, HANDLER_SLOW_THRESHOLD_SECS
|
|
16
16
|
from typing import Any
|
|
17
17
|
from collections.abc import Callable
|
|
18
18
|
import asyncio
|
|
19
19
|
import inspect
|
|
20
|
+
import time as _time
|
|
20
21
|
from itertools import groupby
|
|
21
22
|
from .wrapper import Event
|
|
22
23
|
from ..lifecycle import lifecycle
|
|
@@ -34,11 +35,16 @@ async def _invoke_handler(handler_info: dict, event: Event) -> None:
|
|
|
34
35
|
:param event: 事件对象
|
|
35
36
|
"""
|
|
36
37
|
handler = handler_info["func"]
|
|
38
|
+
_hname = getattr(handler, "__qualname__", getattr(handler, "__name__", str(handler)))
|
|
37
39
|
try:
|
|
40
|
+
_t = _time.monotonic()
|
|
38
41
|
if inspect.iscoroutinefunction(handler):
|
|
39
42
|
await handler(event)
|
|
40
43
|
else:
|
|
41
44
|
handler(event)
|
|
45
|
+
_elapsed = _time.monotonic() - _t
|
|
46
|
+
if _elapsed > HANDLER_SLOW_THRESHOLD_SECS:
|
|
47
|
+
logger.warning(f"[EventHandler] Slow handler {_hname} took {_elapsed:.4f}s")
|
|
42
48
|
except Exception as e:
|
|
43
49
|
logger.error(f"事件处理器执行错误: {e}")
|
|
44
50
|
|
|
@@ -18,11 +18,12 @@ ErisPulse 事件处理基础模块
|
|
|
18
18
|
from .. import adapter, logger
|
|
19
19
|
from ...runtime import get_event_config
|
|
20
20
|
from ...runtime.context import current_owner
|
|
21
|
-
from ..constants import DEFAULT_HANDLER_PRIORITY, UNKNOWN_PLATFORM, EVENT_TYPE_MESSAGE
|
|
21
|
+
from ..constants import DEFAULT_HANDLER_PRIORITY, UNKNOWN_PLATFORM, EVENT_TYPE_MESSAGE, HANDLER_SLOW_THRESHOLD_SECS
|
|
22
22
|
from typing import Any
|
|
23
23
|
from collections.abc import Callable
|
|
24
24
|
import asyncio
|
|
25
25
|
import inspect
|
|
26
|
+
import time as _time
|
|
26
27
|
from itertools import groupby
|
|
27
28
|
from .wrapper import Event
|
|
28
29
|
from ..lifecycle import lifecycle
|
|
@@ -23,6 +23,7 @@ from .constants import (
|
|
|
23
23
|
CONFIG_KEY_ADAPTER_STATUS,
|
|
24
24
|
CONFIG_KEY_ADAPTER_STATUS_OF,
|
|
25
25
|
DEFAULT_ADAPTER_ENABLED,
|
|
26
|
+
HANDLER_SLOW_THRESHOLD_SECS,
|
|
26
27
|
)
|
|
27
28
|
|
|
28
29
|
|
|
@@ -134,24 +135,32 @@ class AdapterManager(ManagerBase):
|
|
|
134
135
|
if existing_instance is not None:
|
|
135
136
|
self._adapters[platform] = existing_instance
|
|
136
137
|
else:
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
138
|
+
try:
|
|
139
|
+
# 创建适配器实例
|
|
140
|
+
# 检查适配器类 __init__ 方法的参数
|
|
141
|
+
init_signature = inspect.signature(adapter_class.__init__)
|
|
142
|
+
params = [p for p in init_signature.parameters.values() if p.name != "self"]
|
|
143
|
+
|
|
144
|
+
sdk_to_use = self._sdk
|
|
145
|
+
if sdk_to_use is None:
|
|
146
|
+
from .. import sdk
|
|
147
|
+
|
|
148
|
+
sdk_to_use = sdk
|
|
149
|
+
|
|
150
|
+
# 根据参数情况创建实例
|
|
151
|
+
if params:
|
|
152
|
+
instance = adapter_class(sdk_to_use)
|
|
153
|
+
else:
|
|
154
|
+
instance = adapter_class()
|
|
155
|
+
|
|
156
|
+
self._adapters[platform] = instance
|
|
157
|
+
except SystemExit as e:
|
|
158
|
+
logger.error(f"适配器 {platform} 尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
159
|
+
f"请不要在适配器中使用 sys.exit() 或 raise SystemExit")
|
|
160
|
+
return False
|
|
161
|
+
except Exception as e:
|
|
162
|
+
logger.error(f"创建适配器实例 {platform} 失败: {e}")
|
|
163
|
+
return False
|
|
155
164
|
|
|
156
165
|
return True
|
|
157
166
|
|
|
@@ -685,6 +694,9 @@ class AdapterManager(ManagerBase):
|
|
|
685
694
|
"""
|
|
686
695
|
提交OneBot12协议事件到指定平台
|
|
687
696
|
|
|
697
|
+
每个事件处理器(handler)都在独立的 asyncio.Task 中执行,
|
|
698
|
+
单个处理器阻塞不会影响框架的事件分发和其他处理器运行。
|
|
699
|
+
|
|
688
700
|
:param data: 符合OneBot12标准的事件数据
|
|
689
701
|
|
|
690
702
|
:example:
|
|
@@ -766,7 +778,7 @@ class AdapterManager(ManagerBase):
|
|
|
766
778
|
},
|
|
767
779
|
)
|
|
768
780
|
|
|
769
|
-
# 先执行OneBot12
|
|
781
|
+
# 先执行OneBot12中间件(中间件可以修改数据,必须顺序执行)
|
|
770
782
|
processed_data = data
|
|
771
783
|
for middleware in self._onebot_middlewares:
|
|
772
784
|
result = await middleware(processed_data)
|
|
@@ -777,7 +789,7 @@ class AdapterManager(ManagerBase):
|
|
|
777
789
|
f"中间件 {middleware.__qualname__} 返回 None,已忽略并保留原数据"
|
|
778
790
|
)
|
|
779
791
|
|
|
780
|
-
# 分发到OneBot12
|
|
792
|
+
# 分发到OneBot12事件处理器(每个 handler 在独立 Task 中执行,不阻塞框架)
|
|
781
793
|
handlers_to_call = []
|
|
782
794
|
|
|
783
795
|
# 处理特定事件类型的处理器
|
|
@@ -787,12 +799,14 @@ class AdapterManager(ManagerBase):
|
|
|
787
799
|
# 处理通配符处理器
|
|
788
800
|
handlers_to_call.extend(self._onebot_handlers.get("*", []))
|
|
789
801
|
|
|
790
|
-
#
|
|
802
|
+
# 将符合条件的处理器分发到独立 Task
|
|
791
803
|
for handler_wrapper in handlers_to_call:
|
|
792
804
|
handler_platform = handler_wrapper.get("platform")
|
|
793
|
-
# 如果处理器没有指定平台,或者指定的平台与当前事件平台匹配
|
|
794
805
|
if handler_platform is None or handler_platform == platform:
|
|
795
|
-
|
|
806
|
+
self._dispatch_handler_task(
|
|
807
|
+
handler_wrapper["func"], processed_data,
|
|
808
|
+
event_type=event_type, platform=platform,
|
|
809
|
+
)
|
|
796
810
|
|
|
797
811
|
# 只有当存在原生事件数据时才分发原生事件
|
|
798
812
|
if raw_event_type and (platform_raw := data.get(f"{platform}_raw")) is not None:
|
|
@@ -805,12 +819,14 @@ class AdapterManager(ManagerBase):
|
|
|
805
819
|
# 处理原生事件的通配符处理器
|
|
806
820
|
raw_handlers_to_call.extend(self._raw_handlers.get("*", []))
|
|
807
821
|
|
|
808
|
-
#
|
|
822
|
+
# 将符合条件的原生事件处理器分发到独立 Task
|
|
809
823
|
for handler_wrapper in raw_handlers_to_call:
|
|
810
824
|
handler_platform = handler_wrapper.get("platform")
|
|
811
|
-
# 如果处理器没有指定平台,或者指定的平台与当前事件平台匹配
|
|
812
825
|
if handler_platform is None or handler_platform == platform:
|
|
813
|
-
|
|
826
|
+
self._dispatch_handler_task(
|
|
827
|
+
handler_wrapper["func"], platform_raw,
|
|
828
|
+
event_type=raw_event_type, platform=platform,
|
|
829
|
+
)
|
|
814
830
|
|
|
815
831
|
# 钩子: 事件分发完成
|
|
816
832
|
await lifecycle.emit("adapter.event.dispatched", {
|
|
@@ -820,6 +836,56 @@ class AdapterManager(ManagerBase):
|
|
|
820
836
|
"onebot_handlers_count": len(handlers_to_call),
|
|
821
837
|
})
|
|
822
838
|
|
|
839
|
+
def _dispatch_handler_task(
|
|
840
|
+
self,
|
|
841
|
+
func: Callable,
|
|
842
|
+
data: Any,
|
|
843
|
+
*,
|
|
844
|
+
event_type: str = "unknown",
|
|
845
|
+
platform: str = "unknown",
|
|
846
|
+
) -> asyncio.Task:
|
|
847
|
+
"""
|
|
848
|
+
{!--< internal-use >!--}
|
|
849
|
+
将事件处理器包装为独立 asyncio.Task 并调度执行
|
|
850
|
+
|
|
851
|
+
处理器在独立 Task 中运行,不会阻塞 adapter.emit() 的后续流程。
|
|
852
|
+
自动捕获处理器异常并记录日志,同时监控处理器执行耗时。
|
|
853
|
+
|
|
854
|
+
:param func: 事件处理器函数
|
|
855
|
+
:param data: 事件数据
|
|
856
|
+
:param event_type: 事件类型(用于日志)
|
|
857
|
+
:param platform: 平台名称(用于日志)
|
|
858
|
+
:return: asyncio.Task
|
|
859
|
+
"""
|
|
860
|
+
import time as _time
|
|
861
|
+
|
|
862
|
+
_func_name = getattr(func, "__qualname__", getattr(func, "__name__", str(func)))
|
|
863
|
+
|
|
864
|
+
async def _safe_run():
|
|
865
|
+
t0 = _time.monotonic()
|
|
866
|
+
try:
|
|
867
|
+
await func(data)
|
|
868
|
+
except asyncio.CancelledError:
|
|
869
|
+
pass
|
|
870
|
+
except Exception as e:
|
|
871
|
+
logger.error(
|
|
872
|
+
f"事件处理器执行错误 [{_func_name}] "
|
|
873
|
+
f"type={event_type} platform={platform}: {e}"
|
|
874
|
+
)
|
|
875
|
+
finally:
|
|
876
|
+
elapsed = _time.monotonic() - t0
|
|
877
|
+
if elapsed > HANDLER_SLOW_THRESHOLD_SECS:
|
|
878
|
+
logger.warning(
|
|
879
|
+
f"事件处理器执行缓慢 [{_func_name}] "
|
|
880
|
+
f"耗时 {elapsed:.2f}s > {HANDLER_SLOW_THRESHOLD_SECS}s "
|
|
881
|
+
f"type={event_type} platform={platform}"
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
try:
|
|
885
|
+
return asyncio.create_task(_safe_run())
|
|
886
|
+
except RuntimeError:
|
|
887
|
+
return asyncio.ensure_future(_safe_run())
|
|
888
|
+
|
|
823
889
|
# ==================== Bot状态管理 ====================
|
|
824
890
|
|
|
825
891
|
def _auto_register_bot(self, platform: str, self_info: dict) -> bool:
|
|
@@ -23,7 +23,7 @@ from .Bases.adapter import BaseAdapter
|
|
|
23
23
|
from .config import config
|
|
24
24
|
from .lifecycle import lifecycle
|
|
25
25
|
from .Bases.manager import ManagerBase
|
|
26
|
-
from .constants import ADAPTER_RETRY_BACKOFF_INTERVALS, ADAPTER_RETRY_FIXED_DELAY_SECS, CONFIG_KEY_ADAPTER_STATUS, CONFIG_KEY_ADAPTER_STATUS_OF, DEFAULT_ADAPTER_ENABLED
|
|
26
|
+
from .constants import ADAPTER_RETRY_BACKOFF_INTERVALS, ADAPTER_RETRY_FIXED_DELAY_SECS, CONFIG_KEY_ADAPTER_STATUS, CONFIG_KEY_ADAPTER_STATUS_OF, DEFAULT_ADAPTER_ENABLED, HANDLER_SLOW_THRESHOLD_SECS
|
|
27
27
|
|
|
28
28
|
class AdapterManager(ManagerBase):
|
|
29
29
|
"""
|
|
@@ -218,6 +218,9 @@ class AdapterManager(ManagerBase):
|
|
|
218
218
|
"""
|
|
219
219
|
提交OneBot12协议事件到指定平台
|
|
220
220
|
|
|
221
|
+
每个事件处理器(handler)都在独立的 asyncio.Task 中执行,
|
|
222
|
+
单个处理器阻塞不会影响框架的事件分发和其他处理器运行。
|
|
223
|
+
|
|
221
224
|
:param data: 符合OneBot12标准的事件数据
|
|
222
225
|
|
|
223
226
|
:example:
|
|
@@ -308,6 +308,11 @@ DEFAULT_WAIT_TIMEOUT_SECS = 60.0
|
|
|
308
308
|
# 修改影响: 验证器拒绝回复后的重试次数。
|
|
309
309
|
DEFAULT_MAX_RETRIES = 3
|
|
310
310
|
|
|
311
|
+
# 事件处理器执行耗时警告阈值(秒)。
|
|
312
|
+
# 使用位置: Core/adapter.py -> emit() 中的 handler 执行监控。
|
|
313
|
+
# 修改影响: 当单个处理器执行超过此时间时记录 WARNING 日志。
|
|
314
|
+
HANDLER_SLOW_THRESHOLD_SECS = 1.0
|
|
315
|
+
|
|
311
316
|
# 平台标识的回退值。
|
|
312
317
|
# 当事件数据缺少 platform 字段时使用。
|
|
313
318
|
# 修改影响: 日志和事件处理中的平台标识显示。
|
|
@@ -13,7 +13,7 @@ ErisPulse 生命周期管理模块
|
|
|
13
13
|
|
|
14
14
|
import asyncio
|
|
15
15
|
import inspect
|
|
16
|
-
from .constants import DEFAULT_EVENT_SOURCE
|
|
16
|
+
from .constants import DEFAULT_EVENT_SOURCE, HANDLER_SLOW_THRESHOLD_SECS
|
|
17
17
|
import time
|
|
18
18
|
from typing import Any
|
|
19
19
|
from collections.abc import Callable
|
|
@@ -307,12 +307,18 @@ class LifecycleManager:
|
|
|
307
307
|
:param data: Any 事件数据
|
|
308
308
|
:return: Any 处理后的数据
|
|
309
309
|
"""
|
|
310
|
-
|
|
310
|
+
import time as _time
|
|
311
|
+
for priority, handler in self._hooks[hook_name]:
|
|
311
312
|
try:
|
|
313
|
+
_t = _time.monotonic()
|
|
314
|
+
_hname = getattr(handler, "__qualname__", getattr(handler, "__name__", str(handler)))
|
|
312
315
|
if inspect.iscoroutinefunction(handler):
|
|
313
316
|
result = await handler(data)
|
|
314
317
|
else:
|
|
315
318
|
result = handler(data)
|
|
319
|
+
_elapsed = _time.monotonic() - _t
|
|
320
|
+
if _elapsed > HANDLER_SLOW_THRESHOLD_SECS:
|
|
321
|
+
_get_logger().warning(f"[Lifecycle] Slow handler {_hname} for event '{event}' took {_elapsed:.4f}s")
|
|
316
322
|
if result is not None:
|
|
317
323
|
data = result
|
|
318
324
|
except Exception as e:
|
|
@@ -19,7 +19,7 @@ ErisPulse 生命周期管理模块
|
|
|
19
19
|
|
|
20
20
|
import asyncio
|
|
21
21
|
import inspect
|
|
22
|
-
from .constants import DEFAULT_EVENT_SOURCE
|
|
22
|
+
from .constants import DEFAULT_EVENT_SOURCE, HANDLER_SLOW_THRESHOLD_SECS
|
|
23
23
|
import time
|
|
24
24
|
from typing import Any
|
|
25
25
|
from collections.abc import Callable
|
|
@@ -203,6 +203,18 @@ class ModuleManager(ManagerBase):
|
|
|
203
203
|
logger.info(f"模块 {module_name} 加载成功")
|
|
204
204
|
return True
|
|
205
205
|
|
|
206
|
+
except SystemExit as e:
|
|
207
|
+
await lifecycle.submit_event(
|
|
208
|
+
"module.load",
|
|
209
|
+
data={
|
|
210
|
+
"module_name": module_name,
|
|
211
|
+
"success": False,
|
|
212
|
+
},
|
|
213
|
+
msg=f"模块 {module_name} 尝试退出进程 (SystemExit)",
|
|
214
|
+
)
|
|
215
|
+
logger.error(f"模块 {module_name} 尝试退出进程 (SystemExit({e.code})),已跳过该模块。"
|
|
216
|
+
f"请不要在模块中使用 sys.exit() 或 raise SystemExit,请改用 raise RuntimeError 或返回错误")
|
|
217
|
+
return False
|
|
206
218
|
except Exception as e:
|
|
207
219
|
await lifecycle.submit_event(
|
|
208
220
|
"module.load",
|
|
@@ -91,8 +91,10 @@ class AdapterLoader(BaseLoader):
|
|
|
91
91
|
|
|
92
92
|
logger.print_section_separator()
|
|
93
93
|
|
|
94
|
-
except
|
|
94
|
+
except BaseException as e:
|
|
95
95
|
logger.error(f"加载 {group_name} entry-points 失败: {e}")
|
|
96
|
+
if isinstance(e, SystemExit):
|
|
97
|
+
logger.warning(f"拦截到 SystemExit,已阻止进程退出,跳过后续适配器加载")
|
|
96
98
|
|
|
97
99
|
return objs, enabled_list, disabled_list
|
|
98
100
|
|
|
@@ -172,6 +174,9 @@ class AdapterLoader(BaseLoader):
|
|
|
172
174
|
objs[meta_name] = adapter_obj
|
|
173
175
|
enabled_list.append(meta_name)
|
|
174
176
|
|
|
177
|
+
except SystemExit as e:
|
|
178
|
+
logger.error(f"加载适配器 {meta_name} 时触发 SystemExit({e.code}),已跳过。"
|
|
179
|
+
f"请不要在适配器中使用 sys.exit() 或 raise SystemExit")
|
|
175
180
|
except Exception as e:
|
|
176
181
|
logger.error(f"加载适配器 {meta_name} 失败,已跳过: {e}")
|
|
177
182
|
|
|
@@ -224,6 +229,15 @@ class AdapterLoader(BaseLoader):
|
|
|
224
229
|
data={"platform": platform, "success": True},
|
|
225
230
|
)
|
|
226
231
|
return success
|
|
232
|
+
except SystemExit as e:
|
|
233
|
+
logger.error(f"适配器 {name} 注册时尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
234
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
235
|
+
await lifecycle.submit_event(
|
|
236
|
+
"adapter.load",
|
|
237
|
+
msg=f"适配器 {name} 注册时触发 SystemExit",
|
|
238
|
+
data={"platform": name, "success": False},
|
|
239
|
+
)
|
|
240
|
+
return False
|
|
227
241
|
except Exception as e:
|
|
228
242
|
logger.error(f"适配器 {name} 注册失败: {e}")
|
|
229
243
|
# 提交适配器加载失败事件
|
|
@@ -243,7 +257,7 @@ class AdapterLoader(BaseLoader):
|
|
|
243
257
|
failed_adapters = []
|
|
244
258
|
for i, result in enumerate(register_results):
|
|
245
259
|
adapter_name = adapters[i]
|
|
246
|
-
if isinstance(result,
|
|
260
|
+
if isinstance(result, BaseException) or result is False:
|
|
247
261
|
logger.warning(f"适配器 {adapter_name} 注册失败,已跳过")
|
|
248
262
|
failed_adapters.append(adapter_name)
|
|
249
263
|
|
|
@@ -93,6 +93,9 @@ class ModuleLoader(BaseLoader):
|
|
|
93
93
|
|
|
94
94
|
logger.print_section_separator()
|
|
95
95
|
|
|
96
|
+
except SystemExit as e:
|
|
97
|
+
logger.error(f"加载 {group_name} 时触发 SystemExit({e.code}),已阻止进程退出。"
|
|
98
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
96
99
|
except Exception as e:
|
|
97
100
|
logger.error(f"加载 {group_name} entry-points 失败: {e}")
|
|
98
101
|
|
|
@@ -191,6 +194,9 @@ class ModuleLoader(BaseLoader):
|
|
|
191
194
|
objs[meta_name] = module_obj
|
|
192
195
|
enabled_list.append(meta_name)
|
|
193
196
|
|
|
197
|
+
except SystemExit as e:
|
|
198
|
+
logger.error(f"加载模块 {meta_name} 时尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
199
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
194
200
|
except Exception as e:
|
|
195
201
|
logger.error(f"从 entry-point 加载模块 {meta_name} 失败,已跳过: {e}")
|
|
196
202
|
|
|
@@ -355,6 +361,10 @@ class ModuleLoader(BaseLoader):
|
|
|
355
361
|
manager_instance.register(name, module_class, obj.moduleInfo)
|
|
356
362
|
return True
|
|
357
363
|
return False
|
|
364
|
+
except SystemExit as e:
|
|
365
|
+
logger.error(f"注册模块 {name} 时尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
366
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
367
|
+
return False
|
|
358
368
|
except Exception as e:
|
|
359
369
|
logger.error(f"注册模块 {name} 失败: {e}")
|
|
360
370
|
return False
|
|
@@ -368,7 +378,7 @@ class ModuleLoader(BaseLoader):
|
|
|
368
378
|
failed_modules = []
|
|
369
379
|
for i, result in enumerate(register_results):
|
|
370
380
|
module_name = modules[i]
|
|
371
|
-
if isinstance(result,
|
|
381
|
+
if isinstance(result, BaseException) or result is False:
|
|
372
382
|
logger.warning(f"模块 {module_name} 注册失败,已跳过")
|
|
373
383
|
failed_modules.append(module_name)
|
|
374
384
|
|
|
@@ -527,6 +537,9 @@ class ModuleLoader(BaseLoader):
|
|
|
527
537
|
logger.debug(f"挂载立即加载模块到 sdk: {meta_name}")
|
|
528
538
|
else:
|
|
529
539
|
logger.warning(f"立即加载模块 {meta_name} 失败,已跳过")
|
|
540
|
+
except SystemExit as e:
|
|
541
|
+
logger.warning(f"初始化模块 {meta_name} 时尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
542
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
530
543
|
except Exception as e:
|
|
531
544
|
logger.warning(f"初始化模块 {meta_name} 失败,已跳过: {e}")
|
|
532
545
|
|
|
@@ -655,6 +668,17 @@ class LazyModule:
|
|
|
655
668
|
f"懒加载模块 {object.__getattribute__(self, '_module_name')} 初始化完成"
|
|
656
669
|
)
|
|
657
670
|
|
|
671
|
+
except SystemExit as e:
|
|
672
|
+
module_name = object.__getattribute__(self, "_module_name")
|
|
673
|
+
await lifecycle.submit_event(
|
|
674
|
+
"module.init",
|
|
675
|
+
msg=f"模块 {module_name} 尝试退出进程 (SystemExit)",
|
|
676
|
+
data={"module_name": module_name, "success": False},
|
|
677
|
+
)
|
|
678
|
+
logger.error(f"懒加载模块 {module_name} 尝试退出进程 (SystemExit({e.code})),已跳过。"
|
|
679
|
+
f"请不要使用 sys.exit() 或 raise SystemExit")
|
|
680
|
+
object.__setattr__(self, "_initialized", False)
|
|
681
|
+
object.__setattr__(self, "_init_failed", True)
|
|
658
682
|
except Exception as e:
|
|
659
683
|
await lifecycle.submit_event(
|
|
660
684
|
"module.init",
|
|
@@ -730,6 +754,9 @@ class LazyModule:
|
|
|
730
754
|
new_loop = asyncio.new_event_loop()
|
|
731
755
|
try:
|
|
732
756
|
new_loop.run_until_complete(self._initialize())
|
|
757
|
+
except SystemExit as e:
|
|
758
|
+
init_error[0] = e
|
|
759
|
+
object.__setattr__(self, "_init_failed", True)
|
|
733
760
|
except Exception as e:
|
|
734
761
|
init_error[0] = e
|
|
735
762
|
object.__setattr__(self, "_init_failed", True)
|
|
@@ -784,6 +811,13 @@ class LazyModule:
|
|
|
784
811
|
f"懒加载模块 {object.__getattribute__(self, '_module_name')} 同步初始化完成"
|
|
785
812
|
)
|
|
786
813
|
|
|
814
|
+
except SystemExit as e:
|
|
815
|
+
logger.error(
|
|
816
|
+
f"懒加载模块 {object.__getattribute__(self, '_module_name')} 尝试退出进程 "
|
|
817
|
+
f"(SystemExit({e.code})),已跳过。请不要使用 sys.exit() 或 raise SystemExit"
|
|
818
|
+
)
|
|
819
|
+
object.__setattr__(self, "_initialized", False)
|
|
820
|
+
object.__setattr__(self, "_init_failed", True)
|
|
787
821
|
except Exception as e:
|
|
788
822
|
logger.error(
|
|
789
823
|
f"懒加载模块 {object.__getattribute__(self, '_module_name')} 同步初始化失败: {e}"
|
|
@@ -15,6 +15,7 @@ from __future__ import annotations
|
|
|
15
15
|
|
|
16
16
|
import asyncio
|
|
17
17
|
import importlib
|
|
18
|
+
import os
|
|
18
19
|
import sys
|
|
19
20
|
from typing import TYPE_CHECKING
|
|
20
21
|
from .Core.constants import UNINIT_SETTLE_DELAY_SECS, LIFECYCLE_TIMER_CORE_INIT, LIFECYCLE_TIMER_CORE_UNINIT
|
|
@@ -971,6 +972,39 @@ class SDK:
|
|
|
971
972
|
|
|
972
973
|
return True
|
|
973
974
|
|
|
975
|
+
RESTART_EXIT_CODE = 42
|
|
976
|
+
|
|
977
|
+
async def hard_restart(self) -> bool:
|
|
978
|
+
"""
|
|
979
|
+
硬重启:反初始化后退出进程,由父进程(run.py)重新启动新实例
|
|
980
|
+
|
|
981
|
+
与 restart()(热重启)的区别:
|
|
982
|
+
- restart(): 在同一进程内反初始化再重新初始化
|
|
983
|
+
- hard_restart(): 反初始化后退出进程,由父进程重新启动全新进程
|
|
984
|
+
|
|
985
|
+
确保资源完全释放
|
|
986
|
+
|
|
987
|
+
需要通过 epsdk run 启动才生效,否则进程退出后不会自动重启。
|
|
988
|
+
|
|
989
|
+
:return: bool 硬重启任务是否成功调度
|
|
990
|
+
|
|
991
|
+
:example:
|
|
992
|
+
>>> await sdk.hard_restart()
|
|
993
|
+
"""
|
|
994
|
+
|
|
995
|
+
async def _do_hard_restart():
|
|
996
|
+
await asyncio.sleep(0.5)
|
|
997
|
+
try:
|
|
998
|
+
self.logger.info("[HardRestart] 开始硬重启,执行反初始化...")
|
|
999
|
+
await self.uninit()
|
|
1000
|
+
self.logger.info("[HardRestart] 反初始化完成,正在退出进程...")
|
|
1001
|
+
except Exception as e:
|
|
1002
|
+
self.logger.error(f"[HardRestart] 反初始化异常: {e}")
|
|
1003
|
+
os._exit(self.RESTART_EXIT_CODE)
|
|
1004
|
+
|
|
1005
|
+
asyncio.ensure_future(_do_hard_restart())
|
|
1006
|
+
return True
|
|
1007
|
+
|
|
974
1008
|
async def uninit(self) -> bool:
|
|
975
1009
|
"""
|
|
976
1010
|
SDK 反初始化
|
|
@@ -20,6 +20,7 @@ example:
|
|
|
20
20
|
from __future__ import annotations
|
|
21
21
|
import asyncio
|
|
22
22
|
import importlib
|
|
23
|
+
import os
|
|
23
24
|
import sys
|
|
24
25
|
from typing import TYPE_CHECKING
|
|
25
26
|
from .Core.constants import UNINIT_SETTLE_DELAY_SECS, LIFECYCLE_TIMER_CORE_INIT, LIFECYCLE_TIMER_CORE_UNINIT
|
|
@@ -170,6 +171,24 @@ class SDK:
|
|
|
170
171
|
>>> await sdk.restart()
|
|
171
172
|
"""
|
|
172
173
|
...
|
|
174
|
+
async def hard_restart(self: object) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
硬重启:反初始化后退出进程,由父进程(run.py)重新启动新实例
|
|
177
|
+
|
|
178
|
+
与 restart()(热重启)的区别:
|
|
179
|
+
- restart(): 在同一进程内反初始化再重新初始化
|
|
180
|
+
- hard_restart(): 反初始化后退出进程,由父进程重新启动全新进程
|
|
181
|
+
|
|
182
|
+
确保资源完全释放
|
|
183
|
+
|
|
184
|
+
需要通过 epsdk run 启动才生效,否则进程退出后不会自动重启。
|
|
185
|
+
|
|
186
|
+
:return: bool 硬重启任务是否成功调度
|
|
187
|
+
|
|
188
|
+
:example:
|
|
189
|
+
>>> await sdk.hard_restart()
|
|
190
|
+
"""
|
|
191
|
+
...
|
|
173
192
|
async def uninit(self: object) -> bool:
|
|
174
193
|
"""
|
|
175
194
|
SDK 反初始化
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|