From ef6a613eb3083a8f1712131d41e48d707b4356b9 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 23 Aug 2026 02:16:03 +0800 Subject: [PATCH] refactor: add dynamic runtime settings compatibility proxy --- app/adapters/external/market.py | 4 +++- app/adapters/external/server.py | 6 +++++- app/adapters/system/plugin/package.py | 10 +++++++--- app/runtime/settings.py | 18 ++++++++++++++++++ .../backend-architecture-next-stage.md | 5 ++++- .../configuration-debt-baseline.json | 4 +--- .../architecture/dependency-baseline.json | 6 +++--- 7 files changed, 41 insertions(+), 12 deletions(-) diff --git a/app/adapters/external/market.py b/app/adapters/external/market.py index f05371852..3b5b61a79 100644 --- a/app/adapters/external/market.py +++ b/app/adapters/external/market.py @@ -30,7 +30,7 @@ from importlib.metadata import distributions from requests import Response from app.runtime.cache import cached, is_fresh -from app.runtime.config import settings +from app.runtime.settings import RuntimeSettingsCompat from app.adapters.system.package import ( PackageInstallRequest, build_package_install_strategies, @@ -52,6 +52,8 @@ from app.adapters.system.host import SystemUtils from app.foundation.url import UrlUtils from version import APP_VERSION +# 保留模块级可替换入口,代理默认读取组合根的最新 runtime 配置。 +settings = RuntimeSettingsCompat() PLUGIN_DIR = Path(settings.ROOT_PATH) / "app" / "plugins" LOCAL_REPO_PREFIX = "local://" PLUGIN_SYSTEM_VERSION_FIELD = "system_version" diff --git a/app/adapters/external/server.py b/app/adapters/external/server.py index 1eb2c4c65..c90547650 100644 --- a/app/adapters/external/server.py +++ b/app/adapters/external/server.py @@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from urllib.parse import parse_qs, quote, urlparse, urlsplit from app.runtime.cache import cached -from app.runtime.config import settings +from app.runtime.settings import RuntimeSettingsCompat from app.domain.context import MediaInfo, MusicInfo from app.domain.meta.metabase import MetaBase from app.runtime.log import logger @@ -24,6 +24,10 @@ from app.adapters.system.host import SystemUtils from version import APP_VERSION, FRONTEND_VERSION +# 保留旧插件可覆盖的模块级入口,默认通过 runtime 代理动态读取配置。 +settings = RuntimeSettingsCompat() + + _server_report_service: Any = None _server_sharing_service: Any = None diff --git a/app/adapters/system/plugin/package.py b/app/adapters/system/plugin/package.py index 8240b2a1d..531f4f8c6 100644 --- a/app/adapters/system/plugin/package.py +++ b/app/adapters/system/plugin/package.py @@ -12,7 +12,11 @@ from typing import Optional from app.adapters.external.market import PluginHelper as _PluginHelper from app.runtime.log import logger -from app.runtime.settings import get_runtime_setting +from app.runtime.settings import RuntimeSettingsCompat + + +# 保留旧模块级入口,插件本地同步测试和旧扩展仍可能覆盖这些设置。 +settings = RuntimeSettingsCompat() @dataclass(frozen=True, slots=True) @@ -38,7 +42,7 @@ class PluginPackageManager: def _plugin_dir(plugin_id: str) -> Path: """解析插件运行目录并拒绝越出宿主插件根目录的标识。""" plugins_root = ( - Path(get_runtime_setting("ROOT_PATH")) / "app" / "plugins" + Path(settings.ROOT_PATH) / "app" / "plugins" ).resolve() plugin_dir = (plugins_root / plugin_id.lower()).resolve() if plugin_dir == plugins_root or not plugin_dir.is_relative_to(plugins_root): @@ -49,7 +53,7 @@ class PluginPackageManager: """在包变更前创建独立快照,供后续提交或补偿恢复。""" plugin_dir = self._plugin_dir(plugin_id) transaction_dir = ( - Path(get_runtime_setting("TEMP_PATH")) + Path(settings.TEMP_PATH) / "plugin_transactions" / f"{plugin_id.lower()}-{uuid.uuid4().hex}" ) diff --git a/app/runtime/settings.py b/app/runtime/settings.py index 9d57e9101..7c2b75dc4 100644 --- a/app/runtime/settings.py +++ b/app/runtime/settings.py @@ -11,6 +11,24 @@ RuntimeSettingProvider = Callable[[str], Any] _provider: RuntimeSettingProvider | None = None +class RuntimeSettingsCompat: + """为旧模块级 Settings 访问提供动态 runtime 配置代理。""" + + def __getattr__(self, key: str) -> Any: + """读取当前组合根配置;未装配时沿用旧 Settings 回退。""" + return get_runtime_setting(key) + + def __setattr__(self, key: str, value: Any) -> None: + """把旧模块级覆盖同步到 legacy Settings,保持测试和插件注入语义。""" + legacy_settings = importlib.import_module("app.runtime.config").settings + setattr(legacy_settings, key, value) + + def __delattr__(self, key: str) -> None: + """删除旧模块级覆盖,使配置对象恢复其原有属性解析。""" + legacy_settings = importlib.import_module("app.runtime.config").settings + delattr(legacy_settings, key) + + def configure_runtime_setting_provider(provider: RuntimeSettingProvider) -> None: """由启动组合根登记配置读取器,保持适配器只依赖 runtime 端口。""" global _provider diff --git a/docs/refactor/backend-architecture-next-stage.md b/docs/refactor/backend-architecture-next-stage.md index d9f542113..b17c57563 100644 --- a/docs/refactor/backend-architecture-next-stage.md +++ b/docs/refactor/backend-architecture-next-stage.md @@ -70,7 +70,7 @@ MoviePilot V3 当前不是“目录混乱、必须推倒重来”的状态。第 | legacy 默认模块契约 | 0 个宿主观察方法;未知动态方法保留 fallback | 所有静态宿主方法已有显式 V2 spec;真实 fallback 命中由 `module.contract.legacy_hit` 观测 | | 事件枚举 | 53 | 66 个静态 producer、15 个静态 consumer | | 专用 EventData model | 53 | Event Contract Registry 已为全部事件登记 typed payload/fallback 原因 | -| 直接读取 `settings` 的文件 | 107 | 仍按模块族迁移,动态协议和安全端口暂保留 | +| 直接读取 `settings` 的文件 | 105 | 仍按模块族迁移,动态协议和安全端口暂保留 | | `SystemConfigOper()` | 1 个 | 仅组合根创建 `SystemConfigService` 时保留 | | Model 上的 DB 查询装饰器 | 117 | `db_update`/`async_db_update` 为 0;查询 ABI 继续按 canonical 用例迁移 | | 路由端点 | 335 | 11 个已装饰端点超过 80 行,最大 400 行 | @@ -1031,6 +1031,9 @@ MFA/Passkey 专项测试与架构门禁通过,密钥类配置仍保留在安 Pylint 与架构基线通过,配置债务由 112 个文件降至 109 个文件。 缓存 Redis 连接池、内存限制和文件缓存工厂随后改用 runtime 配置端口,保留旧模块级 Settings 覆盖入口; 缓存专项 41 项测试与 Pylint 通过,配置债务由 109 个文件降至 107 个文件。 +同时为兼容市场、服务端和插件包适配器增加 `RuntimeSettingsCompat` 动态代理:组合根已装配时读取 +runtime provider,旧插件或测试替换模块级 `settings` 时仍保持原覆盖语义。相关插件市场、插件本地同步、 +服务端和评分专项 184 项测试与 Pylint 通过,配置债务由 107 个文件降至 105 个文件。 用户模型的 `get_by_name` 与 `get_by_id` 同步查询改为显式 Session 执行,并以一次性短会话保留旧插件 无 Session ABI;用户查询与兼容专项 75 项测试、Pylint 及架构基线通过,查询装饰器由 119 个降至 117 个。 diff --git a/tests/fixtures/architecture/configuration-debt-baseline.json b/tests/fixtures/architecture/configuration-debt-baseline.json index 5cdf84f5a..3c2580878 100644 --- a/tests/fixtures/architecture/configuration-debt-baseline.json +++ b/tests/fixtures/architecture/configuration-debt-baseline.json @@ -9,10 +9,8 @@ "root": "app" }, "settings_imports": { - "count": 107, + "count": 105, "files": [ - "app/adapters/external/market.py", - "app/adapters/external/server.py", "app/adapters/network/browser.py", "app/adapters/system/fsproxy.py", "app/adapters/web/security/access.py", diff --git a/tests/fixtures/architecture/dependency-baseline.json b/tests/fixtures/architecture/dependency-baseline.json index dd595ad5f..2e8941cea 100644 --- a/tests/fixtures/architecture/dependency-baseline.json +++ b/tests/fixtures/architecture/dependency-baseline.json @@ -14,7 +14,7 @@ "workflow_to_db": [] }, "edge_count": 6421, - "edge_sha256": "694818834db43d9b556226bb11ddfc5420d2475f03e83125b64b7bd41e4a0771", + "edge_sha256": "0bbd08d44dc572b174f49e5a08ad2191e8b256ecceb0f7a765e82d3cddf10853", "edges": [ "app -> app.runtime", "app -> app.runtime.compat", @@ -62,9 +62,9 @@ "app.adapters.external.market -> app.foundation.version", "app.adapters.external.market -> app.runtime", "app.adapters.external.market -> app.runtime.cache", - "app.adapters.external.market -> app.runtime.config", "app.adapters.external.market -> app.runtime.log", "app.adapters.external.market -> app.runtime.observability", + "app.adapters.external.market -> app.runtime.settings", "app.adapters.external.ocr -> app.adapters", "app.adapters.external.ocr -> app.adapters.network", "app.adapters.external.ocr -> app.adapters.network.http", @@ -87,9 +87,9 @@ "app.adapters.external.server -> app.domain.meta.metabase", "app.adapters.external.server -> app.runtime", "app.adapters.external.server -> app.runtime.cache", - "app.adapters.external.server -> app.runtime.config", "app.adapters.external.server -> app.runtime.log", "app.adapters.external.server -> app.runtime.observability", + "app.adapters.external.server -> app.runtime.settings", "app.adapters.external.server -> app.schemas", "app.adapters.external.server -> app.schemas.media", "app.adapters.external.server -> app.schemas.types",