ErisPulse 2.3.4.dev0__py3-none-any.whl → 2.3.4.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. ErisPulse/{utils/cli → CLI}/__init__.py +1 -1
  2. ErisPulse/{utils/cli → CLI}/__init__.pyi +1 -1
  3. ErisPulse/{utils/cli/__main__.py → CLI/cli.py} +3 -4
  4. ErisPulse/{utils/cli/__main__.pyi → CLI/cli.pyi} +2 -3
  5. ErisPulse/{utils/cli → CLI}/commands/init.py +4 -9
  6. ErisPulse/{utils/cli → CLI}/commands/init.pyi +2 -14
  7. ErisPulse/{utils/cli → CLI}/commands/install.py +2 -7
  8. ErisPulse/{utils/cli → CLI}/commands/install.pyi +2 -14
  9. ErisPulse/{utils/cli → CLI}/commands/list.py +2 -7
  10. ErisPulse/{utils/cli → CLI}/commands/list.pyi +2 -14
  11. ErisPulse/{utils/cli → CLI}/commands/list_remote.py +2 -7
  12. ErisPulse/{utils/cli → CLI}/commands/list_remote.pyi +2 -14
  13. ErisPulse/{utils/cli → CLI}/commands/run.py +3 -7
  14. ErisPulse/{utils/cli → CLI}/commands/run.pyi +2 -11
  15. ErisPulse/{utils/cli → CLI}/commands/self_update.py +2 -7
  16. ErisPulse/{utils/cli → CLI}/commands/self_update.pyi +2 -14
  17. ErisPulse/{utils/cli → CLI}/commands/uninstall.py +1 -6
  18. ErisPulse/{utils/cli → CLI}/commands/uninstall.pyi +1 -14
  19. ErisPulse/{utils/cli → CLI}/commands/upgrade.py +1 -7
  20. ErisPulse/{utils/cli → CLI}/commands/upgrade.pyi +1 -14
  21. ErisPulse/{utils → CLI/utils}/__init__.py +1 -5
  22. ErisPulse/{utils → CLI/utils}/__init__.pyi +0 -2
  23. ErisPulse/{utils → CLI/utils}/package_manager.py +146 -20
  24. ErisPulse/{utils → CLI/utils}/package_manager.pyi +20 -3
  25. ErisPulse/{utils → CLI/utils}/reload_handler.py +7 -8
  26. ErisPulse/{utils → CLI/utils}/reload_handler.pyi +1 -1
  27. ErisPulse/Core/Bases/manager.py +136 -0
  28. ErisPulse/Core/Bases/manager.pyi +108 -0
  29. ErisPulse/Core/Bases/module.py +53 -1
  30. ErisPulse/Core/Bases/module.pyi +43 -0
  31. ErisPulse/Core/Event/command.py +6 -1
  32. ErisPulse/Core/_self_config.py +1 -1
  33. ErisPulse/Core/adapter.py +70 -10
  34. ErisPulse/Core/adapter.pyi +18 -1
  35. ErisPulse/Core/exceptions.py +4 -2
  36. ErisPulse/Core/lifecycle.py +9 -0
  37. ErisPulse/Core/logger.py +21 -15
  38. ErisPulse/Core/logger.pyi +2 -1
  39. ErisPulse/Core/module.py +57 -9
  40. ErisPulse/Core/module.pyi +12 -1
  41. ErisPulse/Core/router.py +13 -5
  42. ErisPulse/Core/storage.py +94 -256
  43. ErisPulse/Core/storage.pyi +13 -66
  44. ErisPulse/__init__.py +35 -1237
  45. ErisPulse/__init__.pyi +3 -290
  46. ErisPulse/__main__.py +1 -1
  47. ErisPulse/__main__.pyi +1 -1
  48. ErisPulse/loaders/__init__.py +22 -0
  49. ErisPulse/loaders/__init__.pyi +21 -0
  50. ErisPulse/loaders/adapter_loader.py +187 -0
  51. ErisPulse/loaders/adapter_loader.pyi +82 -0
  52. ErisPulse/loaders/base_loader.py +162 -0
  53. ErisPulse/loaders/base_loader.pyi +23 -0
  54. ErisPulse/loaders/initializer.py +150 -0
  55. ErisPulse/loaders/initializer.pyi +60 -0
  56. ErisPulse/loaders/module_loader.py +618 -0
  57. ErisPulse/loaders/module_loader.pyi +179 -0
  58. ErisPulse/loaders/strategy.py +129 -0
  59. ErisPulse/loaders/strategy.pyi +90 -0
  60. ErisPulse/sdk.py +435 -0
  61. ErisPulse/sdk.pyi +158 -0
  62. {erispulse-2.3.4.dev0.dist-info → erispulse-2.3.4.dev2.dist-info}/METADATA +1 -1
  63. erispulse-2.3.4.dev2.dist-info/RECORD +103 -0
  64. ErisPulse/sdk_protocol.py +0 -143
  65. ErisPulse/sdk_protocol.pyi +0 -97
  66. erispulse-2.3.4.dev0.dist-info/RECORD +0 -89
  67. /ErisPulse/{utils/cli → CLI}/base.py +0 -0
  68. /ErisPulse/{utils/cli → CLI}/base.pyi +0 -0
  69. /ErisPulse/{utils/cli → CLI}/commands/__init__.py +0 -0
  70. /ErisPulse/{utils/cli → CLI}/commands/__init__.pyi +0 -0
  71. /ErisPulse/{utils → CLI}/console.py +0 -0
  72. /ErisPulse/{utils → CLI}/console.pyi +0 -0
  73. /ErisPulse/{utils/cli → CLI}/registry.py +0 -0
  74. /ErisPulse/{utils/cli → CLI}/registry.pyi +0 -0
  75. {erispulse-2.3.4.dev0.dist-info → erispulse-2.3.4.dev2.dist-info}/WHEEL +0 -0
  76. {erispulse-2.3.4.dev0.dist-info → erispulse-2.3.4.dev2.dist-info}/entry_points.txt +0 -0
  77. {erispulse-2.3.4.dev0.dist-info → erispulse-2.3.4.dev2.dist-info}/licenses/LICENSE +0 -0
