Files
MoviePilot/app/runtime/event/snapshot.py
2026-08-25 22:32:33 +08:00

117 lines
3.2 KiB
Python
Raw 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.
"""事件 payload 的只读契约快照访问。"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from pydantic import BaseModel, ValidationError
from app.runtime.event.contracts import get_event_contract, normalize_event_type
from app.schemas.types import ChainEventType, EventType
@dataclass(frozen=True, slots=True)
class EventPayloadSnapshot:
"""保存原始 payload 及按登记契约解析出的独立快照。"""
event_type: EventType | ChainEventType | str
raw: Any
payload: BaseModel | None
input: BaseModel | None
output: BaseModel | None
known: bool
schema_version: int | None
payload_mode: str | None
validation_mode: str | None
errors: tuple[str, ...] = ()
@property
def valid(self) -> bool:
"""返回已知事件的全部登记模型是否都通过校验。"""
return self.known and not self.errors
def _model_snapshot(
model: type[BaseModel] | None,
payload: Any,
label: str,
cache: dict[type[BaseModel], BaseModel | None],
errors: list[str],
) -> BaseModel | None:
"""按单个模型构造快照,同一模型在一次访问中只解析一次。"""
if model is None:
return None
if model in cache:
return cache[model]
if isinstance(payload, model):
cache[model] = payload.model_copy(deep=True)
return cache[model]
try:
cache[model] = model.model_validate(payload)
except (ValidationError, TypeError, ValueError) as error:
cache[model] = None
errors.append(f"{label}:{model.__name__}: {error}")
return cache[model]
def snapshot_event_data(
event_type: EventType | ChainEventType | str,
event_data: Any,
) -> EventPayloadSnapshot:
"""按事件注册契约解析 payload,未知自定义事件保持原始数据并返回未登记状态。"""
normalized = normalize_event_type(event_type)
try:
contract = get_event_contract(normalized)
except KeyError:
return EventPayloadSnapshot(
event_type=normalized,
raw=event_data,
payload=None,
input=None,
output=None,
known=False,
schema_version=None,
payload_mode=None,
validation_mode=None,
)
errors: list[str] = []
cache: dict[type[BaseModel], BaseModel | None] = {}
payload_snapshot = _model_snapshot(
contract.payload_model,
event_data,
"payload",
cache,
errors,
)
input_snapshot = _model_snapshot(
contract.input_model,
event_data,
"input",
cache,
errors,
)
output_snapshot = _model_snapshot(
contract.output_model,
event_data,
"output",
cache,
errors,
)
return EventPayloadSnapshot(
event_type=normalized,
raw=event_data,
payload=payload_snapshot,
input=input_snapshot,
output=output_snapshot,
known=True,
schema_version=contract.schema_version,
payload_mode=contract.payload_mode.value,
validation_mode=contract.validation_mode.value,
errors=tuple(errors),
)
__all__ = ["EventPayloadSnapshot", "snapshot_event_data"]