mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-03 14:37:36 +08:00
feat(database): bound async configuration writes
This commit is contained in:
@@ -63,6 +63,7 @@ CONFIGURATION_EXCLUDED_ROOTS = (
|
||||
APP_ROOT / "plugins",
|
||||
APP_ROOT / "sdk",
|
||||
APP_ROOT / "runtime" / "compat",
|
||||
APP_ROOT / "testing",
|
||||
)
|
||||
|
||||
|
||||
@@ -85,6 +86,37 @@ def parse_source(path: Path) -> ast.Module:
|
||||
return ast.parse(path.read_text(encoding="utf-8-sig"), filename=str(path))
|
||||
|
||||
|
||||
def _is_type_checking_test(test: ast.expr) -> bool:
|
||||
"""判断条件是否只在静态类型检查阶段成立。"""
|
||||
return (
|
||||
isinstance(test, ast.Name)
|
||||
and test.id == "TYPE_CHECKING"
|
||||
) or (
|
||||
isinstance(test, ast.Attribute)
|
||||
and isinstance(test.value, ast.Name)
|
||||
and test.value.id == "typing"
|
||||
and test.attr == "TYPE_CHECKING"
|
||||
)
|
||||
|
||||
|
||||
def iter_runtime_import_nodes(tree: ast.AST):
|
||||
"""遍历运行期导入,排除 ``if TYPE_CHECKING`` 内的仅类型依赖。"""
|
||||
parents: dict[ast.AST, ast.AST] = {}
|
||||
for parent in ast.walk(tree):
|
||||
for child in ast.iter_child_nodes(parent):
|
||||
parents[child] = parent
|
||||
for node in ast.walk(tree):
|
||||
if not isinstance(node, (ast.Import, ast.ImportFrom)):
|
||||
continue
|
||||
parent = parents.get(node)
|
||||
while parent is not None:
|
||||
if isinstance(parent, ast.If) and _is_type_checking_test(parent.test):
|
||||
break
|
||||
parent = parents.get(parent)
|
||||
else:
|
||||
yield node
|
||||
|
||||
|
||||
def collect_configuration_debt_baseline() -> dict[str, Any]:
|
||||
"""收集宿主 canonical 代码直接读取 settings 和构造数据库配置适配器的债务。"""
|
||||
settings_files: list[str] = []
|
||||
@@ -124,6 +156,7 @@ def collect_configuration_debt_baseline() -> dict[str, Any]:
|
||||
"app/plugins",
|
||||
"app/sdk",
|
||||
"app/runtime/compat",
|
||||
"app/testing",
|
||||
],
|
||||
},
|
||||
"settings_imports": {
|
||||
@@ -144,7 +177,7 @@ def iter_import_candidates(
|
||||
"""提取模块导入候选,第二项记录 from-import 的具体符号。"""
|
||||
package = module_name if path.name == "__init__.py" else module_name.rpartition(".")[0]
|
||||
candidates: list[tuple[str, Optional[str]]] = []
|
||||
for node in ast.walk(parse_source(path)):
|
||||
for node in iter_runtime_import_nodes(parse_source(path)):
|
||||
if isinstance(node, ast.Import):
|
||||
candidates.extend((alias.name, None) for alias in node.names)
|
||||
continue
|
||||
|
||||
@@ -2383,11 +2383,23 @@ def _apply_local_system_config_inner(config_payload: dict[str, Any]) -> None:
|
||||
from app.startup.database_initializer import prepare_database
|
||||
from app.db.oper.systemconfig import SystemConfigOper
|
||||
from app.schemas.types import SystemConfigKey
|
||||
from app.db.session import SessionFactory, async_session_scope
|
||||
from app.db.uow import configure_transaction_runners
|
||||
from app.startup.transaction import TransactionalWriteRunner
|
||||
except ModuleNotFoundError as exc:
|
||||
raise RuntimeError(
|
||||
"当前环境尚未安装 MoviePilot 运行依赖,请先执行 moviepilot install deps 或 moviepilot setup"
|
||||
) from exc
|
||||
|
||||
transaction_runner = TransactionalWriteRunner(
|
||||
sync_session=SessionFactory,
|
||||
async_session=async_session_scope,
|
||||
)
|
||||
configure_transaction_runners(
|
||||
sync=transaction_runner.sync,
|
||||
async_=transaction_runner.async_,
|
||||
)
|
||||
|
||||
generated_password = None
|
||||
|
||||
def prepare_superuser_password() -> None:
|
||||
|
||||
Reference in New Issue
Block a user