@@ -10,18 +10,61 @@ ErisPulse 模块基础模块
10
10
  提供模块基类定义和标准接口
11
11
  """
12
12
 
13
+ from typing import Union, Dict, Any
14
+ from ...loaders.strategy import ModuleLoadStrategy
15
+
13
16
  class BaseModule:
14
17
  """
15
18
  模块基类
16
19
 
17
20
  提供模块加载和卸载的标准接口
18
21
  """
22
+ def get_load_strategy() -> Union[(ModuleLoadStrategy, Dict[(str, Any)])]:
23
+ """
24
+ 获取模块加载策略
25
+
26
+ 支持返回 ModuleLoadStrategy 对象或字典
27
+ 所有属性统一处理,没有任何预定义字段
28
+
29
+ :return: 加载策略对象或字典
30
+
31
+ {!--< tips >!--}
32
+ 常用配置项:
33
+ - lazy_load: bool, 是否懒加载(默认 True)
34
+ - priority: int, 加载优先级(默认 0,数值越大优先级越高)
35
+
36
+ 使用方式:
37
+ >>> class MyModule(BaseModule):
38
+ ... @staticmethod
39
+ ... def get_load_strategy() -> ModuleLoadStrategy:
40
+ ... return ModuleLoadStrategy(
41
+ ... lazy_load=False,
42
+ ... priority=100
43
+ ... )
44
+
45
+ 或使用字典:
46
+ >>> class MyModule(BaseModule):
47
+ ... @staticmethod
48
+ ... def get_load_strategy() -> dict:
49
+ ... return {
50
+ ... "lazy_load": False,
51
+ ... "priority": 100
52
+ ... }
53
+ {!--< /tips >!--}
54
+ """
55
+ ...
19
56
  def should_eager_load() -> bool:
20
57
  """
21
58
  模块是否应该在启动时加载
22
59
  默认为False(即懒加载)
23
60
 
61
+ 兼容方法,实际调用 get_load_strategy()
62
+
24
63
  :return: 是否应该在启动时加载
