Files
MoviePilot/app/runtime/managed_resources.py
T

183 lines
6.1 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.
"""进程级托管资源的轻量调用门面。"""
from __future__ import annotations
import asyncio
import threading
from typing import Any, Optional, Protocol
MANAGED_RESOURCE_SYNC_KIND = "managed_resource.sync"
MANAGED_RESOURCE_ASYNC_KIND = "managed_resource.async"
class ManagedResourceRuntime(Protocol):
"""Managed Resource 门面依赖的最小 Capability Runtime 合同。"""
@property
def is_shutdown(self) -> bool:
"""返回 Runtime 是否已进入不可逆关闭态。"""
def get_spec(self, capability_id: str) -> Any:
"""返回资源声明。"""
def get_running(self, capability_id: str) -> Any:
"""只查询已发布实例。"""
def snapshot(self, capability_id: str) -> Any:
"""返回资源状态快照。"""
def observations(self, capability_id: Optional[str] = None) -> tuple[Any, ...]:
"""返回资源转换观测。"""
def activate(self, capability_id: str, *, reason: str, retry: bool = False) -> Any:
"""通过同步 adapter 激活资源。"""
async def activate_async(
self,
capability_id: str,
*,
reason: str,
retry: bool = False,
) -> Any:
"""通过异步 adapter 激活资源。"""
def stop(self, capability_id: str, *, reason: str) -> None:
"""通过同步 adapter 停止资源。"""
async def stop_async(self, capability_id: str, *, reason: str) -> None:
"""通过异步 adapter 停止资源。"""
async def shutdown_async(self, *, reason: str) -> bool:
"""关闭混合 Runtime,并返回全部资源是否收敛。"""
_runtime_lock = threading.RLock()
_managed_resource_runtime: Optional[ManagedResourceRuntime] = None
def configure_managed_resource_runtime(runtime: ManagedResourceRuntime) -> None:
"""由启动组合层注入唯一的 Managed Resource Runtime。"""
if runtime is None:
raise ValueError("Managed Resource Runtime 不能为空")
global _managed_resource_runtime
with _runtime_lock:
_managed_resource_runtime = runtime
def _runtime(*, required: bool) -> Optional[ManagedResourceRuntime]:
"""读取当前 Runtime;资源使用路径要求启动组合已经完成装配。"""
with _runtime_lock:
runtime = _managed_resource_runtime
if runtime is None and required:
raise RuntimeError("Managed Resource Runtime 尚未初始化")
return runtime
def _resource_kind(runtime: ManagedResourceRuntime, capability_id: str) -> str:
"""返回声明的执行模式;未知资源继续沿用 Runtime 的领域错误。"""
spec = runtime.get_spec(capability_id)
if spec is None:
runtime.get_running(capability_id)
raise RuntimeError(f"未知 Managed Resource{capability_id}")
return str(spec.kind)
def acquire_managed_resource(
capability_id: str,
*,
reason: str,
retry: bool = True,
) -> Any:
"""同步激活一个声明为同步模式的托管资源。"""
runtime = _runtime(required=True)
kind = _resource_kind(runtime, capability_id)
if kind != MANAGED_RESOURCE_SYNC_KIND:
raise RuntimeError(f"异步 Managed Resource 不能通过同步入口激活:{capability_id}")
return runtime.activate(capability_id, reason=reason, retry=retry)
async def acquire_managed_resource_async(
capability_id: str,
*,
reason: str,
retry: bool = True,
) -> Any:
"""异步激活资源;同步资源移交工作线程,避免阻塞事件循环。"""
runtime = _runtime(required=True)
kind = _resource_kind(runtime, capability_id)
if kind == MANAGED_RESOURCE_ASYNC_KIND:
return await runtime.activate_async(
capability_id,
reason=reason,
retry=retry,
)
if kind == MANAGED_RESOURCE_SYNC_KIND:
return await asyncio.to_thread(
runtime.activate,
capability_id,
reason=reason,
retry=retry,
)
raise RuntimeError(f"未知 Managed Resource kind{kind}")
def get_running_managed_resource(capability_id: str) -> Any:
"""只查询已发布资源;Runtime 未配置时返回 None,不触发初始化。"""
runtime = _runtime(required=False)
if runtime is None:
return None
return runtime.get_running(capability_id)
def managed_resource_snapshot(capability_id: str) -> Any:
"""返回资源状态快照;Runtime 未配置时返回 None。"""
runtime = _runtime(required=False)
if runtime is None:
return None
return runtime.snapshot(capability_id)
def managed_resource_observations(
capability_id: Optional[str] = None,
) -> tuple[Any, ...]:
"""返回资源转换观测;Runtime 未配置时返回空快照。"""
runtime = _runtime(required=False)
if runtime is None:
return ()
return runtime.observations(capability_id)
def stop_managed_resource(capability_id: str, *, reason: str) -> None:
"""同步停止资源;Runtime 未配置时保持幂等且不反向初始化。"""
runtime = _runtime(required=False)
if runtime is None:
return
kind = _resource_kind(runtime, capability_id)
if kind != MANAGED_RESOURCE_SYNC_KIND:
raise RuntimeError(f"异步 Managed Resource 不能通过同步入口停止:{capability_id}")
runtime.stop(capability_id, reason=reason)
async def stop_managed_resource_async(capability_id: str, *, reason: str) -> None:
"""异步停止资源;同步资源移交工作线程。"""
runtime = _runtime(required=False)
if runtime is None:
return
kind = _resource_kind(runtime, capability_id)
if kind == MANAGED_RESOURCE_ASYNC_KIND:
await runtime.stop_async(capability_id, reason=reason)
return
if kind == MANAGED_RESOURCE_SYNC_KIND:
await asyncio.to_thread(runtime.stop, capability_id, reason=reason)
return
raise RuntimeError(f"未知 Managed Resource kind{kind}")
async def shutdown_managed_resource_runtime(*, reason: str) -> bool:
"""关闭已配置 Runtime,未配置时按已收敛处理。"""
runtime = _runtime(required=False)
if runtime is None:
return True
return await runtime.shutdown_async(reason=reason)