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.
Files changed (130) hide show
  1. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/PKG-INFO +1 -1
  2. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/pyproject.toml +1 -1
  3. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/run.py +31 -9
  4. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/run.pyi +3 -0
  5. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/base.py +7 -1
  6. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/base.pyi +2 -1
  7. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/command.py +1 -0
  8. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/adapter.py +92 -26
  9. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/adapter.pyi +4 -1
  10. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/constants.py +5 -0
  11. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/lifecycle.py +8 -2
  12. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/lifecycle.pyi +1 -1
  13. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/module.py +12 -0
  14. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/adapter.py +16 -2
  15. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/module.py +35 -1
  16. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/sdk.py +34 -0
  17. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/sdk.pyi +19 -0
  18. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/.gitignore +0 -0
  19. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/LICENSE +0 -0
  20. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/README.md +0 -0
  21. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/__init__.py +0 -0
  22. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/__init__.pyi +0 -0
  23. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/base.py +0 -0
  24. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/base.pyi +0 -0
  25. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/cli.py +0 -0
  26. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/cli.pyi +0 -0
  27. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  28. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
  29. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/create.py +0 -0
  30. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/create.pyi +0 -0
  31. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/init.py +0 -0
  32. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/init.pyi +0 -0
  33. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/install.py +0 -0
  34. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/install.pyi +0 -0
  35. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list.py +0 -0
  36. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list.pyi +0 -0
  37. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  38. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
  39. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  40. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
  41. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  42. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
  43. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  44. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
  45. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/console.py +0 -0
  46. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/console.pyi +0 -0
  47. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/registry.py +0 -0
  48. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/registry.pyi +0 -0
  49. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  50. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
  51. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/display.py +0 -0
  52. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/display.pyi +0 -0
  53. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
  54. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
  55. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/__init__.py +0 -0
  56. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
  57. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/adapter.py +0 -0
  58. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
  59. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/manager.py +0 -0
  60. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
  61. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/module.py +0 -0
  62. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/module.pyi +0 -0
  63. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/storage.py +0 -0
  64. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
  65. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/__init__.py +0 -0
  66. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
  67. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/command.pyi +0 -0
  68. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message.py +0 -0
  69. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message.pyi +0 -0
  70. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  71. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
  72. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/meta.py +0 -0
  73. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/meta.pyi +0 -0
  74. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/notice.py +0 -0
  75. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/notice.pyi +0 -0
  76. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/request.py +0 -0
  77. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/request.pyi +0 -0
  78. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/session_type.py +0 -0
  79. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
  80. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/wrapper.py +0 -0
  81. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/Event/wrapper.pyi +0 -0
  82. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/__init__.py +0 -0
  83. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/__init__.pyi +0 -0
  84. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/config.py +0 -0
  85. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/config.pyi +0 -0
  86. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/constants.pyi +0 -0
  87. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/logger.py +0 -0
  88. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/logger.pyi +0 -0
  89. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/module.pyi +0 -0
  90. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/router.py +0 -0
  91. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/router.pyi +0 -0
  92. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/storage.py +0 -0
  93. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/Core/storage.pyi +0 -0
  94. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__init__.py +0 -0
  95. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__init__.pyi +0 -0
  96. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__main__.py +0 -0
  97. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/__main__.pyi +0 -0
  98. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/__init__.py +0 -0
  99. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/__init__.pyi +0 -0
  100. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/adapter.py +0 -0
  101. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/adapter.pyi +0 -0
  102. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/__init__.py +0 -0
  103. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
  104. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/finder.py +0 -0
  105. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/bases/finder.pyi +0 -0
  106. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/module.py +0 -0
  107. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/finders/module.pyi +0 -0
  108. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/__init__.py +0 -0
  109. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/__init__.pyi +0 -0
  110. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/adapter.pyi +0 -0
  111. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  112. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
  113. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/loader.py +0 -0
  114. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
  115. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/module.pyi +0 -0
  116. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/strategy.py +0 -0
  117. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/loaders/strategy.pyi +0 -0
  118. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/__init__.py +0 -0
  119. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/__init__.pyi +0 -0
  120. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/context.py +0 -0
  121. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/context.pyi +0 -0
  122. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/exceptions.py +0 -0
  123. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/exceptions.pyi +0 -0
  124. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/frame_config.py +0 -0
  125. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/runtime/frame_config.pyi +0 -0
  126. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/4xx.png +0 -0
  127. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/5xx.png +0 -0
  128. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/__init__.py +0 -0
  129. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/__init__.pyi +0 -0
  130. {erispulse-2.4.6.dev2 → erispulse-2.4.6.dev4}/src/ErisPulse/web_status/unknow.png +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse
3
- Version: 2.4.6.dev2
3
+ Version: 2.4.6.dev4
4
4
  Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
5
5
  Author-email: ErisDev <erisdev@88.com>
6
6
  Maintainer-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ErisPulse"
7
- version = "2.4.6-dev.2"
7
+ version = "2.4.6-dev.4"
8
8
  description = "ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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
- async def _run():
125
- from ... import sdk
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
- await sdk.run(keep_running=True)
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
- asyncio.run(_run())
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
@@ -286,6 +286,7 @@ class CommandHandler:
286
286
  # 清理超时的等待
287
287
  if wait_key in self._waiting_replies:
288
288
  del self._waiting_replies[wait_key]
289
+ logger.debug(f"wait_reply 超时: key={wait_key}, timeout={timeout}s")
289
290
  return None
290
291
  except Exception as e:
291
292
  # 清理异常情况
@@ -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
- # 检查适配器类 __init__ 方法的参数
139
- init_signature = inspect.signature(adapter_class.__init__)
140
- params = [p for p in init_signature.parameters.values() if p.name != "self"]
141
-
142
- sdk_to_use = self._sdk
143
- if sdk_to_use is None:
144
- from .. import sdk
145
-
146
- sdk_to_use = sdk
147
-
148
- # 根据参数情况创建实例
149
- if params:
150
- instance = adapter_class(sdk_to_use)
151
- else:
152
- instance = adapter_class()
153
-
154
- self._adapters[platform] = instance
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
- await handler_wrapper["func"](processed_data)
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
- await handler_wrapper["func"](platform_raw)
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
- for _, handler in self._hooks[hook_name]:
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 Exception as e:
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, Exception) or result is False:
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, Exception) or result is False:
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