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

91 lines
2.5 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.
"""
请求与后台工作共用的关联 ID 上下文。
"""
from __future__ import annotations
import re
import uuid
from contextlib import contextmanager
from contextvars import ContextVar, Token
from typing import Any, Callable, Iterator, Mapping
CORRELATION_ID_HEADER = "X-Request-ID"
MAX_CORRELATION_ID_LENGTH = 64
_VALID_CORRELATION_ID = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$")
_CURRENT_CORRELATION_ID: ContextVar[str | None] = ContextVar(
"moviepilot_correlation_id",
default=None,
)
def normalize_correlation_id(candidate: str | None) -> str:
"""
接受安全的调用方 ID;非法、超长或缺失值均替换为随机 ID。
"""
if candidate and _VALID_CORRELATION_ID.fullmatch(candidate):
return candidate
return uuid.uuid4().hex
def get_correlation_id(default: str | None = None) -> str | None:
"""
返回当前执行上下文中的关联 ID。
"""
return _CURRENT_CORRELATION_ID.get() or default
def set_correlation_id(correlation_id: str) -> Token[str | None]:
"""
设置当前关联 ID,并返回供调用方精确恢复的 token。
"""
return _CURRENT_CORRELATION_ID.set(correlation_id)
def reset_correlation_id(token: Token[str | None]) -> None:
"""
恢复设置关联 ID 之前的上下文。
"""
_CURRENT_CORRELATION_ID.reset(token)
@contextmanager
def correlation_scope(correlation_id: str | None) -> Iterator[str | None]:
"""
在当前同步或异步任务作用域内绑定并自动恢复关联 ID。
"""
if correlation_id is None:
yield None
return
token = set_correlation_id(correlation_id)
try:
yield correlation_id
finally:
reset_correlation_id(token)
def with_correlation_header(headers: Mapping[str, str] | None) -> dict[str, str]:
"""
复制请求头并在调用方未显式指定时加入当前关联 ID。
"""
result = dict(headers or {})
if any(key.lower() == CORRELATION_ID_HEADER.lower() for key in result):
return result
correlation_id = get_correlation_id()
if correlation_id:
result[CORRELATION_ID_HEADER] = correlation_id
return result
def call_with_correlation(
correlation_id: str | None,
func: Callable[..., Any],
args: tuple[Any, ...],
kwargs: dict[str, Any],
) -> Any:
"""
从可序列化参数恢复关联 ID 后调用函数,供子进程入口使用。
"""
with correlation_scope(correlation_id):
return func(*args, **kwargs)