36 lines
1.3 KiB
Python
36 lines
1.3 KiB
Python
"""Plumbing shared by the kanban notifier and dispatcher loops."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from contextvars import Context
|
|
from typing import Any, Callable
|
|
|
|
# Keep the logger name run.py used so extracted log records are unchanged.
|
|
logger = logging.getLogger("gateway.run")
|
|
|
|
|
|
def _run_in_fresh_context(func: Callable[..., Any], /, *args: Any) -> Any:
|
|
"""Run *func* in an empty ``Context`` so request-local ContextVars stay behind.
|
|
|
|
``asyncio.to_thread`` copies the caller's context; a lingering
|
|
``delegate_task`` child marker would make ``write_txn`` false-trip for
|
|
these process-owned writers. An empty Context keeps the DB guard intact
|
|
for real children without exempting dispatcher writes.
|
|
"""
|
|
return Context().run(func, *args)
|
|
|
|
|
|
async def _to_thread_process_service(func: Callable[..., Any], /, *args: Any) -> Any:
|
|
"""Offload blocking process-service work without inheriting request ContextVars."""
|
|
return await asyncio.to_thread(_run_in_fresh_context, func, *args)
|
|
|
|
|
|
def _list_boards(kb: Any) -> list:
|
|
"""Enumerate live boards; fall back to the default board when listing fails."""
|
|
try:
|
|
return kb.list_boards(include_archived=False)
|
|
except Exception:
|
|
return [kb.read_board_metadata(kb.DEFAULT_BOARD)]
|