"""Async execution utilities. Provides a shared thread-backed executor abstraction and helpers for running blocking callables without stalling the FastAPI event loop. """ from __future__ import annotations import asyncio import functools from collections.abc import Callable, Coroutine from concurrent.futures import ThreadPoolExecutor from typing import Any, ParamSpec, TypeVar from app.utils.logging_compat import get_logger P = ParamSpec("P") T = TypeVar("T") log = get_logger(__name__) DEFAULT_BLOCKING_EXECUTOR: ThreadPoolExecutor = ThreadPoolExecutor( max_workers=16, thread_name_prefix="bangui-blocking", ) async def run_blocking( func: Callable[P, T], *args: P.args, executor: ThreadPoolExecutor | None = None, **kwargs: P.kwargs, ) -> T: """Run a blocking callable in the shared thread pool. Args: func: Blocking callable to execute. *args: Positional arguments for the callable. executor: Optional custom executor. Defaults to the shared pool. **kwargs: Keyword arguments for the callable. Returns: The callable return value. """ loop = asyncio.get_running_loop() executor = DEFAULT_BLOCKING_EXECUTOR if executor is None else executor if kwargs: func = functools.partial(func, *args, **kwargs) return await loop.run_in_executor(executor, func) return await loop.run_in_executor(executor, func, *args) async def logged_task( coro: Coroutine[Any, Any, Any], name: str, ) -> None: """Execute a coroutine with automatic exception logging. Wraps fire-and-forget tasks to ensure exceptions are always logged and do not become unhandled task exceptions. Use with asyncio.create_task(): asyncio.create_task( logged_task(some_coroutine(), "task_name"), name="task_name" ) Args: coro: Coroutine to execute. name: Task name for logging context. """ try: await coro except Exception: # noqa: BLE001 log.exception("background_task_failed", task_name=name)