ForcePilot/backend/package/yuxi/channel/sdk/async_utils.py
Kris b438af3ba8 feat(channel-sdk): 新增完整的渠道SDK工具链
本提交新增了全渠道SDK核心模块:
1.  异步锁、目标解析、动作调度等基础工具
2.  消息动作注册与统一调度系统
3.  测试套件与契约测试框架
4.  完整的目标解析流水线与工具函数
5.  资源依赖注入与生命周期管理
2026-05-21 10:29:12 +08:00

67 lines
1.6 KiB
Python

from __future__ import annotations
import asyncio
import logging
from collections.abc import Callable, Coroutine
from typing import Protocol, TypeVar
T = TypeVar("T")
_logger = logging.getLogger(__name__)
class KeyedAsyncQueueHooks(Protocol):
def on_enqueue(self) -> None: ...
def on_settle(self) -> None: ...
def enqueue_keyed_task(
tails: dict[str, asyncio.Future],
key: str,
task: Callable[[], Coroutine[None, None, T]],
*,
hooks: KeyedAsyncQueueHooks | None = None,
) -> Coroutine[None, None, T]:
async def _run() -> T:
if hooks:
hooks.on_enqueue()
prev = tails.get(key)
if prev is not None and not prev.done():
try:
await prev
except Exception:
_logger.debug("Keyed task predecessor failed for key=%s", key, exc_info=True)
loop = asyncio.get_running_loop()
tail: asyncio.Future = loop.create_future()
tails[key] = tail
try:
return await task()
finally:
if hooks:
hooks.on_settle()
tail.set_result(None)
if tails.get(key) is tail:
tails.pop(key, None)
return _run()
class KeyedAsyncQueue:
def __init__(self) -> None:
self._tails: dict[str, asyncio.Future] = {}
def get_tails(self) -> dict[str, asyncio.Future]:
return self._tails
async def enqueue(
self,
key: str,
task: Callable[[], Coroutine[None, None, T]],
*,
hooks: KeyedAsyncQueueHooks | None = None,
) -> T:
return await enqueue_keyed_task(self._tails, key, task, hooks=hooks)