64
+
65
+ {!--< tips >!--}
66
+ 旧版方法,建议使用 get_load_strategy() 替代
67
+ {!--< /tips >!--}
25
68
  """
26
69
  ...
27
70
  async def on_load(self: object, event: dict) -> bool:
@@ -94,7 +94,7 @@ class CommandHandler:
94
94
  "main_name": main_name
95
95
  }
96
96
 
97
- # 注册别名映射
97
+ # 注册别名映射(name列表中的额外名称)
98
98
  if cmd_name != main_name:
99
99
  self.aliases[cmd_name] = main_name
100
100
 
@@ -102,6 +102,11 @@ class CommandHandler:
102
102
  if permission and cmd_name not in self.permissions:
103
103
  self.permissions[cmd_name] = permission
104
104
 
105
+ # 注册aliases参数中的别名
106
+ for alias in alias_list:
107
+ if alias not in self.aliases:
108
+ self.aliases[alias] = main_name
109
+
105
110
  # 添加到命令组
106
111
  if group:
107
112
  if group not in self.groups:
@@ -21,7 +21,7 @@ DEFAULT_ERISPULSE_CONFIG = {
21
21
  "memory_limit": 1000
22
22
  },
23
23
  "storage": {
24
- "max_snapshot": 20
24
+ "use_global_db": False,
25
25
  },
26
26
  "modules": {},
27
27
  "adapters": {},
ErisPulse/Core/adapter.py CHANGED
@@ -14,8 +14,9 @@ from .logger import logger
14
14
  from .Bases.adapter import BaseAdapter
15
15
  from .config import config
16
16
  from .lifecycle import lifecycle
17
+ from .Bases.manager import ManagerBase
17
18
 
18
- class AdapterManager:
19
+ class AdapterManager(ManagerBase):
19
20
  """
20
21
  适配器管理器
21
22
 
@@ -269,6 +270,9 @@ class AdapterManager:
269
270
  from .router import router
270
271
  await router.stop()
271
272
 
273
+ # 清空已启动实例集合
274
+ self._started_instances.clear()
275
+
272
276
  # 提交适配器关闭完成事件
