
    `gjz                        d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
  ej                  e      Zddej                  d	 	 	 	 	 	 	 	 	 	 	 d
dZdd	Zy)u  Async/sync bridging helpers.

The codebase has ~30 sites that schedule a coroutine onto an event loop from a
worker thread via :func:`asyncio.run_coroutine_threadsafe`.  That function can
raise :class:`RuntimeError` (e.g. the loop was closed during a shutdown race),
and when it does the coroutine object is never awaited and never closed —
which triggers a ``"coroutine '<name>' was never awaited"`` RuntimeWarning and
leaks the coroutine's frame until GC.

:func:`safe_schedule_threadsafe` wraps the call, closes the coroutine on
scheduling failure, and returns ``None`` (instead of a half-formed future) so
callers can branch cleanly:

    fut = safe_schedule_threadsafe(coro, loop)
    if fut is None:
        return  # or fallback behavior
    fut.result(timeout=5)

The helper deliberately does NOT also handle ``future.result()`` failures —
that is a separate concern.  Once the loop has accepted the coroutine, its
lifecycle belongs to the loop, not the scheduling thread.
    )annotationsN)Future)Any	CoroutineOptionalz$Failed to schedule coroutine on loop)loggerlog_message	log_levelc               X   ||nt         }|9t        j                  |       r| j                          |j	                  |d|       y	 t        j
                  | |      S # t        $ rC}t        j                  |       r| j                          |j	                  |d||       Y d}~yd}~ww xY w)aY  Schedule ``coro`` on ``loop`` from a sync context, leak-safe.

    Returns the :class:`concurrent.futures.Future` on success, or ``None`` if
    the loop is missing or :func:`asyncio.run_coroutine_threadsafe` raised
    (e.g. the loop was closed during a shutdown race).  In all failure paths
    the coroutine is :meth:`close`-d so it does not trigger
    ``"coroutine was never awaited"`` warnings or leak its frame.

    Callers retain full control over what to do with the returned future
    (call ``.result(timeout=...)``, attach ``add_done_callback``, ignore it
    fire-and-forget, etc.).
    Nz%s: loop is Nonez%s: %s)_DEFAULT_LOGGERasyncioiscoroutinecloselogrun_coroutine_threadsafe	Exception)coroloopr   r	   r
   r   excs          D/root/.hermes/venv/lib/python3.12/site-packages/agent/async_utils.pysafe_schedule_threadsafer   "   s    ( &&OC|t$JJL	-{;//d;; t$JJL	8[#6	s   A 	B)&9B$$B)c                d    	 | j                          y# t        j                  t        f$ r Y yw xY w)u  Retrieve a detached task's result without surfacing cancellation.

    Used as an ``add_done_callback`` on tasks that were cancelled and
    detached (e.g. an adapter close path that swallows ``CancelledError``
    past its teardown deadline). Observing ``task.exception()`` prevents
    "exception was never retrieved" noise on the event loop; cancellation
    and any terminal error are deliberately swallowed — the task's owner
    already gave up on it.
    N)	exceptionr   CancelledErrorr   )tasks    r   consume_detached_task_resultr   G   s.    ""I. s    //)r   zCoroutine[Any, Any, Any]r   z#Optional[asyncio.AbstractEventLoop]r   zOptional[logging.Logger]r	   strr
   intreturnzOptional[Future])r   z'asyncio.Future[Any]'r   None)__doc__
__future__r   r   loggingconcurrent.futuresr   typingr   r   r   	getLogger__name__r   DEBUGr   r        r   <module>r+      s   , #   % + + $'##H- (,=]]"
""
-" %	"
 " " "Jr*   