
    `gj!                    (   U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
mZmZmZmZ  eej                   ej"                  ej$                  ej&                   eedd       eedd      hdhz
        Z ej,                  e      Zej2                  j5                  dd	      xs d	j7                         j9                         d
v Ze G d de             Z ej>                  dd      Z de!d<   ddZ"ddZ#ddZ$ G d d      Z% G d d      Z&y)uC  Transport abstraction for the tui_gateway JSON-RPC server.

Historically the gateway wrote every JSON frame directly to real stdout.  This
module decouples the I/O sink from the handler logic so the same dispatcher
can be driven over stdio (``tui_gateway.entry``) or WebSocket
(``tui_gateway.ws``) without duplicating code.

A :class:`Transport` is anything that can accept a JSON-serialisable dict and
forward it to its peer.  The active transport for the current request is
tracked in a :class:`contextvars.ContextVar` so handlers — including those
dispatched onto the worker pool — route their writes to the right peer.

Backward compatibility
----------------------
``tui_gateway.server.write_json`` still works without any transport bound.
When nothing is on the contextvar and no session-level transport is found,
it falls back to the module-level :class:`StdioTransport`, which wraps the
original ``_real_stdout`` + ``_stdout_lock`` pair.  Tests that monkey-patch
``server._real_stdout`` continue to work because the stdio transport resolves
the stream lazily through a callback.
    )annotationsN)AnyCallableOptionalProtocolruntime_checkableWSAECONNRESETWSAESHUTDOWNHERMES_TUI_GATEWAY_NO_FLUSH >   1onyestruec                       e Zd ZdZddZddZy)	Transportz-Minimal interface every transport implements.c                     y)z<Emit one JSON frame. Return ``False`` when the peer is gone.N )selfobjs     H/root/.hermes/venv/lib/python3.12/site-packages/tui_gateway/transport.pywritezTransport.writeF           c                     y)z.Release any resources owned by this transport.Nr   r   s    r   closezTransport.closeI   r   r   Nr   dictreturnboolr!   None)__name__
__module____qualname____doc__r   r   r   r   r   r   r   B   s    7K=r   r   hermes_gateway_transport)defaultz+contextvars.ContextVar[Optional[Transport]]_current_transportc                 *    t         j                         S )z;Return the transport bound for the current request, if any.)r+   getr   r   r   current_transportr.   U   s    !!##r   c                ,    t         j                  |       S )zVBind *transport* for the current context. Returns a token for :func:`reset_transport`.)r+   set)	transports    r   bind_transportr2   Z   s    !!),,r   c                .    t         j                  |        y)zARestore the transport binding captured by :func:`bind_transport`.N)r+   reset)tokens    r   reset_transportr6   _   s    U#r   c                  ,    e Zd ZdZdZddZddZd	dZy)
StdioTransportu/  Writes JSON frames to a stream (usually ``sys.stdout``).

    The stream is resolved via a callable so runtime monkey-patches of the
    underlying stream continue to work — this preserves the behaviour the
    existing test suite relies on (``monkeypatch.setattr(server, "_real_stdout", ...)``).
    _stream_getter_lockc                     || _         || _        y Nr9   )r   stream_getterlocks      r   __init__zStdioTransport.__init__n   s    +
r   c                ,   t        j                  |d      dz   }| j                  5  | j                         }	 |j	                  |       t        s	 |j!                          ddd       y# t
        $ r Y ddd       yt        $ r0}t        |t              sdt        |      vr Y d}~ddd       yd}~wt        $ r;}|j                  t        vr t        j                  d|       Y d}~ddd       yd}~ww xY w# t
        $ r Y ddd       yt        $ r0}t        |t              sdt        |      vr Y d}~ddd       yd}~wt        $ r;}|j                  t        vr t        j                  d|       Y d}~ddd       yd}~ww xY w# 1 sw Y   yxY w)	u  Return ``True`` on success, ``False`` ONLY when the peer is gone.

        Returning ``False`` is the dispatcher's "broken stdout pipe" signal
        — ``entry.py`` calls ``sys.exit(0)`` when ``write_json`` reports
        ``False``.  So programming errors (non-JSON-safe payloads, encoding
        misconfig, unexpected ValueErrors, host I/O bugs like ENOSPC) MUST
        NOT return ``False``, otherwise a real bug looks like a clean
        disconnect and is harder to diagnose.  Those re-raise so the
        existing crash-log infrastructure records the traceback.

        Peer-gone branches:
          * ``BrokenPipeError``
          * ``ValueError("...closed file...")``
          * ``OSError`` whose errno is in :data:`_PEER_GONE_ERRNOS`
            (EPIPE / ECONNRESET / EBADF / ESHUTDOWN; plus WSA mappings
            on Windows).  Other OSError errnos (ENOSPC, EACCES, ...) are
            real host problems and re-raise.
        F)ensure_ascii
Nzclosed filez"StdioTransport write peer gone: %sz"StdioTransport flush peer gone: %sT)jsondumpsr;   r:   r   BrokenPipeError
ValueError
isinstanceUnicodeEncodeErrorstrOSErrorerrno_PEER_GONE_ERRNOSloggerdebug_DISABLE_FLUSHflush)r   r   linestreames        r   r   zStdioTransport.writer   s   . zz#E2T9ZZ '	!((*FT". "!LLN9'	!R K # '	! '	!   a!34SQRV8S'	! '	!  77"33A1E''	! '	! ' ! ='	! '	!> " !!!%78MQTUVQW<W E'	! '	!F  !ww&77LL!EqI O'	! '	!F!G'	!R s   F
A*
F
C:*	C73F
=C7B0#F
0C7<)C2%F
2C77F
:	FF
FE 3F
 F)F5F
FF

Fc                     y r=   r   r   s    r   r   zStdioTransport.close   s    r   N)r>   zCallable[[], Any]r?   zthreading.Lockr!   r$   r   r#   r%   r&   r'   r(   	__slots__r@   r   r   r   r   r   r8   r8   d   s     ,IBHr   r8   c                  ,    e Zd ZdZdZddZddZd	dZy)
TeeTransportu]  Mirrors writes to one primary plus N best-effort secondaries.

    The primary's return value (and exceptions) determine the result —
    secondaries swallow failures so a wedged sidecar never stalls the
    main IO path.  Used by the PTY child so every dispatcher emit lands
    on stdio (Ink) AND on a back-WS feeding the dashboard sidebar.
    _primary_secondariesc                     || _         || _        y r=   rZ   )r   primarysecondariess      r   r@   zTeeTransport.__init__   s    'r   c                    | j                   j                  |      }| j                  D ]  }	 |j                  |        |S # t        $ r Y $w xY wr=   )r[   r   r\   	Exception)r   r   oksecs       r   r   zTeeTransport.write   sV    ]]  %$$ 	C		#	
 	  s   A	AAc                   	 | j                   j                          | j                  D ]  }	 |j                           y # t        $ r Y "w xY w# | j                  D ]"  }	 |j                          # t        $ r Y  w xY w w xY wr=   )r[   r   r\   ra   )r   rc   s     r   r   zTeeTransport.close   s    	MM!(( IIK !  (( IIK  s>   A ?	A
ABA0/B0	A<	9B;A<	<BN)r^   'Transport'r_   re   r!   r$   r   r#   rV   r   r   r   rY   rY      s     -I(r   rY   )r!   Optional[Transport])r1   rf   r#   )'r(   
__future__r   contextvarsrL   rD   loggingos	threadingtypingr   r   r   r   r   	frozensetEPIPE
ECONNRESETEBADF	ESHUTDOWNgetattrrM   	getLoggerr%   rN   environr-   striplowerrP   r   
ContextVarr+   __annotations__r.   r2   r6   r8   rY   r   r   r   <module>ry      s.  , #     	  G G
 	KK		KK	OOE?B'E>2& D	 
  
		8	$ **..!>CIrPPRXXZ _  = = = K" ? $
-
$
S Sl! !r   