ErisPulse 2.6.0.dev0__tar.gz → 2.6.0.dev1__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 (189) hide show
  1. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/PKG-INFO +3 -3
  2. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/README.md +2 -2
  3. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/pyproject.toml +1 -1
  4. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/adapter.py +14 -5
  5. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/command.py +3 -6
  6. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/adapter.py +147 -9
  7. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/adapter.pyi +1 -1
  8. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/constants.py +34 -0
  9. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/constants.pyi +10 -0
  10. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/en.py +5 -0
  11. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/ja.py +5 -0
  12. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/ru.py +5 -0
  13. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_cn.py +5 -0
  14. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_tw.py +5 -0
  15. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/lifecycle.py +33 -6
  16. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/lifecycle.pyi +14 -0
  17. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/module.py +11 -0
  18. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/router.py +71 -0
  19. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/router.pyi +1 -1
  20. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/frame_config.py +7 -0
  21. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/frame_config.pyi +1 -1
  22. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/sdk.py +86 -1
  23. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/sdk.pyi +1 -1
  24. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/.gitignore +0 -0
  25. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/LICENSE +0 -0
  26. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/__init__.py +0 -0
  27. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/__init__.pyi +0 -0
  28. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/base.py +0 -0
  29. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/base.pyi +0 -0
  30. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/cli.py +0 -0
  31. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/cli.pyi +0 -0
  32. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  33. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
  34. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/create.py +0 -0
  35. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/create.pyi +0 -0
  36. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/init.py +0 -0
  37. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/init.pyi +0 -0
  38. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/install.py +0 -0
  39. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/install.pyi +0 -0
  40. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/language.py +0 -0
  41. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/language.pyi +0 -0
  42. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/list.py +0 -0
  43. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/list.pyi +0 -0
  44. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  45. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
  46. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/run.py +0 -0
  47. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/run.pyi +0 -0
  48. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  49. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
  50. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  51. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
  52. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  53. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
  54. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/console.py +0 -0
  55. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/console.pyi +0 -0
  56. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/__init__.py +0 -0
  57. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/__init__.pyi +0 -0
  58. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
  59. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/__init__.pyi +0 -0
  60. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/en.py +0 -0
  61. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/en.pyi +0 -0
  62. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/ja.py +0 -0
  63. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/ja.pyi +0 -0
  64. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/ru.py +0 -0
  65. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/ru.pyi +0 -0
  66. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +0 -0
  67. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_cn.pyi +0 -0
  68. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +0 -0
  69. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/i18n/locales/zh_tw.pyi +0 -0
  70. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/registry.py +0 -0
  71. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/registry.pyi +0 -0
  72. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  73. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
  74. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/display.py +0 -0
  75. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/display.pyi +0 -0
  76. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
  77. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/file_watcher.pyi +0 -0
  78. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
  79. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
  80. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/__init__.py +0 -0
  81. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
  82. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
  83. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/client.py +0 -0
  84. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/client.pyi +0 -0
  85. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/errors.py +0 -0
  86. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/errors.pyi +0 -0
  87. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
  88. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/kv_builder.pyi +0 -0
  89. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/manager.py +0 -0
  90. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
  91. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/module.py +0 -0
  92. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/module.pyi +0 -0
  93. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/router.py +0 -0
  94. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/router.pyi +0 -0
  95. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
  96. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/send_builder.pyi +0 -0
  97. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/send_rules.py +0 -0
  98. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/send_rules.pyi +0 -0
  99. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/storage.py +0 -0
  100. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
  101. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/websocket.py +0 -0
  102. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Bases/websocket.pyi +0 -0
  103. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/__init__.py +0 -0
  104. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
  105. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/base.py +0 -0
  106. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/base.pyi +0 -0
  107. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/command.pyi +0 -0
  108. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/message.py +0 -0
  109. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/message.pyi +0 -0
  110. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  111. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
  112. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/meta.py +0 -0
  113. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/meta.pyi +0 -0
  114. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/notice.py +0 -0
  115. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/notice.pyi +0 -0
  116. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/request.py +0 -0
  117. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/request.pyi +0 -0
  118. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/session_type.py +0 -0
  119. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
  120. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/wrapper.py +0 -0
  121. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/Event/wrapper.pyi +0 -0
  122. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/__init__.py +0 -0
  123. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/__init__.pyi +0 -0
  124. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/admin.py +0 -0
  125. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/admin.pyi +0 -0
  126. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/client.py +0 -0
  127. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/client.pyi +0 -0
  128. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/config.py +0 -0
  129. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/config.pyi +0 -0
  130. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/__init__.py +0 -0
  131. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/__init__.pyi +0 -0
  132. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/constants.py +0 -0
  133. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/constants.pyi +0 -0
  134. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
  135. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/__init__.pyi +0 -0
  136. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/en.pyi +0 -0
  137. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/ja.pyi +0 -0
  138. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/ru.pyi +0 -0
  139. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_cn.pyi +0 -0
  140. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/i18n/locales/zh_tw.pyi +0 -0
  141. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/logger.py +0 -0
  142. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/logger.pyi +0 -0
  143. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/module.pyi +0 -0
  144. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/storage.py +0 -0
  145. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/Core/storage.pyi +0 -0
  146. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/__init__.py +0 -0
  147. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/__init__.pyi +0 -0
  148. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/__main__.py +0 -0
  149. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/__main__.pyi +0 -0
  150. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/__init__.py +0 -0
  151. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/__init__.pyi +0 -0
  152. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/adapter.py +0 -0
  153. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/adapter.pyi +0 -0
  154. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/bases/__init__.py +0 -0
  155. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
  156. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/bases/finder.py +0 -0
  157. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/bases/finder.pyi +0 -0
  158. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/module.py +0 -0
  159. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/finders/module.pyi +0 -0
  160. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/__init__.py +0 -0
  161. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/__init__.pyi +0 -0
  162. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/adapter.py +0 -0
  163. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/adapter.pyi +0 -0
  164. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  165. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
  166. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/bases/loader.py +0 -0
  167. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
  168. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/module.py +0 -0
  169. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/module.pyi +0 -0
  170. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/strategy.py +0 -0
  171. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/strategy.pyi +0 -0
  172. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/strict.py +0 -0
  173. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/loaders/strict.pyi +0 -0
  174. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/py.typed +0 -0
  175. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/__init__.py +0 -0
  176. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/__init__.pyi +0 -0
  177. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/config_schema.py +0 -0
  178. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/config_schema.pyi +0 -0
  179. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/context.py +0 -0
  180. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/context.pyi +0 -0
  181. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/exceptions.py +0 -0
  182. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/exceptions.pyi +0 -0
  183. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/hints.py +0 -0
  184. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/runtime/hints.pyi +0 -0
  185. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/web_status/4xx.png +0 -0
  186. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/web_status/5xx.png +0 -0
  187. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/web_status/__init__.py +0 -0
  188. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/src/ErisPulse/web_status/__init__.pyi +0 -0
  189. {erispulse-2.6.0.dev0 → erispulse-2.6.0.dev1}/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.6.0.dev0
