feat: 新增 Python 3.14t 自由线程镜像 (#6434)

* 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
This commit is contained in:
InfinityPacer
2026-08-24 17:48:14 +08:00
committed by GitHub
parent 88dce4ca8e
commit 326b5cf3ad
63 changed files with 5294 additions and 253 deletions
+8 -1
View File
@@ -16,6 +16,7 @@ from dotenv import set_key, unset_key
from pydantic import BaseModel, Field, ConfigDict, model_validator
from pydantic_settings import BaseSettings, SettingsConfigDict
from app.foundation.environment import is_free_threaded_runtime
from app.runtime.log import (
LogConfigModel,
configure_log_settings,
@@ -596,7 +597,7 @@ class ConfigModel(BaseModel):
# ==================== 性能配置 ====================
# 大内存模式
BIG_MEMORY_MODE: bool = False
# Rust 加速总开关,关闭时所有 Rust 快路径回退到 Python 实现
# Rust 加速总开关,free-threaded 运行时固定启用
RUST_ACCEL: bool = True
# 是否启用编码探测的性能模式
ENCODING_DETECTION_PERFORMANCE_MODE: bool = True
@@ -996,6 +997,12 @@ class Settings(BaseSettings, ConfigModel, LogConfigModel):
converted_value, needs_update = self.generic_type_converter(
value, original_value, field.annotation, field.default, key
)
if (
key == "RUST_ACCEL"
and is_free_threaded_runtime()
and converted_value is not True
):
return False, "free-threaded 运行时必须启用 Rust 加速"
# 如果没有抛出异常,则统一使用 converted_value 进行更新
if needs_update or str(value) != str(converted_value):
success, message = self.update_env_config(key, value, converted_value)
+56
View File
@@ -0,0 +1,56 @@
"""按解释器 ABI 选择主程序运行依赖 profile。"""
from __future__ import annotations
import tomllib
from collections.abc import Iterable
from pathlib import Path
from app.foundation.environment import is_free_threaded_runtime
RUNTIME_STANDARD_GROUP = "runtime-standard"
RUNTIME_FREE_THREADED_GROUP = "runtime-free-threaded"
def runtime_dependency_group() -> str:
"""返回当前解释器必须使用的互斥运行依赖组。"""
if is_free_threaded_runtime():
return RUNTIME_FREE_THREADED_GROUP
return RUNTIME_STANDARD_GROUP
def runtime_sync_arguments() -> tuple[str, ...]:
"""返回 uv sync 选择当前运行依赖组所需的稳定参数。"""
return "--no-default-groups", "--group", runtime_dependency_group()
def iter_runtime_requirement_strings(project_file: Path) -> Iterable[str]:
"""读取主项目依赖及当前运行 profile 的根依赖声明。"""
with project_file.open("rb") as file:
document = tomllib.load(file)
project = document.get("project") or {}
for requirement in project.get("dependencies") or ():
if isinstance(requirement, str):
yield requirement
groups = document.get("dependency-groups") or {}
for requirement in groups.get(runtime_dependency_group()) or ():
if isinstance(requirement, str):
yield requirement
def iter_runtime_profile_requirement_strings(project_file: Path) -> Iterable[str]:
"""读取当前解释器 profile 的根依赖声明。"""
with project_file.open("rb") as file:
document = tomllib.load(file)
groups = document.get("dependency-groups") or {}
for requirement in groups.get(runtime_dependency_group()) or ():
if isinstance(requirement, str):
yield requirement
if __name__ == "__main__":
print(runtime_dependency_group())
+38 -5
View File
@@ -23,6 +23,7 @@ from app.schemas.plugin import Plugin as _SchemaPlugin
from app.schemas.plugin import PluginDashboard as _SchemaPluginDashboard
from app.schemas.plugin import PluginInstance, PluginRuntimeStatus
from app.foundation.crypto import RSAUtils
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
from app.foundation.singleton import Singleton
from app.foundation.version import compare_version
from app.runtime.execution import run_in_threadpool_to_completion
@@ -90,6 +91,24 @@ def _unavailable_plugin_catalog_factory(_manager: "PluginManager") -> Any:
raise RuntimeError("插件目录应用服务尚未由启动组合根装配")
def _warn_if_plugin_enabled_gil(
*,
gil_enabled_before: bool,
plugin_id: Optional[str],
) -> None:
"""记录插件加载使 free-threaded 进程重新启用 GIL 的真实转换。"""
if (
not is_free_threaded_runtime()
or gil_enabled_before
or not is_gil_enabled()
):
return
logger.warning(
"加载插件%s后 free-threaded 运行时已启用 GIL,请检查原生扩展兼容性",
plugin_id or "集合",
)
_legacy_diagnostics_configurator: LegacyDiagnosticsConfigurator = (
_ignore_legacy_diagnostics
)
@@ -363,7 +382,14 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
enabled=settings.DEBUG,
emitter=logger.warning,
)
return self._plugin_lifecycle.start(pid)
gil_enabled_before = is_gil_enabled()
try:
return self._plugin_lifecycle.start(pid)
finally:
_warn_if_plugin_enabled_gil(
gil_enabled_before=gil_enabled_before,
plugin_id=pid,
)
except PluginMutationRejectedError as error:
logger.warning(str(error))
if pid:
@@ -719,10 +745,17 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
try:
with self.mutation("重新加载插件"):
with self._plugin_quiesce_lock:
return self._plugin_lifecycle.reload(
plugin_id,
EventType.PluginReload,
)
gil_enabled_before = is_gil_enabled()
try:
return self._plugin_lifecycle.reload(
plugin_id,
EventType.PluginReload,
)
finally:
_warn_if_plugin_enabled_gil(
gil_enabled_before=gil_enabled_before,
plugin_id=plugin_id,
)
except PluginMutationRejectedError as error:
logger.warning(str(error))
return PluginRuntimeStatus.LOAD_FAILED