273
277
  await lifecycle.submit_event(
274
278
  "adapter.stopped",
@@ -301,9 +305,8 @@ class AdapterManager:
301
305
  :param platform: 平台名称
302
306
  :return: [bool] 平台是否存在
303
307
  """
304
- # 检查平台是否在配置中注册
305
- adapter_statuses = config.getConfig("ErisPulse.adapters.status", {})
306
- return platform in adapter_statuses
308
+ # 检查平台是否已注册(在 _adapters 中)
309
+ return platform in self._adapters
307
310
 
308
311
  def is_enabled(self, platform: str) -> bool:
309
312
  """
@@ -332,10 +335,11 @@ class AdapterManager:
332
335
  :param platform: 平台名称
333
336
  :return: [bool] 操作是否成功
334
337
  """
335
- if not self.exists(platform):
338
+ # 启用平台时自动在配置中注册
339
+ if platform not in self._adapters:
336
340
  logger.error(f"平台 {platform} 不存在")
337
341
  return False
338
-
342
+
339
343
  config.setConfig(f"ErisPulse.adapters.status.{platform}", True)
340
344
  logger.info(f"平台 {platform} 已启用")
341
345
  return True
@@ -347,21 +351,77 @@ class AdapterManager:
347
351
  :param platform: 平台名称
348
352
  :return: [bool] 操作是否成功
349
353
  """
350
- if not self.exists(platform):
354
+ # 禁用平台时自动在配置中注册
355
+ if platform not in self._adapters:
351
356
  logger.error(f"平台 {platform} 不存在")
352
357
  return False
353
-
358
+
354
359
  config.setConfig(f"ErisPulse.adapters.status.{platform}", False)
355
360
  logger.info(f"平台 {platform} 已禁用")
356
361
  return True
357
362
 
363
+ def unregister(self, platform: str) -> bool:
364
+ """
365
+ 取消注册适配器
366
+
367
+ :param platform: 平台名称
368
+ :return: 是否取消成功
369
+
370
+ {!--< internal-use >!--}
371
+ 注意:此方法仅取消注册,不关闭已启动的适配器
372
+ {!--< /internal-use >!--}
373
+ """
374
+ if platform not in self._adapters:
375
+ logger.warning(f"平台 {platform} 未注册")
376
+ return False
377
+
378
+ # 移除适配器实例
379
+ adapter = self._adapters.pop(platform)
380
+
381
+ # 移除平台属性
382
+ if len(platform) <= 10:
383
+ from itertools import product
384
+ combinations = [''.join(c) for c in product(*[(ch.lower(), ch.upper()) for ch in platform])]
385
+ for name in set(combinations):
386
+ if hasattr(self, name):
387
+ delattr(self, name)
388
+ else:
389
+ if hasattr(self, platform.lower()):
390
+ delattr(self, platform.lower())
391
+ if hasattr(self, platform.upper()):
392
+ delattr(self, platform.upper())
393
+ if hasattr(self, platform.capitalize()):
394
+ delattr(self, platform.capitalize())
395
+
396
+ logger.info(f"平台 {platform} 已取消注册")
397
+ return True
398
+
399
+ def list_registered(self) -> List[str]:
400
+ """
401
+ 列出所有已注册的平台
402
+
403
+ :return: 平台名称列表
404
+ """
405
+ return list(self._adapters.keys())
406
+
407
+ def list_items(self) -> Dict[str, bool]:
408
+ """
409
+ 列出所有平台适配器状态
410
+
411
+ :return: {平台名: 是否启用} 字典
412
+ """
413
+ return config.getConfig("ErisPulse.adapters.status", {})
414
+
415
+ # 兼容性方法 - 保持向后兼容
358
416
  def list_adapters(self) -> Dict[str, bool]:
359
417
  """
360
418
  列出所有平台适配器状态
361
-
419
+
420
+ {!--< deprecated >!--} 请使用 list_items() 代替
421
+
362
422
  :return: [Dict[str, bool]] 平台适配器状态字典
363
423
  """
364
- return config.getConfig("ErisPulse.adapters.status", {})
424
+ return self.list_items()
365
425
 
366
426
  # ==================== 事件处理与消息发送 ====================
367
427
 
@@ -18,8 +18,9 @@ from .logger import logger
18
18
  from .Bases.adapter import BaseAdapter
19
19
  from .config import config
20
20
  from .lifecycle import lifecycle
21
+ from .Bases.manager import ManagerBase
21
22
 
22
- class AdapterManager:
23
+ class AdapterManager(ManagerBase):
23
24
  """
24
25
  适配器管理器
25
26
 
@@ -118,10 +119,26 @@ class AdapterManager:
118
119
  :return: [bool] 操作是否成功
119
120
  """
120
121
  ...
122
+ def list_registered(self: object) -> List[str]:
123
+ """
124
+ 列出所有已注册的平台
125
+
126
+ :return: 平台名称列表
127
+ """
128
+ ...
129
+ def list_items(self: object) -> Dict[(str, bool)]:
130
+ """
131
+ 列出所有平台适配器状态
132
+
133
+ :return: {平台名: 是否启用} 字典
134
+ """
135
+ ...
121
136
  def list_adapters(self: object) -> Dict[(str, bool)]:
122
137
  """
123
138
  列出所有平台适配器状态
124
139
 
140
+ {!--< deprecated >!--} 请使用 list_items() 代替
141
+
125
142
  :return: [Dict[str, bool]] 平台适配器状态字典
126
143
  """
127
144
  ...
@@ -108,6 +108,8 @@ def setup_async_loop(loop: asyncio.AbstractEventLoop = None) -> None: # type:
108
108
 
109
109
  sys.excepthook = global_exception_handler
110
110
  try:
111
- asyncio.get_event_loop().set_exception_handler(async_exception_handler)
111
+ loop = asyncio.get_running_loop()
112
+ loop.set_exception_handler(async_exception_handler)
112
113
  except RuntimeError:
113
- pass
114
+ # 没有运行中的事件循环,这是正常的,在运行时再设置
115
+ pass
@@ -120,6 +120,15 @@ class LifecycleManager:
120
120
  :param event: 事件名称
121
121
  :param event_data: 事件数据字典
122
122
  """
123
+ # 验证事件类型
124
+ if event_type is None:
125
+ logger.error("事件类型不能为None")
126
+ return
127
+
128
+ if not isinstance(event_type, str) or not event_type:
129
+ logger.error(f"事件类型必须是非空字符串,收到: {event_type}")
130
+ return
131
+
123
132
  # 构建完整事件数据
124
133
  event_data = {
125
134
  "event": event_type,
ErisPulse/Core/logger.py CHANGED
@@ -91,11 +91,6 @@ class Logger:
91
91
  :param level: 日志级别(DEBUG/INFO/WARNING/ERROR/CRITICAL)
92
92
  :return: bool 设置是否成功
93
93
  """
94
- from .module import module
95
-
96
- if not module.is_enabled(module_name):
97
- self._logger.warning(f"模块 {module_name} 未启用,无法设置日志等级。")
98
- return False
99
94
  level = level.upper()
100
95
  if hasattr(logging, level):
101
96
  self._module_levels[module_name] = getattr(logging, level)
@@ -115,19 +110,21 @@ class Logger:
115
110
  if self._file_handler:
116
111
  self._logger.removeHandler(self._file_handler)
117
112
  self._file_handler.close()
113
+ self._file_handler = None
118
114
 
119
115
  if isinstance(path, str):
120
116
  path = [path]
121
117
 
122
118
  for p in path:
123
119
  try:
124
- file_handler = logging.FileHandler(p, encoding="utf-8")
120
+ self._file_handler = logging.FileHandler(p, encoding="utf-8")
125
121
  # 使用自定义格式化器去除rich markup标签
126
- file_handler.setFormatter(logging.Formatter("[%(name)s] %(message)s"))
127
- self._logger.addHandler(file_handler)
122
+ self._file_handler.setFormatter(logging.Formatter("[%(name)s] %(message)s"))
123
+ self._logger.addHandler(self._file_handler)
128
124
  return True
129
125
  except Exception as e:
130
126
  self._logger.error(f"无法设置日志文件 {p}: {e}")
127
+ self._file_handler = None
131
128
  return False
132
129
 
133
130
  self._logger.warning("出现极端错误,无法设置日志文件。")
@@ -140,9 +137,11 @@ class Logger:
140
137
  :param path: 日志文件路径 Str/List
141
138
  :return: bool 设置是否成功
142
139
  """
143
- if self._logs is None:
140
+ # 检查是否有日志记录
141
+ if not self._logs or all(len(logs) == 0 for logs in self._logs.values()):
144
142
  self._logger.warning("没有log记录可供保存。")
145
143
  return False
144
+
146
145
  if isinstance(path, str):
147
146
  path = [path]
148
147
 
@@ -162,16 +161,18 @@ class Logger:
162
161
  self._logger.warning("出现极端错误,无法保存日志。")
163
162
  return False
164
163
 
165
- def get_logs(self, module_name: str = "Unknown") -> dict:
164
+ def get_logs(self, module_name: str = None) -> dict:
166
165
  """
167
166
  获取日志内容
168
167
 
169
- :param module_name (可选): 模块名称
168
+ :param module_name (可选): 模块名称,None表示获取所有日志
170
169
  :return: dict 日志内容
171
170
  """
172
- if module_name:
173
- return {module_name: self._logs.get(module_name, [])}
174
- return {k: v.copy() for k, v in self._logs.items()}
171
+ if module_name is None:
172
+ # 返回所有日志
173
+ return {k: v.copy() for k, v in self._logs.items()}
174
+ # 返回指定模块的日志
175
+ return {module_name: self._logs.get(module_name, [])}
175
176
 
176
177
  def _save_in_memory(self, ModuleName, msg):
177
178
  if ModuleName not in self._logs:
@@ -226,13 +227,18 @@ class Logger:
226
227
  except Exception:
227
228
  return "Unknown"
228
229
 
229
- def get_child(self, child_name: str = "UnknownChild"):
230
+ def get_child(self, child_name: str = "UnknownChild", *, relative: bool = True):
230
231
  """
231
232
  获取子日志记录器
232
233
 
233
234
  :param child_name: 子模块名称(可选)
235
+ :param relative: 是否相对于调用者模块(默认True),False表示使用完整名称
234
236
  :return: LoggerChild 子日志记录器实例
235
237
  """
238
+ if child_name and not relative:
239
+ # 使用完整的指定名称,不添加前缀
240
+ return LoggerChild(self, child_name)
241
+
236
242
  caller_module = self._get_caller()
237
243
  if child_name:
238
244
  full_module_name = f"{caller_module}.{child_name}"
ErisPulse/Core/logger.pyi CHANGED
@@ -81,7 +81,7 @@ class Logger:
81
81
  """
82
82
  获取日志内容
83
83
 
84
- :param module_name (可选): 模块名称
84
+ :param module_name (可选): 模块名称,None表示获取所有日志
85
85
  :return: dict 日志内容
86
86
  """
87
87
  ...
@@ -98,6 +98,7 @@ class Logger:
98
98
  获取子日志记录器
99
99
 
100
100
  :param child_name: 子模块名称(可选)
101
+ :param relative: 是否相对于调用者模块(默认True),False表示使用完整名称
101
102
  :return: LoggerChild 子日志记录器实例
102
103
  """
103
104
  ...
ErisPulse/Core/module.py CHANGED
@@ -5,13 +5,15 @@ ErisPulse 模块系统
5
5
  """
6
6
 
7
7
  import asyncio
8
+ import warnings
8
9
  from typing import Any, Dict, List, Type, Optional
9
10
  from .logger import logger
10
11
  from .config import config
11
12
  from .Bases import BaseModule
12
13
  from .lifecycle import lifecycle
14
+ from .Bases.manager import ManagerBase
13
15
 
14
- class ModuleManager:
16
+ class ModuleManager(ManagerBase):
15
17
  """
16
18
  模块管理器
17
19
 
@@ -48,9 +50,16 @@ class ModuleManager:
48
50
  >>> module.register("MyModule", MyModuleClass)
49
51
  """
50
52
  # 严格验证模块类,确保继承自BaseModule
53
+ # 先检查是否为类对象
54
+ if not isinstance(module_class, type):
55
+ error_msg = f"模块 {module_name} 的参数必须是类,而不是 {type(module_class).__name__}"
56
+ logger.error(error_msg)
57
+ raise TypeError(error_msg)
58
+
51
59
  if not issubclass(module_class, BaseModule):
52
60
  warn_msg = f"模块 {module_name} 的类 {module_class.__name__} 没有继承自BaseModule,但我们仍会继续尝试加载这个模块,但请注意这可能引发其他问题"
53
61
  logger.warning(warn_msg)
62
+ warnings.warn(warn_msg, UserWarning)
54
63
  # error_msg = f"模块 {module_name} 的类 {module_class.__name__} 必须继承自BaseModule"
55
64
  # logger.error(error_msg)
56
65
  # raise TypeError(error_msg)
@@ -63,7 +72,9 @@ class ModuleManager:
63
72
 
64
73
  # 检查模块名是否已存在
65
74
  if module_name in self._module_classes:
66
- logger.warning(f"模块 {module_name} 已存在,将覆盖原模块类")
75
+ warn_msg = f"模块 {module_name} 已存在,将覆盖原模块类"
76
+ logger.warning(warn_msg)
77
+ warnings.warn(warn_msg, UserWarning)
67
78
 
68
79
  self._module_classes[module_name] = module_class
69
80
  if module_info:
@@ -189,14 +200,15 @@ class ModuleManager:
189
200
  :param module_name: 模块名称
190
201
  :return: 是否卸载成功
191
202
  """
203
+ # 模块未加载,返回 True(表示没有需要卸载的模块,这不是错误)
192
204
  if module_name not in self._loaded_modules:
193
205
  logger.warning(f"模块 {module_name} 未加载")
194
- return False
206
+ return True
195
207
 
196
208
  try:
197
209
  # 调用模块的on_unload卸载方法
198
- instance = self._modules[module_name]
199
- if hasattr(instance, 'on_unload'):
210
+ instance = self._modules.get(module_name)
211
+ if instance and hasattr(instance, 'on_unload'):
200
212
  try:
201
213
  if asyncio.iscoroutinefunction(instance.on_unload):
202
214
  await instance.on_unload({"module_name": module_name})
@@ -204,9 +216,9 @@ class ModuleManager:
204
216
  instance.on_unload({"module_name": module_name})
205
217
  except Exception as e:
206
218
  logger.error(f"模块 {module_name} on_unload 方法执行失败: {e}")
207
-
219
+
208
220
  # 清理缓存
209
- del self._modules[module_name]
221
+ del self._modules[module_name]
210
222
  self._loaded_modules.discard(module_name)
211
223
 
212
224
  logger.info(f"模块 {module_name} 卸载成功")
@@ -334,13 +346,49 @@ class ModuleManager:
334
346
  self._loaded_modules.discard(module_name)
335
347
  return True
336
348
 
349
+ def unregister(self, module_name: str) -> bool:
350
+ """
351
+ 取消注册模块
352
+
353
+ :param module_name: 模块名称
354
+ :return: 是否取消成功
355
+
356
+ {!--< internal-use >!--}
357
+ 注意:此方法仅取消注册,不卸载已加载的模块
358
+ {!--< /internal-use >!--}
359
+ """
360
+ if module_name not in self._module_classes:
361
+ logger.warning(f"模块 {module_name} 未注册")
362
+ return False
363
+
364
+ # 移除模块类
365
+ self._module_classes.pop(module_name)
366
+
367
+ # 移除模块信息
368
+ if module_name in self._module_info:
369
+ self._module_info.pop(module_name)
370
+
371
+ logger.info(f"模块 {module_name} 已取消注册")
372
+ return True
373
+
374
+ def list_items(self) -> Dict[str, bool]:
375
+ """
376
+ 列出所有模块状态
377
+
378
+ :return: {模块名: 是否启用} 字典
379
+ """
380
+ return config.getConfig("ErisPulse.modules.status", {})
381
+
382
+ # 兼容性方法 - 保持向后兼容
337
383
  def list_modules(self) -> Dict[str, bool]:
338
384
  """
339
385
  列出所有模块状态
340
386
 
387
+ {!--< deprecated >!--} 请使用 list_items() 代替
388
+
341
389
  :return: [Dict[str, bool]] 模块状态字典
342
390
  """
343
- return config.getConfig("ErisPulse.modules.status", {})
391
+ return self.list_items()
344
392
 
345
393
  # ==================== 工具方法 ====================
346
394
 
@@ -370,4 +418,4 @@ module = ModuleManager()
370
418
 
371
419
  __all__ = [
372
420
  "module"
373
- ]
421
+ ]
ErisPulse/Core/module.pyi CHANGED
@@ -11,13 +11,15 @@ ErisPulse 模块系统
11
11
  """
12
12
 
13
13
  import asyncio
14
+ import warnings
14
15
  from typing import Any, Dict, List, Type, Optional
15
16
  from .logger import logger
16
17
  from .config import config
17
18
  from .Bases import BaseModule
18
19
  from .lifecycle import lifecycle
20
+ from .Bases.manager import ManagerBase
19
21
 
20
- class ModuleManager:
22
+ class ModuleManager(ManagerBase):
21
23
  """
