refactor: unify module runtime settings

This commit is contained in:
jxxghp
2026-08-24 00:19:57 +08:00
parent 25ad924aac
commit 6b65dc5d12
11 changed files with 148 additions and 30 deletions
@@ -10,16 +10,11 @@
"root": "app"
},
"settings_imports": {
"count": 8,
"count": 3,
"files": [
"app/db/base.py",
"app/db/engine.py",
"app/db/session.py",
"app/modules/postgresql/__init__.py",
"app/modules/qbittorrent/__init__.py",
"app/modules/redis/__init__.py",
"app/modules/rtorrent/__init__.py",
"app/modules/transmission/__init__.py"
"app/db/session.py"
]
},
"system_config_oper_constructions": {
+6 -6
View File
@@ -14,7 +14,7 @@
"workflow_to_db": []
},
"edge_count": 6499,
"edge_sha256": "f21eea0878e60d4b955a87da15cf0653eaecf07d147dc43e9eabc9f9573bb793",
"edge_sha256": "54ddd2d6cd5669354a2d1303b24c95e573368e145fdd74e4240b01bb8ef1f687",
"edges": [
"app -> app.runtime",
"app -> app.runtime.compat",
@@ -4808,7 +4808,7 @@
"app.modules.postgresql -> app.application.database",
"app.modules.postgresql -> app.modules",
"app.modules.postgresql -> app.runtime",
"app.modules.postgresql -> app.runtime.config",
"app.modules.postgresql -> app.runtime.settings",
"app.modules.postgresql -> app.schemas",
"app.modules.postgresql -> app.schemas.types",
"app.modules.qbittorrent -> app.domain",
@@ -4821,8 +4821,8 @@
"app.modules.qbittorrent -> app.modules._base",
"app.modules.qbittorrent -> app.modules.qbittorrent.qbittorrent",
"app.modules.qbittorrent -> app.runtime",
"app.modules.qbittorrent -> app.runtime.config",
"app.modules.qbittorrent -> app.runtime.log",
"app.modules.qbittorrent -> app.runtime.settings",
"app.modules.qbittorrent -> app.schemas",
"app.modules.qbittorrent -> app.schemas.dashboard",
"app.modules.qbittorrent -> app.schemas.transfer",
@@ -4879,7 +4879,7 @@
"app.modules.redis -> app.adapters.cache.redis",
"app.modules.redis -> app.modules",
"app.modules.redis -> app.runtime",
"app.modules.redis -> app.runtime.config",
"app.modules.redis -> app.runtime.settings",
"app.modules.redis -> app.schemas",
"app.modules.redis -> app.schemas.types",
"app.modules.rtorrent -> app.domain",
@@ -4892,8 +4892,8 @@
"app.modules.rtorrent -> app.modules._base",
"app.modules.rtorrent -> app.modules.rtorrent.rtorrent",
"app.modules.rtorrent -> app.runtime",
"app.modules.rtorrent -> app.runtime.config",
"app.modules.rtorrent -> app.runtime.log",
"app.modules.rtorrent -> app.runtime.settings",
"app.modules.rtorrent -> app.schemas",
"app.modules.rtorrent -> app.schemas.dashboard",
"app.modules.rtorrent -> app.schemas.transfer",
@@ -5248,8 +5248,8 @@
"app.modules.transmission -> app.modules._base",
"app.modules.transmission -> app.modules.transmission.transmission",
"app.modules.transmission -> app.runtime",
"app.modules.transmission -> app.runtime.config",
"app.modules.transmission -> app.runtime.log",
"app.modules.transmission -> app.runtime.settings",
"app.modules.transmission -> app.schemas",
"app.modules.transmission -> app.schemas.dashboard",
"app.modules.transmission -> app.schemas.transfer",
+15
View File
@@ -926,6 +926,21 @@ def test_runtime_consumers_use_command_application_facade():
assert violations == {}
def test_modules_read_deployment_settings_through_runtime_port():
"""宿主 Module 不得绕过 runtime 配置端口直接依赖 Settings 实例。"""
violations: list[str] = []
for path in (APP_ROOT / "modules").rglob("*.py"):
tree = ast.parse(path.read_text(encoding="utf-8-sig"), filename=str(path))
for node in ast.walk(tree):
if not isinstance(node, ast.ImportFrom) or node.module != "app.runtime.config":
continue
if any(alias.name == "settings" for alias in node.names):
violations.append(path.relative_to(PROJECT_ROOT).as_posix())
break
assert violations == []
def test_api_does_not_import_factory():
"""装配器(factory)只允许 app.main 使用,HTTP 端点不得回引。"""
violations: dict[str, set[str]] = {}
+61 -8
View File
@@ -13,8 +13,16 @@ def _load_downloader_base():
app_module.__path__ = []
helper_module = types.ModuleType("app.helper")
helper_module.__path__ = []
runtime_module = types.ModuleType("app.runtime")
runtime_module.__path__ = []
runtime_extensions_module = types.ModuleType("app.runtime.extensions")
runtime_extensions_module.__path__ = []
service_module = types.ModuleType("app.runtime.extensions.service_config")
log_module = types.ModuleType("app.runtime.log")
schemas_module = types.ModuleType("app.schemas")
schemas_module.__path__ = []
schema_message_module = types.ModuleType("app.schemas.message")
schema_system_module = types.ModuleType("app.schemas.system")
schema_types_module = types.ModuleType("app.schemas.types")
utils_module = types.ModuleType("app.utils")
utils_module.__path__ = []
@@ -27,6 +35,13 @@ def _load_downloader_base():
class _ConfigReloadMixin:
pass
class _Logger:
"""隔离模块基类加载时使用的无副作用日志桩。"""
def error(self, *_args, **_kwargs):
"""忽略测试范围外的错误日志输出。"""
pass
class _ServiceConfigHelper:
@staticmethod
def get_downloader_configs():
@@ -59,24 +74,37 @@ def _load_downloader_base():
)
service_module.ServiceConfigHelper = _ServiceConfigHelper
log_module.logger = _Logger()
mixins_module.ConfigReloadMixin = _ConfigReloadMixin
schemas_module.Message = object
schemas_module.NotificationConf = object
schemas_module.MediaServerConf = object
schemas_module.DownloaderConf = object
schema_message_module.Message = object
schema_system_module.NotificationConf = object
schema_system_module.MediaServerConf = object
schema_system_module.DownloaderConf = object
app_module.helper = helper_module
app_module.runtime = runtime_module
app_module.schemas = schemas_module
app_module.utils = utils_module
helper_module.service = service_module
runtime_module.extensions = runtime_extensions_module
runtime_module.log = log_module
runtime_module.reload = mixins_module
runtime_extensions_module.service_config = service_module
schemas_module.message = schema_message_module
schemas_module.system = schema_system_module
schemas_module.types = schema_types_module
utils_module.mixins = mixins_module
stub_modules = {
"app": app_module,
"app.helper": helper_module,
"app.runtime": runtime_module,
"app.runtime.extensions": runtime_extensions_module,
"app.runtime.extensions.service_config": service_module,
"app.runtime.log": log_module,
"app.schemas": schemas_module,
"app.schemas.message": schema_message_module,
"app.schemas.system": schema_system_module,
"app.schemas.types": schema_types_module,
"app.utils": utils_module,
"app.runtime.reload": mixins_module,
@@ -108,7 +136,10 @@ def _load_transmission_module():
torrent_rules_module = types.ModuleType("app.domain.torrent")
size_tools_module = types.ModuleType("app.foundation.size")
temporal_tools_module = types.ModuleType("app.foundation.temporal")
runtime_module = types.ModuleType("app.runtime")
runtime_module.__path__ = []
cache_module = types.ModuleType("app.runtime.cache")
runtime_settings_module = types.ModuleType("app.runtime.settings")
base_module = types.ModuleType("app.modules._base")
modules_module = types.ModuleType("app.modules")
modules_module.__path__ = []
@@ -116,6 +147,9 @@ def _load_transmission_module():
transmission_package_module.__path__ = []
transmission_client_module = types.ModuleType("app.modules.transmission.transmission")
schemas_module = types.ModuleType("app.schemas")
schemas_module.__path__ = []
schema_dashboard_module = types.ModuleType("app.schemas.dashboard")
schema_transfer_module = types.ModuleType("app.schemas.transfer")
schema_types_module = types.ModuleType("app.schemas.types")
config_module = types.ModuleType("app.runtime.config")
metainfo_module = types.ModuleType("app.domain.metainfo")
@@ -225,12 +259,19 @@ def _load_transmission_module():
def get(self, *_args, **_kwargs):
return None
class _RuntimeSettingsCompat:
"""隔离测试用动态配置代理,保持生产模块的兼容读取语义。"""
def __getattr__(self, key):
"""从测试提供的旧 Settings 桩读取配置项。"""
return getattr(config_module.settings, key)
transmission_client_module.Transmission = object
cache_module.FileCache = _FileCache
schemas_module.TransferTorrent = _TransferTorrent
schemas_module.DownloadingTorrent = _DownloadingTorrent
schemas_module.DownloaderTorrent = _DownloaderTorrent
schemas_module.DownloaderInfo = object
schema_transfer_module.TransferTorrent = _TransferTorrent
schema_transfer_module.DownloadingTorrent = _DownloadingTorrent
schema_transfer_module.DownloaderTorrent = _DownloaderTorrent
schema_dashboard_module.DownloaderInfo = object
schema_types_module.TorrentStatus = TorrentStatus
schema_types_module.TorrentQueryStatus = TorrentQueryStatus
schema_types_module.DownloadTaskState = DownloadTaskState
@@ -239,6 +280,7 @@ def _load_transmission_module():
"DownloaderType", {"Transmission": "Transmission"}
)
config_module.settings = SimpleNamespace(TORRENT_TAG="moviepilot-tag")
runtime_settings_module.RuntimeSettingsCompat = _RuntimeSettingsCompat
metainfo_module.MetaInfo = _MetaInfo
log_module.logger = _Logger()
modules_module._ModuleBase = _ModuleBase
@@ -257,6 +299,7 @@ def _load_transmission_module():
app_module.domain = domain_module
app_module.foundation = foundation_module
app_module.modules = modules_module
app_module.runtime = runtime_module
app_module.schemas = schemas_module
domain_module.torrent = torrent_rules_module
foundation_module.size = size_tools_module
@@ -264,8 +307,14 @@ def _load_transmission_module():
core_module.cache = cache_module
core_module.config = config_module
core_module.metainfo = metainfo_module
runtime_module.cache = cache_module
runtime_module.config = config_module
runtime_module.log = log_module
runtime_module.settings = runtime_settings_module
modules_module.transmission = transmission_package_module
transmission_package_module.transmission = transmission_client_module
schemas_module.dashboard = schema_dashboard_module
schemas_module.transfer = schema_transfer_module
schemas_module.types = schema_types_module
torrentool_module.torrent = torrentool_torrent_module
@@ -279,13 +328,17 @@ def _load_transmission_module():
"app.foundation.temporal": temporal_tools_module,
"app.runtime.cache": cache_module,
"app.runtime.config": config_module,
"app.runtime": runtime_module,
"app.domain.metainfo": metainfo_module,
"app.runtime.log": log_module,
"app.runtime.settings": runtime_settings_module,
"app.modules": modules_module,
"app.modules._base": base_module,
"app.modules.transmission": transmission_package_module,
"app.modules.transmission.transmission": transmission_client_module,
"app.schemas": schemas_module,
"app.schemas.dashboard": schema_dashboard_module,
"app.schemas.transfer": schema_transfer_module,
"app.schemas.types": schema_types_module,
"transmission_rpc": transmission_rpc_module,
"torrentool": torrentool_module,
+29 -4
View File
@@ -26,11 +26,17 @@ def _load_qbittorrent_modules():
modules_module.__path__ = []
qbittorrent_package_module = types.ModuleType("app.modules.qbittorrent")
qbittorrent_package_module.__path__ = []
runtime_module = types.ModuleType("app.runtime")
runtime_module.__path__ = []
log_module = types.ModuleType("app.runtime.log")
cache_module = types.ModuleType("app.runtime.cache")
config_module = types.ModuleType("app.runtime.config")
runtime_settings_module = types.ModuleType("app.runtime.settings")
metainfo_module = types.ModuleType("app.domain.metainfo")
schemas_module = types.ModuleType("app.schemas")
schemas_module.__path__ = []
schema_dashboard_module = types.ModuleType("app.schemas.dashboard")
schema_transfer_module = types.ModuleType("app.schemas.transfer")
schema_types_module = types.ModuleType("app.schemas.types")
torrentool_module = types.ModuleType("torrentool")
torrentool_module.__path__ = []
@@ -78,6 +84,13 @@ def _load_qbittorrent_modules():
def get(self, *_args, **_kwargs):
return None
class _RuntimeSettingsCompat:
"""隔离测试用动态配置代理,保持生产模块的兼容读取语义。"""
def __getattr__(self, key):
"""从测试提供的旧 Settings 桩读取配置项。"""
return getattr(config_module.settings, key)
class _MetaInfo:
def __init__(self, name):
self.name = name
@@ -160,11 +173,12 @@ def _load_qbittorrent_modules():
log_module.logger = _Logger()
cache_module.FileCache = _FileCache
config_module.settings = types.SimpleNamespace(TORRENT_TAG="moviepilot-tag")
runtime_settings_module.RuntimeSettingsCompat = _RuntimeSettingsCompat
metainfo_module.MetaInfo = _MetaInfo
schemas_module.DownloaderInfo = object
schemas_module.TransferTorrent = object
schemas_module.DownloadingTorrent = object
schemas_module.DownloaderTorrent = _DownloaderTorrent
schema_dashboard_module.DownloaderInfo = object
schema_transfer_module.TransferTorrent = object
schema_transfer_module.DownloadingTorrent = object
schema_transfer_module.DownloaderTorrent = _DownloaderTorrent
schema_types_module.TorrentStatus = TorrentStatus
schema_types_module.TorrentQueryStatus = TorrentQueryStatus
schema_types_module.DownloadTaskState = DownloadTaskState
@@ -195,6 +209,7 @@ def _load_qbittorrent_modules():
app_module.foundation = foundation_module
app_module.log = log_module
app_module.modules = modules_module
app_module.runtime = runtime_module
app_module.schemas = schemas_module
domain_module.torrent = torrent_rules_module
foundation_module.size = size_tools_module
@@ -204,6 +219,12 @@ def _load_qbittorrent_modules():
core_module.cache = cache_module
core_module.config = config_module
core_module.metainfo = metainfo_module
runtime_module.cache = cache_module
runtime_module.config = config_module
runtime_module.log = log_module
runtime_module.settings = runtime_settings_module
schemas_module.dashboard = schema_dashboard_module
schemas_module.transfer = schema_transfer_module
schemas_module.types = schema_types_module
modules_module.qbittorrent = qbittorrent_package_module
torrentool_module.torrent = torrentool_torrent_module
@@ -220,12 +241,16 @@ def _load_qbittorrent_modules():
"app.foundation.url": url_tools_module,
"app.runtime.cache": cache_module,
"app.runtime.config": config_module,
"app.runtime": runtime_module,
"app.domain.metainfo": metainfo_module,
"app.runtime.log": log_module,
"app.runtime.settings": runtime_settings_module,
"app.modules": modules_module,
"app.modules._base": base_module,
"app.modules.qbittorrent": qbittorrent_package_module,
"app.schemas": schemas_module,
"app.schemas.dashboard": schema_dashboard_module,
"app.schemas.transfer": schema_transfer_module,
"app.schemas.types": schema_types_module,
"qbittorrentapi": qbittorrentapi_module,
"qbittorrentapi.client": qbittorrentapi_client_module,