3
+ Version: 2.6.0.dev1
4
4
  Summary: Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署
5
5
  Project-URL: Homepage, https://github.com/ErisPulse/ErisPulse
6
6
  Project-URL: Documentation, https://www.erisdev.com#docs
@@ -69,7 +69,7 @@ Description-Content-Type: text/markdown
69
69
 
70
70
  <img src=".github/assets/mascot-hero.png" align="right" width="300" alt="ErisPulse" style="margin-left: 24px; margin-bottom: 16px; border-radius: 12px;" />
71
71
 
72
- [**English**](README.md) | 简体中文 | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
72
+ **English** | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
73
73
 
74
74
  # ErisPulse
75
75
 
@@ -715,4 +715,4 @@ The early exploration and growth of ErisPulse would not have been possible witho
715
715
 
716
716
  We also thank all developers and project authors who have contributed to ErisPulse, OneBot ecosystem, and the open-source community.
717
717
 
718
- </div>
718
+ </div>
@@ -1,6 +1,6 @@
1
1
  <img src=".github/assets/mascot-hero.png" align="right" width="300" alt="ErisPulse" style="margin-left: 24px; margin-bottom: 16px; border-radius: 12px;" />
2
2
 
3
- [**English**](README.md) | 简体中文 | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
3
+ **English** | [简体中文](README.zh-CN.md) | [繁體中文](README.zh-TW.md) | [日本語](README.ja.md) | [Русский](README.ru.md)
4
4
 
5
5
  # ErisPulse
6
6
 
@@ -646,4 +646,4 @@ The early exploration and growth of ErisPulse would not have been possible witho
646
646
 
647
647
  We also thank all developers and project authors who have contributed to ErisPulse, OneBot ecosystem, and the open-source community.
648
648
 
649
- </div>
649
+ </div>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ErisPulse"
7
- version = "2.6.0-dev.0"
7
+ version = "2.6.0-dev.1"
8
8
  description = "Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -464,7 +464,10 @@ class SendDSL:
464
464
  if target_type == DETAIL_TYPE_PRIVATE:
465
465
  target_type = "user"
466
466
 
467
- return self.__class__(self._adapter, target_type, target_id, self._account_id, self._rules)
467
+ instance = self.__class__(self._adapter, target_type, target_id, self._account_id)
468
+ if self._rules:
469
+ instance._rules = _copy_rules(self._rules)
470
+ return instance
468
471
 
469
472
  def Using(self, account_id: str | int) -> "SendDSL":
470
473
  """
@@ -477,9 +480,12 @@ class SendDSL:
477
480
  >>> adapter.Send.Using("bot1").To("123").Text("Hello")
478
481
  >>> adapter.Send.To("123").Using("bot1").Text("Hello") # 支持乱序
479
482
  """