22
24
  模块管理器
23
25
 
@@ -152,10 +154,19 @@ class ModuleManager:
152
154
  :return: [bool] 操作是否成功
153
155
  """
154
156
  ...
157
+ def list_items(self: object) -> Dict[(str, bool)]:
158
+ """
159
+ 列出所有模块状态
160
+
161
+ :return: {模块名: 是否启用} 字典
162
+ """
163
+ ...
155
164
  def list_modules(self: object) -> Dict[(str, bool)]:
156
165
  """
157
166
  列出所有模块状态
158
167
 
168
+ {!--< deprecated >!--} 请使用 list_items() 代替
169
+
159
170
  :return: [Dict[str, bool]] 模块状态字典
160
171
  """
161
172
  ...
ErisPulse/Core/router.py CHANGED
@@ -235,13 +235,20 @@ class RouterManager:
235
235
  try:
236
236
  full_path = f"/{module_name}{path}"
237
237
 
238
- # 使用类型忽略注释
239
- if full_path in self.app.websocket_routes: # type: ignore || 原因:实际上,FastAPI的API提供了websocket_routes属性
240
- self.app.remove_api_websocket_route(full_path) # type: ignore || 原因:实际上,FastAPI的API提供了remove_api_websocket_route方法
238
+ # 检查 WebSocket 路由是否存在于我们的内部记录中
239
+ if full_path in self._websocket_routes.get(module_name, {}):
241
240
  display_url = self._format_display_url(f"{self.base_url}{full_path}")
242
241
  logger.info(f"注销WebSocket: {display_url}")
243
242
  del self._websocket_routes[module_name][full_path]
243
+
244
+ # 从 FastAPI 路由列表中移除对应的 WebSocket 路由
245
+ # FastAPI 的 WebSocket 路由有 websocket_endpoint 属性
246
+ self.app.router.routes = [
247
+ route for route in self.app.router.routes
248
+ if not (hasattr(route, 'path') and route.path == full_path)
249
+ ]
244
250
  return True
251
+
245
252
  display_url = self._format_display_url(f"{self.base_url}{full_path}")
246
253
  logger.error(f"注销WebSocket失败: 路径 {display_url} 不存在")
247
254
  return False
@@ -339,8 +346,9 @@ class RouterManager:
339
346
  if "0.0.0.0" in url:
340
347
  display_url = url.replace("0.0.0.0", "127.0.0.1")
341
348
  return f"{url} (可访问: {display_url})"
342
- elif "::" in url:
343
- display_url = url.replace("::", "localhost")
349
+ elif "[::]" in url:
350
+ # IPv6 回环地址需要替换括号
351
+ display_url = url.replace("[::]", "localhost")
344
352
  return f"{url} (可访问: {display_url})"
345
353
  return url
346
354