
    iRL                        d Z ddlZddlZddlZddlZddlmZmZ ddlmZm	Z	 ddl
mZmZmZmZmZmZmZmZmZmZ ddlZddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZm Z m!Z!m"Z" ddl#m$Z$ ddl%m&Z&m'Z'  ej(        e)          Z*e G d d                      Z+ ej,                    Z-de+fdZ.d Z/ddZ0ddZ1de!de!fdZ2 G d de3          Z4 G d d          Z5	 ddl6m7Z7 e7j8        e5_9        e5j8        e7_8        n # e:$ r e*;                    d           Y nw xY wee<e=e>e&ef         Z?ee?         Z@ee?         ZAeeAe@f         ZB G d de          ZCdS )u  Server-Sent Events response for Starlette / FastAPI.

Intentional divergence from ``starlette.responses.StreamingResponse``
--------------------------------------------------------------------

``EventSourceResponse`` is modelled on Starlette's ``StreamingResponse`` and
re-syncs most of its behaviour (WebSocket denial, ``collapse_excgroups()``
around the task group, ``memoryview`` chunk handling). The following points
are deliberate divergences — DO NOT "fix" them without reading the rationale:

1. ASGI ``spec_version >= 2.4`` fast path is NOT adopted.
   Upstream short-circuits to ``await stream_response(send)`` and converts
   ``OSError`` into ``ClientDisconnect``, skipping ``listen_for_disconnect``.
   We keep ``_listen_for_disconnect`` running because it
     (a) invokes ``client_close_handler_callable`` on disconnect,
     (b) flips ``self.active = False`` so ``_ping`` and the cooperative
         shutdown grace loop exit promptly.
   Adopting the upstream fast path would regress both features.

2. ``_wrap_websocket_denial_send`` is inlined in this module rather than
   inherited from ``starlette.responses.Response``. The helper landed on
   Starlette ``main`` after our minimum pin (``starlette>=0.41.3``); inline
   until the floor moves past the release that contains it.

3. ``collapse_excgroups()`` is vendored in ``sse_starlette._utils`` rather
   than imported from ``starlette._utils`` (private module).
    N)	dataclassfield)datetimetimezone)
AnyAsyncIterable	AwaitableCallable	CoroutineIteratorMappingOptionalSetUnion)BackgroundTask)iterate_in_threadpool)MutableHeaders)Response)ReceiveScopeSendMessage)collapse_excgroups)ServerSentEventensure_bytesc                   Z    e Zd ZU dZ ee          Zeej	                 e
d<   dZee
d<   dS )_ShutdownStatezPer-thread state for shutdown coordination.

    Issue #152 fix: Uses threading.local() instead of ContextVar to ensure
    one watcher per thread rather than one per async context.
    )default_factoryeventsFwatcher_startedN)__name__
__module____qualname____doc__r   setr   r   anyioEvent__annotations__r    bool     X/home/piyush/.hermes/hermes-agent/venv/lib/python3.11/site-packages/sse_starlette/sse.pyr   r   >   sR            %uS999FC999!OT!!!!!r+   r   returnc                  j    t          t          dd          } | t                      } | t          _        | S )z4Get or create shutdown state for the current thread.shutdown_stateN)getattr_thread_stater   r/   )states    r,   _get_shutdown_stater3   N   s1    M#3T::E}  ',$Lr+   c                      	 t          j        t           j                  } t          | d          r| j        }t          |d          r|S n# t
          $ r Y nw xY wdS )am  
    Try to get uvicorn Server instance via signal handler introspection.

    When uvicorn registers signal handlers, they're bound methods on the Server instance.
    We can retrieve the Server from the handler's __self__ attribute.

    Returns None if:
    - Not running under uvicorn
    - Signal handler isn't a bound method
    - Any introspection fails
    __self__should_exitN)signal	getsignalSIGTERMhasattrr5   	Exception)handlerservers     r,   _get_uvicorn_serverr>   W   sp    "6>227J'' 	%Fv}--    4s   AA
 

