Coverage for src/symphra_modules/registry.py: 82.96%
119 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-26 18:16 +0800
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-26 18:16 +0800
1"""模块注册表实现."""
3from typing import Any
5from symphra_modules.abc import BaseModule, ModuleInterface
6from symphra_modules.config import ModuleInfo, ModuleState
7from symphra_modules.exceptions import ModuleNotFoundException, ModuleStateError
8from symphra_modules.utils import get_logger, now
10logger = get_logger()
13class ModuleRegistry:
14 """模块注册表,管理模块实例和生命周期."""
16 def __init__(self) -> None:
17 """初始化注册表."""
18 self._modules: dict[str, ModuleInterface] = {}
19 self._module_info: dict[str, ModuleInfo] = {}
21 def register(
22 self, name: str, module_class: type[BaseModule], config: dict[str, Any] | None = None
23 ) -> ModuleInterface:
24 """注册模块.
26 Args:
27 name: 模块名称
28 module_class: 模块类
29 config: 初始配置(可选)
31 Returns:
32 模块实例
34 Raises:
35 ModuleStateError: 模块已注册时抛出
36 """
37 if name in self._modules:
38 raise ModuleStateError(f"模块 {name} 已经注册")
40 module_instance = module_class(config)
41 module_instance.bootstrap() # 调用引导方法
43 # 创建模块信息
44 module_info = ModuleInfo(
45 metadata=module_instance.metadata,
46 state=ModuleState.LOADED,
47 loaded_at=now(),
48 )
49 self._modules[name] = module_instance
50 self._module_info[name] = module_info
51 logger.info(f"模块 {name} 注册成功")
53 return module_instance
55 def unregister(self, name: str) -> None:
56 """注销模块.
58 Args:
59 name: 模块名称
60 """
61 _, info = self._get_module_and_info(name)
62 if info.state == ModuleState.STARTED: 62 ↛ 63line 62 didn't jump to line 63 because the condition on line 62 was never true
63 self.stop(name)
65 del self._modules[name]
66 del self._module_info[name]
68 logger.info(f"模块 {name} 注销成功")
70 def install(self, name: str, config: dict[str, Any] | None = None) -> None:
71 """安装模块.
73 Args:
74 name: 模块名称
75 config: 安装配置(可选)
77 Raises:
78 ModuleStateError: 状态不正确时抛出
79 """
80 module, info = self._get_module_and_info(name)
82 # 已加载的模块才允许安装
83 if info.state != ModuleState.LOADED:
84 raise ModuleStateError(
85 f"模块 {name} 状态错误,当前状态为 {info.state},期望状态为 {ModuleState.LOADED}",
86 module_name=name,
87 current_state=info.state,
88 expected_states=[ModuleState.LOADED],
89 )
91 try:
92 module.install(config)
93 info.state = ModuleState.INSTALLED
94 info.installed_at = now()
96 if config:
97 module.configure(config)
98 info.config = config
100 logger.info(f"模块 {name} 安装成功")
101 except Exception as e:
102 info.state = ModuleState.ERROR
103 raise ModuleStateError(f"模块 {name} 安装失败: {e}", module_name=name) from e
105 def uninstall(self, name: str) -> None:
106 """卸载模块.
108 Args:
109 name: 模块名称
111 Raises:
112 ModuleStateError: 状态不正确时抛出
113 """
114 module, info = self._get_module_and_info(name)
116 if info.state not in [ 116 ↛ 120line 116 didn't jump to line 120 because the condition on line 116 was never true
117 ModuleState.INSTALLED,
118 ModuleState.STOPPED,
119 ]:
120 raise ModuleStateError(
121 f"无法卸载模块 {name},当前状态为 {info.state}",
122 module_name=name,
123 current_state=info.state,
124 expected_states=[ModuleState.INSTALLED, ModuleState.STOPPED],
125 )
126 try:
127 module.uninstall()
128 info.state = ModuleState.LOADED
129 info.installed_at = None
130 info.started_at = None
131 logger.info(f"模块 {name} 卸载成功")
132 except Exception as e:
133 info.state = ModuleState.ERROR
134 raise ModuleStateError(f"模块 {name} 卸载失败: {e}", module_name=name) from e
136 def start(self, name: str) -> None:
137 """启动模块.
139 Args:
140 name: 模块名称
142 Raises:
143 ModuleStateError: 状态不正确时抛出
144 """
145 module, info = self._get_module_and_info(name)
147 if info.state not in [ModuleState.INSTALLED, ModuleState.STOPPED]: 147 ↛ 148line 147 didn't jump to line 148 because the condition on line 147 was never true
148 raise ModuleStateError(
149 f"无法启动模块 {name},当前状态为 {info.state}",
150 module_name=name,
151 current_state=info.state,
152 expected_states=[ModuleState.INSTALLED, ModuleState.STOPPED],
153 )
154 try:
155 module.start()
156 info.state = ModuleState.STARTED
157 info.started_at = now()
159 logger.info(f"模块 {name} 启动成功")
161 except Exception as e:
162 info.state = ModuleState.ERROR
163 raise ModuleStateError(f"模块 {name} 启动失败: {e}", module_name=name) from e
165 def stop(self, name: str) -> None:
166 """停止模块.
168 Args:
169 name: 模块名称
171 Raises:
172 ModuleStateError: 停止失败时抛出
173 """
174 module, info = self._get_module_and_info(name)
176 if info.state != ModuleState.STARTED:
177 return # 模块未启动,无需停止
179 try:
180 module.stop()
181 info.state = ModuleState.STOPPED
182 info.started_at = None
184 logger.info(f"模块 {name} 停止成功")
186 except Exception as e:
187 info.state = ModuleState.ERROR
188 raise ModuleStateError(f"模块 {name} 停止失败: {e}", module_name=name) from e
190 def reload(self, name: str) -> None:
191 """重载模块.
193 Args:
194 name: 模块名称
196 Raises:
197 ModuleStateError: 重载失败时抛出
198 """
199 module, _ = self._get_module_and_info(name)
200 try:
201 module.reload()
202 logger.info(f"模块 {name} 重载成功")
203 except Exception as e:
204 self._module_info[name].state = ModuleState.ERROR
205 raise ModuleStateError(f"模块 {name} 重载失败: {e}", module_name=name) from e
207 def configure(self, name: str, config: dict[str, Any] | None = None) -> None:
208 """配置模块.
210 Args:
211 name: 模块名称
212 config: 配置字典(可选)
214 Raises:
215 ModuleStateError: 配置失败时抛出
216 """
217 module, info = self._get_module_and_info(name)
219 try:
220 module.configure(config)
221 info.config = config or {}
222 logger.info(f"模块 {name} 配置成功")
223 except Exception as e:
224 raise ModuleStateError(f"模块 {name} 配置失败: {e}", module_name=name) from e
226 def get(self, name: str) -> ModuleInterface | None:
227 """获取模块实例.
229 Args:
230 name: 模块名称
232 Returns:
233 模块实例,如果不存在则返回 None
234 """
235 return self._modules.get(name)
237 def get_info(self, name: str) -> ModuleInfo | None:
238 """获取模块信息.
240 Args:
241 name: 模块名称
243 Returns:
244 模块信息,如果不存在则返回 None
245 """
246 return self._module_info.get(name)
248 def list_modules(self) -> list[str]:
249 """列出所有已加载模块的名称.
251 Returns:
252 模块名称列表
253 """
254 return list(self._modules.keys())
256 def list_modules_by_state(self, state: ModuleState) -> list[str]:
257 """根据状态列出模块名称.
259 Args:
260 state: 模块状态
262 Returns:
263 符合状态的模块名称列表
264 """
265 return [name for name, info in self._module_info.items() if info.state == state]
267 def get_module_states(self) -> dict[str, ModuleState]:
268 """获取所有模块状态.
270 Returns:
271 模块名到状态的映射
272 """
273 return {name: info.state for name, info in self._module_info.items()}
275 def is_installed(self, name: str) -> bool:
276 """检查模块是否已安装.
278 Args:
279 name: 模块名称
281 Returns:
282 是否已安装
283 """
284 info = self.get_info(name)
285 return info is not None and info.state in [
286 ModuleState.INSTALLED,
287 ModuleState.STARTED,
288 ModuleState.STOPPED,
289 ]
291 def is_loaded(self, name: str) -> bool:
292 """检查模块是否已加载.
294 Args:
295 name: 模块名称
297 Returns:
298 是否已加载
299 """
300 return name in self._modules
302 def is_started(self, name: str) -> bool:
303 """检查模块是否已启动.
305 Args:
306 name: 模块名称
308 Returns:
309 是否已启动
310 """
311 info = self.get_info(name)
312 return info is not None and info.state == ModuleState.STARTED
314 def _ensure_module_loaded(self, name: str) -> None:
315 """确保模块已加载.
317 Args:
318 name: 模块名称
320 Raises:
321 ModuleNotFoundException: 模块未注册时抛出
322 """
323 if name not in self._modules:
324 raise ModuleNotFoundException(f"模块 {name} 未注册", module_name=name)
326 def _get_module_and_info(self, name: str) -> tuple[ModuleInterface, ModuleInfo]:
327 """获取模块实例和信息.
329 Args:
330 name: 模块名称
332 Returns:
333 (模块实例, 模块信息)元组
335 Raises:
336 ModuleNotFoundException: 模块未注册时抛出
337 """
338 self._ensure_module_loaded(name)
339 return self._modules[name], self._module_info[name]