
    PL
j	                        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S )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_levelcoroCoroutine[Any, Any, Any]loop#Optional[asyncio.AbstractEventLoop]r   Optional[logging.Logger]r	   strr
   intreturnOptional[Future]c               z   ||nt           }|At          j        |           r|                                  |                    |d|           dS 	 t          j        | |          S # t          $ rK}t          j        |           r|                                  |                    |d||           Y d}~dS 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)r   r   r   r	   r
   r   excs          5/home/kuhnn/.hermes/hermes-agent/agent/async_utils.pysafe_schedule_threadsafer   "   s    ( &&&OC|t$$ 	JJLLL	-{;;;t/d;;;   t$$ 	JJLLL	8[#666ttttt	s   A% %
B:/A B55B:)r   r   r   r   r   r   r	   r   r
   r   r   r   )__doc__
__future__r   r   loggingconcurrent.futuresr   typingr   r   r   	getLogger__name__r   DEBUGr        r   <module>r)      s    , # " " " " "   % % % % % % + + + + + + + + + + $'#H-- (,=]" " " " " " " "r(   