mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-03 14:37:36 +08:00
* fix(resource): select free-threaded extension ABI * chore(deps): require moviepilot-rust 0.2.9 * perf: add free-threaded runtime comparison * feat: add free-threaded runtime profile * chore(deps): require moviepilot-rust 0.3.0 * test: isolate system endpoint import graph * fix(docker): preserve runtime profile during recovery * feat(runtime): expose active GIL state * feat(plugin): log GIL fallback attribution * test: refresh runtime observability dependency baseline * fix(plugin): validate the active uv runtime profile * test(runtime): expand free-threaded benchmark evidence * docs(runtime): define v3t governance gates * test(runtime): separate rust benchmark modes * docs: sync free-threaded architecture baseline * perf: add PostgreSQL driver comparison * docs(runtime): record final free-threaded evidence * fix(runtime): converge dual-profile dependency verification * fix(runtime): scope Python 3.14 warning filter * fix(runtime): match actual oss2 syntax warning * docs(runtime): refresh free-threaded benchmark evidence * test(architecture): refresh runtime dependency baseline * ci: skip unused Trivy Java database * build: exclude local verification artifacts * docs(runtime): refresh PostgreSQL driver benchmarks * docs(runtime): record plugin restore acceptance * docs(runtime): record amd64 candidate acceptance * ci: pin beta image publisher action * test(architecture): merge runtime dependency baseline * feat(runtime): expose Python GIL status * docs(runtime): document Python runtime status fields
93 lines
2.9 KiB
Python
93 lines
2.9 KiB
Python
"""主程序运行依赖的轻量可用性探针。"""
|
|
|
|
import argparse
|
|
from importlib import import_module
|
|
import sys
|
|
import sysconfig
|
|
import warnings
|
|
|
|
|
|
CORE_MODULES = (
|
|
"alembic",
|
|
"cloakbrowser",
|
|
"fastapi",
|
|
"pydantic",
|
|
"pydantic_core",
|
|
"pydantic_settings",
|
|
"sqlalchemy",
|
|
"starlette",
|
|
"uvicorn",
|
|
)
|
|
NATIVE_MODULES = (
|
|
("asyncpg", "asyncpg"),
|
|
("bcrypt", "bcrypt._bcrypt"),
|
|
("brotli", "brotli"),
|
|
("crcmod", "crcmod.crcmod"),
|
|
("cryptography", "cryptography.hazmat.bindings._rust"),
|
|
("greenlet", "greenlet._greenlet"),
|
|
("lxml", "lxml.etree"),
|
|
("orjson", "orjson"),
|
|
("oss2", "oss2"),
|
|
("pillow", "PIL._imaging"),
|
|
("pillow-avif-plugin", "pillow_avif"),
|
|
("pydantic-core", "pydantic_core._pydantic_core"),
|
|
("zstandard", "zstandard"),
|
|
)
|
|
|
|
|
|
def _verify_text_capabilities(*, free_threaded: bool) -> None:
|
|
"""验证标准与 free-threaded profile 共同依赖的文本能力。"""
|
|
moviepilot_rust = import_module("moviepilot_rust")
|
|
if not moviepilot_rust.is_available() or not moviepilot_rust.jieba_cut("中文分词"):
|
|
raise RuntimeError("中文分词运行依赖不可用")
|
|
|
|
if free_threaded:
|
|
converted = moviepilot_rust.zhconv_fast("后台", "zh-hant")
|
|
else:
|
|
converted = import_module("zhconv_rs").zhconv("后台", "zh-hant")
|
|
if not converted:
|
|
raise RuntimeError("中文转换运行依赖不可用")
|
|
|
|
|
|
def _verify_native_profile(*, free_threaded: bool) -> None:
|
|
"""验证 ABI 敏感依赖提供预期的原生能力。"""
|
|
warnings.filterwarnings(
|
|
"ignore",
|
|
message=r'"\\&" is an invalid escape sequence\..*',
|
|
category=SyntaxWarning,
|
|
)
|
|
imported = {}
|
|
profile_modules = (
|
|
(("psycopg", "psycopg"),)
|
|
if free_threaded
|
|
else (("psycopg2", "psycopg2"), ("zhconv-rs", "zhconv_rs"))
|
|
)
|
|
for name, module_name in (*NATIVE_MODULES, *profile_modules):
|
|
imported[name] = import_module(module_name)
|
|
if free_threaded and sys._is_gil_enabled():
|
|
raise RuntimeError(f"原生依赖 {name} 启用了 GIL")
|
|
|
|
if not imported["crcmod"]._usingExtension:
|
|
raise RuntimeError("crcmod 原生实现不可用")
|
|
if free_threaded and imported["psycopg"].pq.__impl__ != "c":
|
|
raise RuntimeError("psycopg C 实现不可用")
|
|
|
|
|
|
def main(*, full: bool = False) -> None:
|
|
"""验证 Web 栈和启动关键能力可导入、可执行。"""
|
|
free_threaded = sysconfig.get_config_var("Py_GIL_DISABLED") == 1
|
|
for module_name in CORE_MODULES:
|
|
import_module(module_name)
|
|
|
|
_verify_text_capabilities(free_threaded=free_threaded)
|
|
if full:
|
|
_verify_native_profile(free_threaded=free_threaded)
|
|
if free_threaded and sys._is_gil_enabled():
|
|
raise RuntimeError("核心运行依赖启用了 GIL")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--full", action="store_true")
|
|
main(full=parser.parse_args().full)
|