AAc                  X  K   t                      } t                      }	 	 t          j        rn=t          j        r||j        rdt          _        nt          j        d           d{V  Jt          | j                  D ]}|	                                 	 d| _
        dS # d| _
        w xY w)ag  
    Poll for shutdown and broadcast to all events in this context.

    One watcher runs per thread (event loop). Checks two shutdown sources:
    1. AppStatus.should_exit - set when our monkey-patch works
    2. uvicorn Server.should_exit - via signal handler introspection (Issue #132 fix)

    When either becomes True, signals all registered events.
    TNg      ?F)r3   r>   	AppStatusr6   enable_automatic_graceful_drainr&   sleeplistr   r%   r    )r2   uvicorn_serverevents      r,   _shutdown_watcherrF   n   s        !!E(**N&	#$  9".". / )-	%+c"""""""""	# %,'' 	 	EIIKKKK	 !&%%%%s   A6B   	B)c                      t                      } | j        sVd| _        	 t          j                    }|                    t                                 dS # t          $ r d| _        Y dS w xY wdS )zDEnsure the shutdown watcher is running for this thread (event loop).TFN)r3   r    asyncioget_running_loopcreate_taskrF   RuntimeError)r2   loops     r,   $_ensure_watcher_started_on_this_looprM      s    !!E  * $	*+--D.0011111 	* 	* 	*$)E!!!!	** *s   4A A)(A)sendc                 &     dt           ddf fd}|S )a;  Mirror of ``starlette.responses.Response._wrap_websocket_denial_send``.

    Divergence #2 (see module docstring): inlined because the helper landed
    on Starlette ``main`` (commit 9ee9519) after our minimum pin
    ``starlette>=0.41.3``. Drop this once the floor moves past the release
    that contains it.
    messager-   Nc                 \   K   | d         }|dv ri | dd|z   i}  |            d {V  d S )Ntype>   http.response.bodyhttp.response.startz
websocket.r*   )rP   message_typerN   s     r,   wrappedz,_wrap_websocket_denial_send.<locals>.wrapped   s[      vHHHFF&,*EFFGd7mmr+   )r   )rN   rV   s   ` r,   _wrap_websocket_denial_sendrW      s6    w 4       Nr+   c                       e Zd ZdS )SendTimeoutErrorN)r!   r"   r#   r*   r+   r,   rY   rY      s        Dr+   rY   c                   x    e Zd ZU dZdZdZdZee         e	d<   e
d             Ze
d             Ze
d             ZdS )	r@   z\Helper to capture a shutdown signal from Uvicorn so we can gracefully terminate SSE streams.FTNoriginal_handlerc                      dt           _        dS )aJ  
        Prevent automatic SSE stream termination on server shutdown.

        WARNING: When disabled, you MUST set AppStatus.should_exit = True
        at some point during shutdown, or streams will never close and the
        server will hang indefinitely (or until uvicorn's graceful shutdown
        timeout expires).
        FNr@   rA   r*   r+   r,    disable_automatic_graceful_drainz*AppStatus.disable_automatic_graceful_drain   s     5:	111r+   c                      dt           _        dS )a  
        Re-enable automatic SSE stream termination on server shutdown.

        This restores the default behavior where SIGTERM triggers immediate
        stream draining. Call this to undo a previous call to
        disable_automatic_graceful_drain().
        TNr]   r*   r+   r,   $enable_automatic_graceful_drain_modez.AppStatus.enable_automatic_graceful_drain_mode   s     59	111r+   c                  t    t           j        rdt           _        t           j        t          j        | i | d S d S )NT)r@   rA   r6   r[   )argskwargss     r,   handle_exitzAppStatus.handle_exit   sB    4 	)$(I!%1&777777 21r+   )r!   r"   r#   r$   r6   rA   r[   r   r
   r(   staticmethodr^   r`   rd   r*   r+   r,   r@   r@      s         ffK&*#+/hx(///	: 	: \	: 9 9 \9 8 8 \8 8 8r+   r@   )ServerzHUvicorn not installed. Graceful shutdown on server termination disabled.c                   $   e Zd ZdZdZdZ	 	 	 	 	 	 	 	 	 	 	 	 d'ded	ed
ee	e
e
f                  de
dee         dee         dee
         deeg ef                  deeg ed         f                  dee         deeeged         f                  deej                 deddfdZedeeef         fd            Zej        deeef         ddfd            Zd(deddfdZdeddfdZd eddfd!Zed)d"            Z d)d#Z!deddfd$Z"d%e#d ededdfd&Z$dS )*EventSourceResponseag  Streaming response implementing the SSE (Server-Sent Events) specification.

    Args:
        content: Async iterable or sync iterator yielding SSE event data.
        status_code: HTTP status code. Default: 200.
        headers: Additional HTTP headers.
        media_type: Response media type. Default: "text/event-stream".
        background: Background task to run after response completes.
        ping: Ping interval in seconds (0 to disable). Default: 15.
        sep: Line separator for SSE messages ("\r\n", "\r", or "\n").
        ping_message_factory: Callable returning custom ping ServerSentEvent.
        data_sender_callable: Async callable for push-based data sending.
        send_timeout: Timeout in seconds for individual send operations.
        client_close_handler_callable: Async callback on client disconnect.
        shutdown_event: Optional ``anyio.Event`` set by the library when server
            shutdown is detected. Generators can watch this event to send farewell
            messages and exit cooperatively instead of receiving CancelledError.
        shutdown_grace_period: Seconds to wait after setting ``shutdown_event``
            before force-cancelling the generator. Must be >= 0. Should be less
            than your ASGI server's graceful shutdown timeout. Default: 0
            (immediate cancel, identical to pre-v3.3.0 behavior).
       
   Ntext/event-streamr   contentstatus_codeheaders
media_type
backgroundpingsepping_message_factorydata_sender_callable)NNNsend_timeoutclient_close_handler_callableshutdown_eventshutdown_grace_periodr-   c                 |   |dvrt          d|           |p| j        | _        t          |t                    r|| _        nt          |          | _        || _        || j        n|| _        || _	        |	| _
        |
| _        t                      }||                    |           |                    dd           d|d<   d|d<   |                     |           || j        n|| _        || _        || _        |d	k     rt          d
          || _        || _        d| _        t/          j                    | _        d S )N)Nrj   
z'sep must be one of: \r\n, \r, \n, got: zCache-Controlzno-storez
keep-alive
ConnectionnozX-Accel-Bufferingr   z"shutdown_grace_period must be >= 0T)
ValueErrorDEFAULT_SEPARATORrs   
isinstancer   body_iteratorr   rn   rp   rq   ru   rv   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalrt   rw   _shutdown_event_shutdown_grace_periodactiver&   Lock
_send_lock)selfrm   rn   ro   rp   rq   rr   rs   rt   ru   rv   rw   rx   ry   _headerss                  r,   __init__zEventSourceResponse.__init__  se   * 000P3PPQQQ0$0 g}-- 	@!(D!6w!?!?D&-7-?$//Z$$8!( "##OOG$$$ 	OZ888!-(,$%(###;?<T77T$8!-J* !1$$ABBB-&;#*,,r+   c                     | j         S N)_ping_intervalr   s    r,   r   z!EventSourceResponse.ping_intervalO  s    ""r+   valuec                     t          |t          t          f          st          d          |dk     rt	          d          || _        d S )Nzping interval must be intr   z$ping interval must be greater than 0)r   intfloat	TypeErrorr   r   )r   r   s     r,   r   z!EventSourceResponse.ping_intervalS  sM    %#u.. 	97888199CDDD#r+   Fforcec                      t          d          )Nz-Compression is not supported for SSE streams.)NotImplementedError)r   r   s     r,   enable_compressionz&EventSourceResponse.enable_compression[  s    !"QRRRr+   rN   c                 n  K    |d| j         | j        d           d{V  | j        2 3 d{V }t          || j                  }t
                              d|           t          j        | j	                  5 } |d|dd           d{V  ddd           n# 1 swxY w Y   |r=|j
        r6t          | j        dd          }| |             d{V  t                      6 | j        4 d{V  d	| _         |dd
d	d           d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS )zHSend out SSE data to the client as it becomes available in the iterator.rT   )rR   statusro   Nz	chunk: %srS   TrR   body	more_bodyacloseFr+   )rn   raw_headersr   r   rs   loggerdebugr&   move_on_afterrv   cancel_calledr0   rY   r   r   )r   rN   datachunkcancel_scoper   s         r,   _stream_responsez$EventSourceResponse._stream_response^  s     d-*+ 
 
 	
 	
 	
 	
 	
 	
 	
 , 	) 	) 	) 	) 	) 	) 	)$ tx00ELLe,,,$T%677 <d15tTT                      
  ) : ) !3XtDD% &((NNNNNNN&((( - ? 	X 	X 	X 	X 	X 	X 	X 	XDK$ 4cPUVVWWWWWWWWW	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	Xs)   C&9BB	"B	4D$$
D.1D.receivec                    K   | j         rj |             d{V }|d         dk    rEd| _         t                              d           | j        r|                     |           d{V  dS | j         hdS dS )uq  Watch for a disconnect message from the client.

        Divergence #1 (see module docstring): kept unconditionally instead of
        adopting Starlette's ASGI 2.4 ``OSError → ClientDisconnect`` fast path,
        because this loop drives ``client_close_handler_callable`` and flips
        ``self.active = False`` for ``_ping`` and the shutdown grace loop.
        NrR   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)r   r   r   rw   )r   r   rP   s      r,   _listen_for_disconnectz*EventSourceResponse._listen_for_disconnectz  s       k 	#GIIooooooGv"333#JKKK5 F<<WEEEEEEEEE k 	 	 	 	 	r+   c                    K   t           j        rdS t                       t                      } t	          j                    }| j                            |           	 t           j        r	 | j                            |           dS |	                                 d{V  | j                            |           dS # | j                            |           w xY w)z0Wait for shutdown signal via the shared watcher.N)
r@   r6   rM   r3   r&   r'   r   adddiscardwait)r2   rE   s     r,   _listen_for_exit_signalz+EventSourceResponse._listen_for_exit_signal  s         	F,...#%%	($  L  ''''' **,,L  '''''EL  ''''s   B: B: :Cc                 L  K   |                                   d{V  | j        r| j                                         | j        dk    r\t	          j        | j                  5  | j        r!t	          j        d           d{V  | j        !ddd           dS # 1 swxY w Y   dS dS )a~  Wait for shutdown signal, then optionally give generator a grace period.

        Issue #167: When a shutdown_event is provided, the library sets it before
        returning, giving the generator a chance to send farewell events and exit
        cooperatively. The shutdown_grace_period controls how long to wait before
        force-cancelling via task group cancellation.
        Nr   g?)r   r   r%   r   r&   r   r   rB   r   s    r,   "_listen_for_exit_signal_with_gracez6EventSourceResponse._listen_for_exit_signal_with_grace  s      **,,,,,,,,,  	' $$&&& &**$T%@AA + +k ++c********* k ++ + + + + + + + + + + + + + + + + + +*s   !)BBBc                   K   | j         rt          j        | j                   d{V  | j        r|                                 n4t          dt          j        t          j	                   | j
                  }t          || j
                  }t                              d|           | j        4 d{V  | j         r |d|dd           d{V  ddd          d{V  n# 1 d{V swxY w Y   | j         dS dS )zPeriodically send ping messages to keep the connection alive on proxies.
        - frequenccy ca every 15 seconds.
        - Alternatively one can send periodically a comment line (one starting with a ':' character)
        Nzping - )commentrs   zping: %srS   Tr   )r   r&   rB   r   rt   r   r   nowr   utcrs   r   r   r   r   )r   rN   sse_ping
ping_bytess       r,   _pingzEventSourceResponse._ping  s     
 k 	+d1222222222 ,))+++$Bhl8<&@&@BB    &h99JLLZ000        ; $$8$.)-                                    k 	 	 	 	 	s   6C%%
C/2C/scopec                    K   |d         dk    rt                    t                      5  t          j                    4 d{V dt          g t
          d         f         ffd}                    | fd                               | fd                               | j                    j        r                     j                                       | fd           ddd          d{V  n# 1 d{V swxY w Y   ddd           n# 1 swxY w Y    j	         	                                 d{V  dS dS )	a  Entrypoint for Starlette's ASGI contract. We spin up tasks:
        - _stream_response to push events
        - _ping to keep the connection alive
        - _listen_for_exit_signal to respond to server shutdown
        - _listen_for_disconnect to respond to client disconnect
        rR   	websocketNcoroc                 ^   K    |              d {V  j                                          d S r   )r   cancel)r   
task_groups    r,   cancel_on_finishz6EventSourceResponse.__call__.<locals>.cancel_on_finish  s9      $&&LLLLLLL+2244444r+   c                  .                                    S r   )r   r   rN   s   r,   <lambda>z.EventSourceResponse.__call__.<locals>.<lambda>  s    d.C.CD.I.I r+   c                  .                                    S r   )r   r   s   r,   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    

4@P@P r+   c                  .                                    S r   )r   )r   r   s   r,   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    d.I.I'.R.R r+   )
rW   r   r&   create_task_groupr
   r	   
start_soonr   ru   rq   )r   r   r   rN   r   r   s   ` `` @r,   __call__zEventSourceResponse.__call__  sv      =K''.t44D
  !! 	 	.00       J5"io:M1N 5 5 5 5 5 5 %%$&I&I&I&I&I   %%&68P8P8P8P8PQQQ%%$d&M   , E))$*CDDD %%$&R&R&R&R&R  %                          	 	 	 	 	 	 	 	 	 	 	 	 	 	 	. ?&//########### '&s6   D$
B+D5D$
D	D$D	D$$D(+D()rk   Nrl   NNNNNNNNr   )Fr-   N)%r!   r"   r#   r$   r   r   ContentStreamr   r   r   strr   r
   r   r   r   r   r	   r&   r'   r   propertyr   r   setterr)   r   r   r   r   r   re   r   r   r   r   r   r*   r+   r,   rh   rh      s        . 
 /3-/3"!HL (, 04'(%G' G'G' G' '#s(+,	G'
 G' ^,G' smG' c]G' 'xO0C'DEG' 'R#3445
G' uoG' (0gY	$/0(
G'" !-#G'$  %%G'& 
'G' G' G' G'R #uS%Z0 # # # X# $5e#4 $ $ $ $ $S S S S S S SX4 XD X X X X8G     " ( ( ( \(&+ + + +(     6($E ($G ($4 ($D ($ ($ ($ ($ ($ ($r+   rh   r   )Dr$   rH   loggingr7   	threadingdataclassesr   r   r   r   typingr   r   r	   r
   r   r   r   r   r   r   r&   starlette.backgroundr   starlette.concurrencyr   starlette.datastructuresr   starlette.responsesr   starlette.typesr   r   r   r   sse_starlette._utilsr   sse_starlette.eventr   r   	getLoggerr!   r   r   localr1   r3   r>   rF   rM   rW   TimeoutErrorrY   r@   uvicorn.mainrf   rd   r[   ImportErrorr   r   bytesdictContentSyncContentStreamAsyncContentStreamr   rh   r*   r+   r,   <module>r      s   8        ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' '                         / / / / / / 7 7 7 7 7 7 3 3 3 3 3 3 ( ( ( ( ( ( 9 9 9 9 9 9 9 9 9 9 9 9 3 3 3 3 3 3 = = = = = = = = 
	8	$	$ " " " " " " " "  	!!^      .!& !& !& !&H
* 
* 
* 
*d t    $	 	 	 	 	| 	 	 	#8 #8 #8 #8 #8 #8 #8 #8L######!'!3I".F   
LLR    
 UD/36
7W% "7+ (*;;<K$ K$ K$ K$ K$( K$ K$ K$ K$ K$s   C9 9DD