Files
MoviePilot/app/runtime/reload.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

109 lines
3.8 KiB
Python

"""
运行时重载协调,按配置变更触发对应能力的重新初始化。
"""
import inspect
from collections.abc import Awaitable, Callable
from typing import Any, cast
from app.runtime.events import Event, eventmanager
from app.runtime.log import logger
from app.schemas.types import EventType
class ConfigReloadMixin:
"""
配置重载混入类。
继承此 Mixin 类的类,会在配置变更时自动调用 on_config_changed 方法。
在类中定义 CONFIG_WATCH 集合,指定需要监听的配置项
重写 on_config_changed 方法实现具体的重载逻辑
可选地重写 get_reload_name 方法提供模块名称(用于日志显示)
"""
# 统一生命周期管理器可以继承此 Mixin 的重载方法,但由外部唯一负责事件绑定。
CONFIG_RELOAD_MANAGED_EXTERNALLY: bool = False
def __init_subclass__(cls, **kwargs: Any) -> None:
"""
为声明了 CONFIG_WATCH 的子类生成配置变更处理器。
"""
super().__init_subclass__(**kwargs)
if getattr(cls, "CONFIG_RELOAD_MANAGED_EXTERNALLY", False):
return
config_watch = getattr(cls, "CONFIG_WATCH", None)
if not config_watch:
return
is_async = inspect.iscoroutinefunction(cls.on_config_changed)
method_name = "handle_config_changed"
def create_handler(
is_async: bool,
) -> Callable[[ConfigReloadMixin, Event], Awaitable[None] | None]:
"""
按子类重载方法类型创建同步或异步事件包装器。
"""
if is_async:
async def async_wrapper(self: ConfigReloadMixin, event: Event) -> None:
"""
筛选配置键后异步执行子类重载逻辑。
"""
if not event:
return
changed_keys = (
getattr(event.event_data, "key", set()) & config_watch
)
if not changed_keys:
return
logger.info(
f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}..."
)
await cast(Awaitable[None], self.on_config_changed())
return async_wrapper
else:
def wrapper(self: ConfigReloadMixin, event: Event) -> None:
"""
筛选配置键后同步执行子类重载逻辑。
"""
if not event:
return
changed_keys = (
getattr(event.event_data, "key", set()) & config_watch
)
if not changed_keys:
return
logger.info(
f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}..."
)
self.on_config_changed()
return wrapper
handler = create_handler(is_async)
handler.__module__ = cls.__module__
# 事件总线按 __name__ 在实例上解析方法,必须与类上的方法名保持一致,
# 否则闭包默认名 wrapper 会导致处理器被静默跳过
handler.__name__ = method_name
handler.__qualname__ = f"{cls.__name__}.{method_name}"
setattr(cls, method_name, handler)
eventmanager.add_event_listener(EventType.ConfigChanged, handler)
def on_config_changed(self) -> Awaitable[None] | None:
"""
子类重写此方法实现具体重载逻辑。
"""
pass
def get_reload_name(self) -> str | None:
"""
功能/模块名称。
"""
return self.__class__.__name__