480
- return self.__class__(
481
- self._adapter, self._target_type, self._target_id, account_id, self._rules
483
+ instance = self.__class__(
484
+ self._adapter, self._target_type, self._target_id, account_id
482
485
  )
486
+ if self._rules:
487
+ instance._rules = _copy_rules(self._rules)
488
+ return instance
483
489
 
484
490
  def Account(self, account_id: str | int) -> "SendDSL":
485
491
  """
@@ -492,9 +498,12 @@ class SendDSL:
492
498
  >>> adapter.Send.Account("bot1").To("123").Text("Hello")
493
499
  >>> adapter.Send.To("123").Account("bot1").Text("Hello") # 支持乱序
494
500
  """
495
- return self.__class__(
496
- self._adapter, self._target_type, self._target_id, account_id, self._rules
501
+ instance = self.__class__(
502
+ self._adapter, self._target_type, self._target_id, account_id
497
503
  )
504
+ if self._rules:
505
+ instance._rules = _copy_rules(self._rules)
506
+ return instance
498
507
 
499
508
  # ==================== 发送规则装饰器 ====================
500
509
 
@@ -330,17 +330,14 @@ class CommandHandler:
330
330
 
331
331
  return result
332
332
  except asyncio.TimeoutError:
333
- # 清理超时的等待
334
- if wait_key in self._waiting_replies:
335
- del self._waiting_replies[wait_key]
336
333
  logger.trace(f"wait_reply 超时: key={wait_key}, timeout={timeout}s")
337
334
  return None
338
335
  except Exception as e:
339
- # 清理异常情况
340
- if wait_key in self._waiting_replies:
341
- del self._waiting_replies[wait_key]
342
336
  logger.error(f"等待回复时发生错误: {e}")
343
337
  return None
338
+ finally:
339
+ # 无论成功、超时、异常还是 CancelledError,都确保清理等待条目
340
+ self._waiting_replies.pop(wait_key, None)
344
341
 
345
342
  async def _handle_message(self, event: dict[str, Any]):
346
343
  """
@@ -23,6 +23,9 @@ from .constants import (
23
23
  CONFIG_KEY_ADAPTER_STATUS,
24
24
  CONFIG_KEY_ADAPTER_STATUS_OF,
25
25
  DEFAULT_ADAPTER_ENABLED,
26
+ DEFAULT_HANDLER_DRAIN_TIMEOUT_SECS,
27
+ DEFAULT_HANDLER_MAX_CONCURRENCY,
28
+ DEFAULT_OFFLINE_BOT_EXPIRY_SECS,
26
29
  HANDLER_SLOW_THRESHOLD_SECS,
27
30
  )
28
31
  from .i18n import i18n
@@ -91,6 +94,13 @@ class AdapterManager(ManagerBase):
91
94
  # 后台任务追踪 - {platform: asyncio.Task}
92
95
  self._adapter_tasks: dict[str, asyncio.Task] = {}
93
96
 
97
+ # 在途事件处理器 Task 集合(用于 shutdown 时等待/取消)
98
+ self._pending_handler_tasks: set[asyncio.Task] = set()
99
+
100
+ # 事件处理器并发控制信号量(运行时从配置加载,默认值由常量提供)
101
+ self._handler_semaphore: asyncio.Semaphore | None = None
102
+ self._handler_max_concurrency: int = 0 # 0 = 尚未初始化
103
+
94
104
  # Bot状态存储 - {platform: {bot_id: {"status": str, "last_active": float, "info": dict}}}
95
105
  self._bots: dict[str, dict[str, dict]] = {}
96
106
 
@@ -505,6 +515,8 @@ class AdapterManager(ManagerBase):
505
515
  # 仅在关闭全部适配器时清理事件处理器,避免部分关闭影响其他适配器
506
516
  all_platforms = set(self._adapters.keys())
507
517
  if set(platforms) >= all_platforms:
518
+ # 等待或取消所有在途的事件处理器 Task
519
+ await self._drain_pending_handler_tasks()
508
520
  self._onebot_handlers.clear()
509
521
  self._raw_handlers.clear()
510
522
  self._onebot_middlewares.clear()
@@ -550,6 +562,39 @@ class AdapterManager(ManagerBase):
550
562
  # 回收该平台运行期间注册的路由/事件/命令(幂等)
551
563
  self._cleanup_adapter_resources(platform)
552
564
 
565
+ async def _drain_pending_handler_tasks(
566
+ self, timeout: float = DEFAULT_HANDLER_DRAIN_TIMEOUT_SECS
567
+ ) -> None:
568
+ """
569
+ {!--< internal-use >!--}
570
+ 等待或取消所有在途的事件处理器 Task
571
+
572
+ :param timeout: 等待 Task 退出的最长时间(秒)
573
+ """
574
+ pending = list(self._pending_handler_tasks)
575
+ if not pending:
576
+ return
577
+ # 取消所有未完成的 Task
578
+ for task in pending:
579
+ if not task.done():
580
+ task.cancel()
581
+ # 等待它们退出(带超时)
582
+ try:
583
+ await asyncio.wait_for(
584
+ asyncio.gather(*pending, return_exceptions=True),
585
+ timeout=timeout,
586
+ )
587
+ except asyncio.TimeoutError:
588
+ logger.trace(
589
+ i18n.t(
590
+ "core.adapter.handler_drain_timeout",
591
+ count=len(pending),
592
+ timeout=timeout,
593
+ )
594
+ )
595
+ finally:
596
+ self._pending_handler_tasks.clear()
597
+
553
598
  def _cleanup_adapter_resources(self, platform: str) -> None:
554
599
  """
555
600
  {!--< internal-use >!--}
@@ -605,6 +650,20 @@ class AdapterManager(ManagerBase):
605
650
  i18n.t("core.adapter.handlers_clean_failed", platform=platform, error=e)
606
651
  )
607
652
 
653
+ # 清理该适配器注册的生命周期钩子
654
+ try:
655
+ lifecycle_removed = lifecycle.unregister_by_owner(platform)
656
+ if lifecycle_removed > 0:
657
+ logger.trace(
658
+ i18n.t(
659
+ "core.adapter.lifecycle_hooks_cleaned",
660
+ platform=platform,
661
+ count=lifecycle_removed,
662
+ )
663
+ )
664
+ except Exception:
665
+ pass
666
+
608
667
  async def restart(self, platform: str) -> bool:
609
668
  """
610
669
  重启指定平台适配器(shutdown + 资源兜底清理 + start)
@@ -716,6 +775,13 @@ class AdapterManager(ManagerBase):
716
775
  task.cancel()
717
776
  self._adapter_tasks.clear()
718
777
 
778
+ # 清除在途事件处理器 Task 引用(不等待,在 shutdown 中已处理)
779
+ self._pending_handler_tasks.clear()
780
+
781
+ # 重置并发信号量
782
+ self._handler_semaphore = None
783
+ self._handler_max_concurrency = 0
784
+
719
785
  # 清除Bot状态
720
786
  self._bots.clear()
721
787
 
@@ -1126,6 +1192,33 @@ class AdapterManager(ManagerBase):
1126
1192
  },
1127
1193
  )
1128
1194
 
1195
+ def _get_handler_semaphore(self) -> asyncio.Semaphore:
1196
+ """
1197
+ {!--< internal-use >!--}
1198
+ 获取事件处理器并发控制信号量
1199
+
1200
+ 懒初始化,首次调用时从框架配置读取 handler_max_concurrency。
1201
+ 配置变更后可通过设置 _handler_max_concurrency = 0 来强制重建。
1202
+
1203
+ :return: asyncio.Semaphore 并发控制信号量
1204
+ """
1205
+ if self._handler_semaphore is None or self._handler_max_concurrency == 0:
1206
+ max_concurrency = DEFAULT_HANDLER_MAX_CONCURRENCY
1207
+ try:
1208
+ from ..runtime import get_framework_config
1209
+
1210
+ framework_config = get_framework_config()
1211
+ max_concurrency = framework_config.get(
1212
+ "handler_max_concurrency", max_concurrency
1213
+ )
1214
+ except Exception:
1215
+ pass
1216
+ if max_concurrency <= 0:
1217
+ max_concurrency = DEFAULT_HANDLER_MAX_CONCURRENCY
1218
+ self._handler_max_concurrency = max_concurrency
1219
+ self._handler_semaphore = asyncio.Semaphore(max_concurrency)
1220
+ return self._handler_semaphore
1221
+
1129
1222
  def _dispatch_handler_task(
1130
1223
  self,
1131
1224
  func: Callable,
@@ -1133,7 +1226,7 @@ class AdapterManager(ManagerBase):
1133
1226
  *,
1134
1227
  event_type: str = "unknown",
1135
1228
  platform: str = "unknown",
1136
- ) -> asyncio.Task:
1229
+ ) -> asyncio.Task | None:
1137
1230
  """
1138
1231
  {!--< internal-use >!--}
1139
1232
  将事件处理器包装为独立 asyncio.Task 并调度执行
@@ -1147,20 +1240,21 @@ class AdapterManager(ManagerBase):
1147
1240
  :param platform: 平台名称(用于日志)
1148
1241
  :return: asyncio.Task
1149
1242
  """
1150
- import time as _time
1151
-
1152
1243
  _func_name = getattr(func, "__qualname__", getattr(func, "__name__", str(func)))
1153
1244
 
1154
- # 在 Task 顶层准备 wait_reply 累计器(list 对象,可跨 ContextVar 共享)。
1245
+ # 在 Task 顶层准备 wait_reply 累加器(list 对象,可跨 ContextVar 共享)。
1155
1246
  # 注意:ContextVar 的 set()/reset() 必须在同一个 Task Context 内完成,
1156
1247
  # 所以下面把 .set() 移入 _safe_run,避免跨 Task token 错误。
1157
1248
  _task_waits: list[dict] = []
1249
+ _sem = self._get_handler_semaphore()
1158
1250
 
1159
1251
  async def _safe_run():
1160
1252
  _wait_token = handler_waits.set(_task_waits)
1161
- t0 = _time.monotonic()
1253
+ t0 = time.monotonic()
1162
1254
  try:
1163
- await func(data)
1255
+ # 并发背压控制:限制同时在途的 handler Task 数量
1256
+ async with _sem:
1257
+ await func(data)
1164
1258
  except asyncio.CancelledError:
1165
1259
  pass
1166
1260
  except Exception as e:
@@ -1174,7 +1268,7 @@ class AdapterManager(ManagerBase):
1174
1268
  )
1175
1269
  )
1176
1270
  finally:
1177
- elapsed = _time.monotonic() - t0
1271
+ elapsed = time.monotonic() - t0
1178
1272
  handler_waits.reset(_wait_token)
1179
1273
 
1180
1274
  _wait_total = sum(w.get("duration", 0.0) for w in _task_waits)
@@ -1217,9 +1311,16 @@ class AdapterManager(ManagerBase):
1217
1311
  )
1218
1312
 
1219
1313
  try:
1220
- return asyncio.create_task(_safe_run())
1314
+ task = asyncio.create_task(_safe_run())
1315
+ # 追踪 Task 以便 shutdown 时取消/等待
1316
+ self._pending_handler_tasks.add(task)
1317
+ task.add_done_callback(self._pending_handler_tasks.discard)
1318
+ return task
1221
1319
  except RuntimeError:
1222
- return asyncio.ensure_future(_safe_run())
1320
+ task = asyncio.ensure_future(_safe_run())
1321
+ self._pending_handler_tasks.add(task)
1322
+ task.add_done_callback(self._pending_handler_tasks.discard)
1323
+ return task
1223
1324
 
1224
1325
  # ==================== Bot状态管理 ====================
1225
1326
 
@@ -1365,6 +1466,43 @@ class AdapterManager(ManagerBase):
1365
1466
  if bot_meta:
1366
1467
  self._bots[platform][bot_id].setdefault("info", {}).update(bot_meta)
1367
1468
 
1469
+ def _evict_offline_bots(self, expiry_secs: int | None = None) -> int:
1470
+ """
1471
+ {!--< internal-use >!--}
1472
+ 清除过期的离线 Bot 记录
1473
+
1474
+ 遍历 _bots,将状态为 offline 且 last_active 距今超过 expiry_secs 的条目移除。
1475
+
1476
+ :param expiry_secs: 过期时间(秒),None 时从框架配置读取
1477
+ :return: int 被清除的 Bot 记录数
1478
+ """
1479
+ if expiry_secs is None:
1480
+ expiry_secs = DEFAULT_OFFLINE_BOT_EXPIRY_SECS
1481
+ try:
1482
+ from ..runtime import get_framework_config
1483
+
1484
+ framework_config = get_framework_config()
1485
+ expiry_secs = framework_config.get("offline_bot_expiry", expiry_secs)
1486
+ except Exception:
1487
+ pass
1488
+ if expiry_secs <= 0:
1489
+ return 0
1490
+
1491
+ now = time.time()
1492
+ evicted = 0
1493
+ for platform in list(self._bots.keys()):
1494
+ for bot_id in list(self._bots[platform].keys()):
1495
+ bot_info = self._bots[platform][bot_id]
1496
+ if (
1497
+ bot_info.get("status") == "offline"
1498
+ and (now - bot_info.get("last_active", 0)) > expiry_secs
1499
+ ):
1500
+ del self._bots[platform][bot_id]
1501
+ evicted += 1
1502
+ if not self._bots[platform]:
1503
+ del self._bots[platform]
1504
+ return evicted
1505
+
1368
1506
  def get_bot_info(self, platform: str, bot_id: str) -> dict | None:
1369
1507
  """
1370
1508
  获取Bot详细信息
@@ -22,7 +22,7 @@ from ..runtime.context import current_owner, handler_waits
22
22
  from .Bases.adapter import BaseAdapter
23
23
  from .Bases.manager import ManagerBase
24
24
  from .config import config
25
- 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
25
+ from .constants import ADAPTER_RETRY_BACKOFF_INTERVALS, ADAPTER_RETRY_FIXED_DELAY_SECS, CONFIG_KEY_ADAPTER_STATUS, CONFIG_KEY_ADAPTER_STATUS_OF, DEFAULT_ADAPTER_ENABLED, DEFAULT_HANDLER_DRAIN_TIMEOUT_SECS, DEFAULT_HANDLER_MAX_CONCURRENCY, DEFAULT_OFFLINE_BOT_EXPIRY_SECS, HANDLER_SLOW_THRESHOLD_SECS
26
26
  from .i18n import i18n
27
27
  from .lifecycle import lifecycle
28
28
  from .logger import logger
@@ -585,3 +585,37 @@ DEFAULT_WS_CLIENT_CONNECT_TIMEOUT_SECS: Final[float] = 10.0
585
585
  # 才会按数组索引处理。超过此值的索引会被安全跳过,避免分配超大列表导致 OOM。
586
586
  # 修改影响: 运行时行为。storage.set() 写入超大列表索引时的安全阈值。
587
587
  STORAGE_MAX_LIST_INDEX: Final[int] = 10000
588
+
589
+ # ==============================================================================
590
+ # 性能优化与主动 GC
591
+ #
592
+ # 控制事件处理器并发上限、后台资源回收节奏和内存释放策略。
593
+ # 使用位置: Core/adapter.py -> emit(), shutdown()
594
+ # Core/lifecycle.py -> register()
595
+ # sdk.py -> _do_uninit()
596
+ # ==============================================================================
597
+
598
+ # 事件处理器最大并发 Task 数。
599
+ # 运行时行为。每个事件匹配的处理器在独立 Task 中执行,此值限制同时运行的 Task 数量。
600
+ # 修改影响: 设大提高并发吞吐但增加内存占用,设小限制资源消耗但可能降低事件处理速度。
601
+ DEFAULT_HANDLER_MAX_CONCURRENCY: Final[int] = 64
602
+
603
+ # 路由限流存储的自动清理间隔(秒)。
604
+ # 运行时行为。后台定期扫描 _rate_limit_store,清除过期的 IP 记录。
605
+ # 修改影响: 设大减少 CPU 开销但内存占用更久,设小更积极回收内存。
606
+ DEFAULT_RATE_LIMIT_CLEANUP_INTERVAL_SECS: Final[int] = 300
607
+
608
+ # 离线 Bot 信息的自动过期时间(秒)。
609
+ # 运行时行为。Bot 标记为 offline 后,经过此时间会被自动清除。
610
+ # 修改影响: 设大保留更多历史 Bot 信息,设小更快回收内存。0 表示不过期。
611
+ DEFAULT_OFFLINE_BOT_EXPIRY_SECS: Final[int] = 3600
612
+
613
+ # 主动 GC(垃圾回收)间隔(秒)。
614
+ # 运行时行为。SDK 初始化后启动定期 GC 后台任务,按此间隔触发 Python GC 和内部资源回收。
615
+ # 修改影响: 设大减少 CPU 中断但内存峰值更高,设小更频繁回收但增加开销。0 表示禁用。
616
+ DEFAULT_PROACTIVE_GC_INTERVAL_SECS: Final[int] = 300
617
+
618
+ # shutdown 时等待在途事件处理器完成的最长时间(秒)。
619
+ # 运行时行为。适配器关闭时取消所有 pending handler tasks 后等待它们退出的耐心时间。
620
+ # 修改影响: 设大确保处理器完整结束,设小加速关闭流程。
621
+ DEFAULT_HANDLER_DRAIN_TIMEOUT_SECS: Final[float] = 5.0
@@ -208,3 +208,13 @@ DEFAULT_WS_CLIENT_HEARTBEAT_SECS: Final[float | None]
208
208
  DEFAULT_WS_CLIENT_CONNECT_TIMEOUT_SECS: Final[float]
209
209
 
210
210
  STORAGE_MAX_LIST_INDEX: Final[int]
211
+
212
+ DEFAULT_HANDLER_MAX_CONCURRENCY: Final[int]
213
+
214
+ DEFAULT_RATE_LIMIT_CLEANUP_INTERVAL_SECS: Final[int]
215
+
216
+ DEFAULT_OFFLINE_BOT_EXPIRY_SECS: Final[int]
217
+
218
+ DEFAULT_PROACTIVE_GC_INTERVAL_SECS: Final[int]
219
+
220
+ DEFAULT_HANDLER_DRAIN_TIMEOUT_SECS: Final[float]
@@ -45,6 +45,8 @@ TRANSLATIONS = {
45
45
  "core.sdk.uninit.timeout_msg": "SDK uninitialization timed out",
46
46
  "core.sdk.uninit.failed_msg": "SDK uninitialization failed",
47
47
  "core.sdk.uninit.critical_error": "SDK uninitialization critical error: {error}",
48
+ "core.sdk.uninit.client_close_failed": "Failed to close HTTP client connection pool: {error}",
49
+ "core.sdk.gc.collected": "[GC] Python collected {collected} object(s), evicted {evicted} offline bot(s)",
48
50
  # ==================== SDK Environment Preparation ====================
49
51
  "core.sdk.prepare.start": "Starting initialization",
50
52
  "core.sdk.prepare.starting": "Preparing initialization environment...",
@@ -133,6 +135,8 @@ TRANSLATIONS = {
133
135
  "core.adapter.bot_online": "Bot {platform}/{bot_id} online",
134
136
  "core.adapter.handler_error": "Event handler execution error [{handler}] type={type} platform={platform}: {error}",
135
137
  "core.adapter.handler_slow": "Event handler execution slow [{handler}] elapsed {elapsed}s > {threshold}s type={type} platform={platform}{tag}",
138
+ "core.adapter.handler_drain_timeout": "{count} pending event handler(s) did not exit within {timeout}s, force cancelled",
139
+ "core.adapter.lifecycle_hooks_cleaned": "Adapter {platform} cleaned {count} lifecycle hook(s)",
136
140
  "core.adapter.auto_discover_bot": "Auto-discovered Bot: {platform}/{bot_id}",
137
141
  "core.adapter.bot_status_change": "Bot status change: {platform}/{bot_id} {old} -> {new}",
138
142
  "core.adapter.method_not_exist": "Method {method} does not exist",
@@ -161,6 +165,7 @@ TRANSLATIONS = {
161
165
  "core.module.on_unload_failed": "Module {name} on_unload method execution failed: {error}",
162
166
  "core.module.unload_routes_cleaned": "Cleaned routes for module {name}: HTTP={http}, WebSocket={ws}",
163
167
  "core.module.unload_handlers_cleaned": "Cleaned {count} event handlers/commands for module {name}",
168
+ "core.module.lifecycle_hooks_cleaned": "Module {name} cleaned {count} lifecycle hook(s)",
164
169
  "core.module.unload_success": "Module {name} unloaded successfully",
165
170
  "core.module.unload_failed": "Failed to unload module {name}: {error}",
166
171
  "core.module.registered_status": "Module {name} registered and {status}",
@@ -45,6 +45,8 @@ TRANSLATIONS = {
45
45
  "core.sdk.uninit.timeout_msg": "SDK の非初期化がタイムアウトしました",
46
46
  "core.sdk.uninit.failed_msg": "SDK の非初期化に失敗しました",
47
47
  "core.sdk.uninit.critical_error": "SDK 非初期化の重大なエラー: {error}",
48
+ "core.sdk.uninit.client_close_failed": "HTTP クライアント接続プールのクローズに失敗しました: {error}",
49
+ "core.sdk.gc.collected": "[GC] Python が {collected} 個のオブジェクトを回収し、{evicted} 個のオフライン Bot を削除しました",
48
50
  # ==================== SDK 環境準備 ====================
49
51
  "core.sdk.prepare.start": "初期化を開始します",
50
52
  "core.sdk.prepare.starting": "初期化環境を準備しています...",
@@ -133,6 +135,8 @@ TRANSLATIONS = {
133
135
  "core.adapter.bot_online": "Bot {platform}/{bot_id} がオンラインになりました",
134
136
  "core.adapter.handler_error": "イベントハンドラーの実行エラー [{handler}] type={type} platform={platform}: {error}",
135
137
  "core.adapter.handler_slow": "イベントハンドラーの実行が遅いです [{handler}] 経過時間 {elapsed}s > {threshold}s type={type} platform={platform}{tag}",
138
+ "core.adapter.handler_drain_timeout": "{count} 個の保留中イベントハンドラーが {timeout}s 内に終了しなかったため、強制キャンセルされました",
139
+ "core.adapter.lifecycle_hooks_cleaned": "アダプター {platform} が {count} 個のライフサイクルフックをクリーンアップしました",
136
140
  "core.adapter.auto_discover_bot": "Bot を自動検出しました: {platform}/{bot_id}",
137
141
  "core.adapter.bot_status_change": "Bot の状態変化: {platform}/{bot_id} {old} -> {new}",
138
142
  "core.adapter.method_not_exist": "メソッド {method} は存在しません",
@@ -161,6 +165,7 @@ TRANSLATIONS = {
161
165
  "core.module.on_unload_failed": "モジュール {name} の on_unload メソッドの実行に失敗しました: {error}",
162
166
  "core.module.unload_routes_cleaned": "モジュール {name} のルートをクリーンアップしました: HTTP={http}, WebSocket={ws}",
163
167
  "core.module.unload_handlers_cleaned": "モジュール {name} の {count} 個のイベントハンドラー/コマンドをクリーンアップしました",
168
+ "core.module.lifecycle_hooks_cleaned": "モジュール {name} が {count} 個のライフサイクルフックをクリーンアップしました",
164
169
  "core.module.unload_success": "モジュール {name} のアンロードに成功しました",
165
170
  "core.module.unload_failed": "モジュール {name} のアンロードに失敗しました: {error}",
166
171
  "core.module.registered_status": "モジュール {name} は登録され、{status}",
@@ -45,6 +45,8 @@ TRANSLATIONS = {
45
45
  "core.sdk.uninit.timeout_msg": "Деинициализация SDK превысила время ожидания",
46
46
  "core.sdk.uninit.failed_msg": "Ошибка деинициализации SDK",
47
47
  "core.sdk.uninit.critical_error": "Критическая ошибка деинициализации SDK: {error}",
48
+ "core.sdk.uninit.client_close_failed": "Не удалось закрыть пул соединений HTTP-клиента: {error}",
49
+ "core.sdk.gc.collected": "[GC] Python собрал {collected} объект(ов), удалено {evicted} автономных бот(ов)",
48
50
  # ==================== Подготовка окружения SDK ====================
49
51
  "core.sdk.prepare.start": "Начало инициализации",
50
52
  "core.sdk.prepare.starting": "Подготовка окружения инициализации...",
@@ -133,6 +135,8 @@ TRANSLATIONS = {
133
135
  "core.adapter.bot_online": "Bot {platform}/{bot_id} в сети",
134
136
  "core.adapter.handler_error": "Ошибка выполнения обработчика событий [{handler}] type={type} platform={platform}: {error}",
135
137
  "core.adapter.handler_slow": "Медленное выполнение обработчика событий [{handler}] время {elapsed}s > {threshold}s type={type} platform={platform}{tag}",
138
+ "core.adapter.handler_drain_timeout": "{count} ожидающих обработчиков событий не завершились за {timeout}s, принудительно отменены",
139
+ "core.adapter.lifecycle_hooks_cleaned": "Адаптер {platform} очистил {count} перехватчиков жизненного цикла",
136
140
  "core.adapter.auto_discover_bot": "Автообнаружение Bot: {platform}/{bot_id}",
137
141
  "core.adapter.bot_status_change": "Изменение состояния Bot: {platform}/{bot_id} {old} -> {new}",
138
142
  "core.adapter.method_not_exist": "Метод {method} не существует",
@@ -161,6 +165,7 @@ TRANSLATIONS = {
161
165
  "core.module.on_unload_failed": "Ошибка выполнения метода on_unload модуля {name}: {error}",
162
166
  "core.module.unload_routes_cleaned": "Очищены маршруты для модуля {name}: HTTP={http}, WebSocket={ws}",
163
167
  "core.module.unload_handlers_cleaned": "Очищено {count} обработчиков событий/команд для модуля {name}",
168
+ "core.module.lifecycle_hooks_cleaned": "Модуль {name} очистил {count} перехватчиков жизненного цикла",
164
169
  "core.module.unload_success": "Модуль {name} успешно выгружен",
165
170
  "core.module.unload_failed": "Ошибка выгрузки модуля {name}: {error}",
166
171
  "core.module.registered_status": "Модуль {name} зарегистрирован и {status}",
@@ -45,6 +45,8 @@ TRANSLATIONS = {
45
45
  "core.sdk.uninit.timeout_msg": "SDK反初始化超时",
46
46
  "core.sdk.uninit.failed_msg": "SDK反初始化失败",
47
47
  "core.sdk.uninit.critical_error": "SDK反初始化严重错误: {error}",
48
+ "core.sdk.uninit.client_close_failed": "关闭 HTTP 客户端连接池失败: {error}",
49
+ "core.sdk.gc.collected": "[GC] Python 回收了 {collected} 个对象,清理了 {evicted} 个离线 Bot",
48
50
  # ==================== SDK 环境准备 ====================
49
51
  "core.sdk.prepare.start": "开始初始化",
50
52
  "core.sdk.prepare.starting": "准备初始化环境...",
@@ -133,6 +135,8 @@ TRANSLATIONS = {
133
135
  "core.adapter.bot_online": "Bot {platform}/{bot_id} 上线",
134
136
  "core.adapter.handler_error": "事件处理器执行错误 [{handler}] type={type} platform={platform}: {error}",
135
137
  "core.adapter.handler_slow": "事件处理器执行缓慢 [{handler}] 耗时 {elapsed}s > {threshold}s type={type} platform={platform}{tag}",
138
+ "core.adapter.handler_drain_timeout": "{count} 个在途事件处理器未在 {timeout}s 内退出,已强制取消",
139
+ "core.adapter.lifecycle_hooks_cleaned": "适配器 {platform} 清理了 {count} 个生命周期钩子",
136
140
  "core.adapter.auto_discover_bot": "自动发现Bot: {platform}/{bot_id}",
137
141
  "core.adapter.bot_status_change": "Bot状态变更: {platform}/{bot_id} {old} -> {new}",
138
142
  "core.adapter.method_not_exist": "方法 {method} 不存在",
@@ -161,6 +165,7 @@ TRANSLATIONS = {
161
165
  "core.module.on_unload_failed": "模块 {name} on_unload 方法执行失败: {error}",
162
166
  "core.module.unload_routes_cleaned": "已清理模块 {name} 的路由: HTTP={http}, WebSocket={ws}",
163
167
  "core.module.unload_handlers_cleaned": "已清理模块 {name} 的 {count} 个事件处理器/命令",
168
+ "core.module.lifecycle_hooks_cleaned": "模块 {name} 清理了 {count} 个生命周期钩子",
164
169
  "core.module.unload_success": "模块 {name} 卸载成功",
165
170
  "core.module.unload_failed": "卸载模块 {name} 失败: {error}",
166
171
  "core.module.registered_status": "模块 {name} 已注册并{status}",
@@ -45,6 +45,8 @@ TRANSLATIONS = {
45
45
  "core.sdk.uninit.timeout_msg": "SDK反初始化逾時",
46
46
  "core.sdk.uninit.failed_msg": "SDK反初始化失敗",
47
47
  "core.sdk.uninit.critical_error": "SDK反初始化嚴重錯誤: {error}",
48
+ "core.sdk.uninit.client_close_failed": "關閉 HTTP 客戶端連線池失敗: {error}",
49
+ "core.sdk.gc.collected": "[GC] Python 回收了 {collected} 個物件,清理了 {evicted} 個離線 Bot",
48
50
  # ==================== SDK 環境準備 ====================
49
51
  "core.sdk.prepare.start": "開始初始化",
50
52
  "core.sdk.prepare.starting": "準備初始化環境...",
@@ -133,6 +135,8 @@ TRANSLATIONS = {
133
135
  "core.adapter.bot_online": "Bot {platform}/{bot_id} 上線",
134
136
  "core.adapter.handler_error": "事件處理器執行錯誤 [{handler}] type={type} platform={platform}: {error}",
135
137
  "core.adapter.handler_slow": "事件處理器執行緩慢 [{handler}] 耗時 {elapsed}s > {threshold}s type={type} platform={platform}{tag}",
138
+ "core.adapter.handler_drain_timeout": "{count} 個在途事件處理器未在 {timeout}s 內退出,已強制取消",
139
+ "core.adapter.lifecycle_hooks_cleaned": "介面卡 {platform} 清理了 {count} 個生命週期鉤子",
136
140
  "core.adapter.auto_discover_bot": "自動發現Bot: {platform}/{bot_id}",
137
141
  "core.adapter.bot_status_change": "Bot狀態變更: {platform}/{bot_id} {old} -> {new}",
138
142
  "core.adapter.method_not_exist": "方法 {method} 不存在",
@@ -161,6 +165,7 @@ TRANSLATIONS = {
161
165
  "core.module.on_unload_failed": "模組 {name} on_unload 方法執行失敗: {error}",
162
166
  "core.module.unload_routes_cleaned": "已清理模組 {name} 的路由: HTTP={http}, WebSocket={ws}",
163
167
  "core.module.unload_handlers_cleaned": "已清理模組 {name} 的 {count} 個事件處理器/命令",
168
+ "core.module.lifecycle_hooks_cleaned": "模組 {name} 清理了 {count} 個生命週期鉤子",
164
169
  "core.module.unload_success": "模組 {name} 卸載成功",
165
170
  "core.module.unload_failed": "卸載模組 {name} 失敗: {error}",
166
171
  "core.module.registered_status": "模組 {name} 已註冊並{status}",
@@ -19,6 +19,7 @@ from typing import Any
19
19
 
20
20
  from .constants import DEFAULT_EVENT_SOURCE, HANDLER_SLOW_THRESHOLD_SECS
21
21
  from .i18n import i18n
22
+ from ..runtime.context import current_owner
22
23
 
23
24
 
24
25
  class _NullLogger:
@@ -108,7 +109,8 @@ class LifecycleManager:
108
109
  }
109
110
 
110
111
  def __init__(self):
111
- self._hooks: dict[str, list[tuple[int, Callable]]] = {}
112
+ # _hooks 存储 (priority, handler, owner) 三元组
113
+ self._hooks: dict[str, list[tuple[int, Callable, str | None]]] = {}
112
114
  self._timers: dict[str, float] = {}
113
115
 
114
116
  # ==================== 注册 API ====================
@@ -136,7 +138,8 @@ class LifecycleManager:
136
138
  raise ValueError(i18n.t("core.lifecycle.event_name_required"))
137
139
 
138
140
  def decorator(func: Callable) -> Callable:
139
- self._hooks.setdefault(event, []).append((priority, func))
141
+ owner = current_owner.get()
142
+ self._hooks.setdefault(event, []).append((priority, func, owner))
140
143
  self._hooks[event].sort(key=lambda x: x[0], reverse=True)
141
144
  return func
142
145
 
@@ -155,7 +158,8 @@ class LifecycleManager:
155
158
  """
156
159
  if not isinstance(event, str) or not event:
157
160
  raise ValueError(i18n.t("core.lifecycle.event_name_required"))
158
- self._hooks.setdefault(event, []).append((priority, handler))
161
+ owner = current_owner.get()
162
+ self._hooks.setdefault(event, []).append((priority, handler, owner))
159
163
  self._hooks[event].sort(key=lambda x: x[0], reverse=True)
160
164
 
161
165
  def unregister(self, event: str, handler: Callable = None):
@@ -173,7 +177,30 @@ class LifecycleManager:
173
177
  self._hooks.pop(event, None)
174
178
  else:
175
179
  handlers = self._hooks.get(event, [])
176
- self._hooks[event] = [(p, h) for p, h in handlers if h != handler]
180
+ self._hooks[event] = [(p, h, o) for p, h, o in handlers if h != handler]
181
+
182
+ def unregister_by_owner(self, owner: str) -> int:
183
+ """
184
+ 取消指定 owner 注册的所有事件处理器
185
+
186
+ 用于模块/适配器卸载时自动清理其注册的钩子,避免闭包引用导致内存泄漏。
187
+
188
+ :param owner: 模块或适配器名称
189
+ :return: int 被移除的处理器数量
190
+
191
+ :example:
192
+ >>> lifecycle.unregister_by_owner("MyModule")
193
+ """
194
+ removed = 0
195
+ for event in list(self._hooks.keys()):
196
+ original_len = len(self._hooks[event])
197
+ self._hooks[event] = [
198
+ (p, h, o) for p, h, o in self._hooks[event] if o != owner
199
+ ]
200
+ removed += original_len - len(self._hooks[event])
201
+ if not self._hooks[event]:
202
+ del self._hooks[event]
203
+ return removed
177
204
 
178
205
  # ==================== 触发 API ====================
179
206
 
@@ -346,7 +373,7 @@ class LifecycleManager:
346
373
  """
347
374
  import time as _time
348
375
 
349
- for priority, handler in self._hooks[hook_name]:
376
+ for priority, handler, _owner in self._hooks[hook_name]:
350
377
  try:
351
378
  _t = _time.monotonic()
352
379
  _hname = getattr(
@@ -381,7 +408,7 @@ class LifecycleManager:
381
408
  :param data: Any 事件数据
382
409
  :return: Any 处理后的数据
383
410
  """
384
- for _, handler in self._hooks[hook_name]:
411
+ for _, handler, _owner in self._hooks[hook_name]:
385
412
  try:
386
413
  if inspect.iscoroutinefunction(handler):
387
414
  try: