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

222 lines
7.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
验证运行依赖清单、锁定版本与环境约束。
"""
import tomllib
from pathlib import Path
from types import SimpleNamespace
import pytest
import app.runtime.dependencies.profile as dependencies
from app.doctor import dependencies as dependency_doctor
from app.foundation import environment
from scripts import verify_runtime_profile
def test_free_threaded_runtime_tracks_interpreter_build(monkeypatch):
"""
根据解释器编译标记识别自由线程运行时,避免与当前 GIL 开关混淆。
"""
monkeypatch.setattr(
environment.sysconfig,
"get_config_var",
lambda name: 1 if name == "Py_GIL_DISABLED" else None,
)
assert environment.is_free_threaded_runtime() is True
def test_gil_status_tracks_current_interpreter_state(monkeypatch):
"""
查询解释器当前 GIL 状态,支持运行时已禁用 GIL 的情况。
"""
monkeypatch.setattr(environment.sys, "_is_gil_enabled", lambda: False)
assert environment.is_gil_enabled() is False
def test_runtime_dependency_group_tracks_interpreter_abi(monkeypatch):
"""
按解释器 ABI 选择标准或自由线程运行依赖组。
"""
monkeypatch.setattr(dependencies, "is_free_threaded_runtime", lambda: False)
assert dependencies.runtime_dependency_group() == "runtime-standard"
monkeypatch.setattr(dependencies, "is_free_threaded_runtime", lambda: True)
assert dependencies.runtime_dependency_group() == "runtime-free-threaded"
def test_runtime_requirements_include_project_and_active_group(tmp_path: Path, monkeypatch):
"""
运行依赖同时包含项目公共依赖与当前 ABI 对应的依赖组。
"""
project_file = tmp_path / "pyproject.toml"
project_file.write_text(
"""
[project]
dependencies = ["shared==1"]
[dependency-groups]
runtime-standard = ["standard==2"]
runtime-free-threaded = ["free-threaded==3"]
""",
encoding="utf-8",
)
monkeypatch.setattr(
dependencies,
"runtime_dependency_group",
lambda: "runtime-free-threaded",
)
assert list(dependencies.iter_runtime_requirement_strings(project_file)) == [
"shared==1",
"free-threaded==3",
]
assert list(dependencies.iter_runtime_profile_requirement_strings(project_file)) == [
"free-threaded==3",
]
def test_runtime_profiles_share_gil_safe_crcmod_distribution():
"""
两种运行配置共享支持 GIL 策略的 CRC 实现,排除旧间接依赖。
"""
project_file = Path(__file__).resolve().parents[1] / "pyproject.toml"
with project_file.open("rb") as file:
document = tomllib.load(file)
assert "crcmod-plus==2.3.1" in document["project"]["dependencies"]
groups = document["dependency-groups"]
assert all(
not requirement.lower().startswith("crcmod")
for group in ("runtime-standard", "runtime-free-threaded")
for requirement in groups[group]
)
assert {
"package": {"name": "oss2"},
"dependencies": ["crcmod"],
} in document["tool"]["uv"]["exclude-dependencies"]
def test_windows_runtime_profiles_isolate_pywin32_dependency():
"""
标准 Windows 保留 named-pipe 依赖,free-threaded profile 不解析 pywin32。
"""
project_file = Path(__file__).resolve().parents[1] / "pyproject.toml"
with project_file.open("rb") as file:
document = tomllib.load(file)
dependencies = document["project"]["dependencies"]
groups = document["dependency-groups"]
assert not any(requirement.lower().startswith("pympler") for requirement in dependencies)
assert "pywin32==312 ; sys_platform == 'win32'" in groups["runtime-standard"]
assert not any(
requirement.lower().startswith("pywin32")
for requirement in groups["runtime-free-threaded"]
)
assert {
"package": {"name": "docker"},
"dependencies": ["pywin32"],
} in document["tool"]["uv"]["exclude-dependencies"]
def test_standard_runtime_uses_shared_rust_text_capabilities(monkeypatch):
"""
标准镜像的文本能力不得重新引入 profile 专属实现。
"""
imported = []
moviepilot_rust = SimpleNamespace(
is_available=lambda: True,
jieba_cut=lambda _value: ["中文", "分词"],
zhconv_fast=lambda value, _target: value,
)
def import_module(name):
imported.append(name)
return moviepilot_rust if name == "moviepilot_rust" else SimpleNamespace()
monkeypatch.setattr(dependency_doctor, "import_module", import_module)
monkeypatch.setattr(dependency_doctor.sysconfig, "get_config_var", lambda _name: None)
dependency_doctor.main()
assert "zhconv_rs" not in imported
def test_runtime_excluded_dependency_pairs_reads_uv_policy(tmp_path: Path):
"""
运行时诊断应复用 uv 排除配置,不维护第二份包名特判。
"""
project_file = tmp_path / "pyproject.toml"
project_file.write_text(
"""
[tool.uv]
exclude-dependencies = [
{ package = { name = "Demo_Package" }, dependencies = ["Legacy-Dep>=1"] },
]
""",
encoding="utf-8",
)
assert dependencies.runtime_excluded_dependency_pairs(project_file) == {
("demo-package", "legacy-dep")
}
def test_runtime_profile_probe_accepts_only_declared_uv_exclusions(tmp_path: Path):
"""
CI 健康检查只能忽略 uv 配置中明确排除的依赖边。
"""
project_file = tmp_path / "pyproject.toml"
project_file.write_text(
"""
[tool.uv]
exclude-dependencies = [
{ package = { name = "docker" }, dependencies = ["pywin32"] },
]
""",
encoding="utf-8",
)
ignored = "The package `docker` requires `pywin32>=304`, but it's not installed"
actionable = "The package `demo` requires `missing>=1`, but it's not installed"
summary = "\n".join((
"Using Python 3.14.7 environment at: .venv",
"Checked 189 packages in 3ms",
"Found 2 incompatibilities",
))
assert verify_runtime_profile._uv_health_errors(
f"{summary}\n{ignored}\n{actionable}",
project_file,
) == {actionable}
def test_full_dependency_probe_rejects_psycopg_python_fallback(monkeypatch):
"""
V3t 构建不得把 psycopg 纯 Python 实现误认为可发布能力。
"""
modules = {
"moviepilot_rust": SimpleNamespace(
is_available=lambda: True,
jieba_cut=lambda _value: ["中文", "分词"],
zhconv_fast=lambda value, _target: value,
),
"crcmod.crcmod": SimpleNamespace(_usingExtension=True),
"psycopg": SimpleNamespace(pq=SimpleNamespace(__impl__="python")),
}
monkeypatch.setattr(
dependency_doctor,
"import_module",
lambda name: modules.get(name, SimpleNamespace()),
)
monkeypatch.setattr(
dependency_doctor.sysconfig,
"get_config_var",
lambda name: 1 if name == "Py_GIL_DISABLED" else None,
)
monkeypatch.setattr(dependency_doctor.sys, "_is_gil_enabled", lambda: False)
with pytest.raises(RuntimeError, match="psycopg C 实现不可用"):
dependency_doctor.main(full=True)