
    CjRL                        S r SSKrSSKrSSKrSSKrSSKJrJr  SSKJrJ	r	  SSK
JrJrJrJrJrJrJrJrJrJr  SSKrSSKJr  SSKJr  SSKJr  SS	KJr  SS
KJrJ r J!r!J"r"  SSK#J$r$  SSK%J&r&J'r'  \RP                  " \)5      r*\ " S S5      5       r+\RX                  " 5       r-S\+4S jr.S r/SS jr0SS jr1S\!S\!4S jr2 " S S\35      r4 " S S5      r5 SSK6J7r7  \7Rp                  \5l9        \5Rp                  \7l8        \\<\=\>\&\4   r?\\?   r@\\?   rA\\A\@4   rB " S S\5      rCg! \: a    \*Rw                  S5         N@f = f)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                   X    \ rS rSr% Sr\" \S9r\\	R                     \S'   Sr\\S'   Srg)	_ShutdownState>   zPer-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_started N)__name__
__module____qualname____firstlineno____doc__r   setr    r   anyioEvent__annotations__r!   bool__static_attributes__r"       c/home/rurouni/.local/share/pipx/venvs/strix-agent/lib/python3.13/site-packages/sse_starlette/sse.pyr   r   >   s+      %S9FC9!OT!r.   r   returnc                  X    [        [        SS5      n U c  [        5       n U [        l        U $ )z4Get or create shutdown state for the current thread.shutdown_stateN)getattr_thread_stater   r2   )states    r/   _get_shutdown_stater6   N   s+    M#3T:E} ',$Lr.   c                       [         R                  " [         R                  5      n [        U S5      (       a  U R                  n[        US5      (       a  U$ g! [
         a     gf = f)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hasattrr8   	Exception)handlerservers     r/   _get_uvicorn_serverrA   W   sa    ""6>>27J''%%Fv}--   s   AA 
A$#A$c                    #    [        5       n [        5       n  [        R                  (       a  OU[        R                  (       a   Ub  UR                  (       a  S[        l        O [
        R                  " S5      I Sh  vN   Mk  [        U R                  5       H  nUR                  5         M     SU l
        g N:! SU l
        f = f7f)aO  
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)r6   rA   	AppStatusr9   enable_automatic_graceful_drainr)   sleeplistr    r(   r!   )r5   uvicorn_serverevents      r/   _shutdown_watcherrI   n   s       !E(*N&$$ 99"."..(,	%++c"""  %,,'EIIK ( !& # !&s/   CA%B: =B8>2B: 0C8B: :	CCc                      [        5       n U R                  (       d7  SU l         [        R                  " 5       nUR	                  [        5       5        gg! [         a
    SU l         gf = f)zDEnsure the shutdown watcher is running for this thread (event loop).TFN)r6   r!   asyncioget_running_loopcreate_taskrI   RuntimeError)r5   loops     r/   $_ensure_watcher_started_on_this_looprP      s^    !E   $	*++-D.01	 !
  	*$)E!	*s   .A A('A(sendc                 (   ^  S[         SS4U 4S jjnU$ )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.
messager0   Nc                 ^   >#    U S   nUS;   a  0 U ESSU-   0En T" U 5      I S h  vN   g  N7f)Ntype>   http.response.bodyhttp.response.startz
websocket.r"   )rS   message_typerQ   s     r/   wrapped,_wrap_websocket_denial_send.<locals>.wrapped   s;     vHHFF&,*EFG7ms   "-+-)r   )rQ   rY   s   ` r/   _wrap_websocket_denial_sendr[      s    w 4  Nr.   c                       \ rS rSrSrg)SendTimeoutError   r"   N)r#   r$   r%   r&   r-   r"   r.   r/   r]   r]      s    r.   r]   c                   f    \ rS rSr% SrSrSrSr\\	   \
S'   \S 5       r\S 5       r\S	 5       rS
rg)rC      z\Helper to capture a shutdown signal from Uvicorn so we can gracefully terminate SSE streams.FTNoriginal_handlerc                      S[         l        g)a  
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rC   rD   r"   r.   r/    disable_automatic_graceful_drain*AppStatus.disable_automatic_graceful_drain   s     5:	1r.   c                      S[         l        g)z
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().
TNrc   r"   r.   r/   $enable_automatic_graceful_drain_mode.AppStatus.enable_automatic_graceful_drain_mode   s     59	1r.   c                      [         R                  (       a  S[         l        [         R                  b  [         R                  " U 0 UD6  g g )NT)rC   rD   r9   ra   )argskwargss     r/   handle_exitAppStatus.handle_exit   s7    44$(I!%%1&&77 2r.   r"   )r#   r$   r%   r&   r'   r9   rD   ra   r   r
   r+   staticmethodrd   rg   rl   r-   r"   r.   r/   rC   rC      sZ    fK&*#+/hx(/	: 	: 9 9 8 8r.   rC   )ServerzHUvicorn not installed. Graceful shutdown on server termination disabled.c                      \ rS rSrSrSrSr            S%S\S\S\	\
\\4      S	\S
\	\   S\	\   S\	\   S\	\/ \4      S\	\/ \S   4      S\	\   S\	\\/\S   4      S\	\R(                     S\SS4S jjr\S\\\4   4S j5       r\R2                  S\\\4   SS4S j5       rS&S\SS4S jjrS\SS4S jrS\SS4S jr\ S'S j5       r!S'S  jr"S\SS4S! jr#S"\$S\S\SS4S# jr%S$r&g)(EventSourceResponse   a  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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_periodr0   c                 j   US;  a  [        SU 35      eU=(       d    U R                  U l        [        U[        5      (       a  Xl        O[        U5      U l        X l        Uc  U R                  OUU l        XPl	        Xl
        Xl        [        5       nUb  UR                  U5        UR                  SS5        SUS'   SUS'   U R                  U5        Uc  U R                   OUU l        Xl        Xl        US	:  a  [        S
5      eXl        Xl        SU l        [.        R0                  " 5       U l        g )N)Nrt   
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_SEPARATORr{   
isinstancer   body_iteratorr   rv   rx   ry   r}   r~   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalr|   r   _shutdown_event_shutdown_grace_periodactiver)   Lock
_send_lock)selfru   rv   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   _headerss                  r/   __init__EventSourceResponse.__init__  s$   * 00J3%PQQ0$00 g}--!(!6w!?D&-7-?$//Z$$8!( "#OOG$ 	OZ8!-(,$%(#;?<T77T$8!-J* !1$ABB-&;#**,r.   c                     U R                   $ N)_ping_intervalr   s    r/   r   !EventSourceResponse.ping_intervalO  s    """r.   valuec                 ~    [        U[        [        45      (       d  [        S5      eUS:  a  [	        S5      eXl        g )Nzping interval must be intr   z$ping interval must be greater than 0)r   intfloat	TypeErrorr   r   )r   r   s     r/   r   r   S  s8    %#u..78819CDD#r.   forcec                     [        S5      e)Nz-Compression is not supported for SSE streams.)NotImplementedError)r   r   s     r/   enable_compression&EventSourceResponse.enable_compression[  s    !"QRRr.   rQ   c                   #    U" SU R                   U R                  S.5      I Sh  vN   U R                    Sh  vN n[        X R                  5      n[
        R                  SU5        [        R                  " U R                  5       nU" SUSS.5      I Sh  vN   SSS5        W(       d  Mz  UR                  (       d  M  [        U R                  SS5      nUb  U" 5       I Sh  vN   [        5       e N N N_! , (       d  f       Nc= f N%
 U R                   ISh  vN    S	U l        U" SS
S	S.5      I Sh  vN    SSS5      ISh  vN    g! , ISh  vN  (       d  f       g= f7f)zHSend out SSE data to the client as it becomes available in the iterator.rW   )rU   statusrw   Nz	chunk: %srV   TrU   body	more_bodyacloseFr.   )rv   raw_headersr   r   r{   loggerdebugr)   move_on_afterr~   cancel_calledr3   r]   r   r   )r   rQ   datachunkcancel_scoper   s         r/   _stream_response$EventSourceResponse._stream_response^  s.    -**++
 	
 	
 ,, 	)$ xx0ELLe,$$T%6%67<15tT   8
 | : : : !3!3XtD% (NN&(()	
	) 87 # - ???DK 4cPUVWWW #?????s   $E/C7E/DC9DAE/C=C;C= E/1E/$E/(D)E/9D;C==
D	E/E/!D$"E/&E=E >EE/EE/E,EE,(E/receivec                   #    U R                   (       as  U" 5       I Sh  vN nUS   S:X  aG  SU l         [        R                  S5        U R                  (       a  U R                  U5      I Sh  vN   gU R                   (       a  Mr  gg Ni N7f)uI  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.
NrU   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)r   r   r   r   )r   r   rS   s      r/   _listen_for_disconnect*EventSourceResponse._listen_for_disconnectz  sl      kk#IoGv"33#JK55<<WEEE kkk%
 Fs(   BBAB-B
.BB
Bc                    #    [         R                  (       a  g[        5         [        5       n [        R
                  " 5       nU R                  R                  U5         [         R                  (       a   U R                  R                  U5        gUR                  5       I Sh  vN   U R                  R                  U5        g N ! U R                  R                  U5        f = f7f)z0Wait for shutdown signal via the shared watcher.N)
rC   r9   rP   r6   r)   r*   r    adddiscardwait)r5   rH   s     r/   _listen_for_exit_signal+EventSourceResponse._listen_for_exit_signal  s        ,.#%	($$ LL  ' **,LL  ' LL  's<   AC&C 4C&C #C$C (C&C C##C&c                   #    U R                  5       I Sh  vN   U R                  (       a  U R                  R                  5         U R                  S:  al  [        R
                  " U R                  5         U R                  (       a1  [        R                  " S5      I Sh  vN   U R                  (       a  M1  SSS5        gg N N#! , (       d  f       g= f7f)aV  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   rE   r   s    r/   "_listen_for_exit_signal_with_grace6EventSourceResponse._listen_for_exit_signal_with_grace  s      **,,,   $$& &&*$$T%@%@Akk++c*** kkk BA + 	- + BAs:   CCAC6+C!C"C9CC
CCc                   #    U R                   (       Ga  [        R                  " U R                  5      I Sh  vN   U R                  (       a  U R	                  5       O8[        S[        R                  " [        R                  5       3U R                  S9n[        X R                  5      n[        R                  SU5        U R                   ISh  vN   U R                   (       a  U" SUSS.5      I Sh  vN   SSS5      ISh  vN   U R                   (       a  GM  gg N NQ N. N ! , ISh  vN  (       d  f       N5= f7f)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 - )commentr{   zping: %srV   Tr   )r   r)   rE   r   r|   r   r   nowr   utcr{   r   r   r   r   )r   rQ   sse_ping
ping_bytess       r/   _pingEventSourceResponse._ping  s     
 kkk++d11222 ,, ))+$%hll8<<&@%AB  &h9JLLZ0;;$8$.)-   ' kkk2 ' 'sp   6ED"BED$E!D*7D&8D*<ED(E E$E&D*(E*E0D31E=Escopec                   ^ ^^^#    US   S:X  a  [        T5      m[        5          [        R                  " 5        ISh  vN mS[        / [
        S   4   4U4S jjnTR                  UU U4S j5        TR                  UU U4S j5        TR                  UT R                  5        T R                  (       a  TR                  T R                  5        TR                  UUU 4S j5        SSS5      ISh  vN   SSS5        T R                  b  T R                  5       I Sh  vN   gg N N5! , ISh  vN  (       d  f       NJ= f! , (       d  f       NS= f N57f)	zEntrypoint 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
rU   	websocketNcoroc                 d   >#    U " 5       I S h  vN   TR                   R                  5         g  N7fr   )r   cancel)r   
task_groups    r/   cancel_on_finish6EventSourceResponse.__call__.<locals>.cancel_on_finish  s&     &LL++224 !s   0. 0c                  &   > T R                  T5      $ r   )r   r   rQ   s   r/   <lambda>.EventSourceResponse.__call__.<locals>.<lambda>  s    d.C.CD.Ir.   c                  &   > T R                  T5      $ r   )r   r   s   r/   r   r     s    

4@Pr.   c                  &   > TR                  T 5      $ r   )r   )r   r   s   r/   r   r     s    d.I.I'.Rr.   )
r[   r   r)   create_task_groupr
   r	   
start_soonr   r}   ry   )r   r   r   rQ   r   r   s   ` `` @r/   __call__EventSourceResponse.__call__  s     =K'.t4D
  !..00J5"io:M1N 5 %%$&I %%&68PQ%%$d&M&M ,,))$*C*CD %%$&R% 10 ". ??&//### '- 1000 "!0 $sp   EED* EB(D.+E6D,7E;(E#E$E*E,E.E	4D75E	E
EE)r   r   r   r   r   ry   r   r   r}   rx   r   r|   r~   r{   rv   )   Nztext/event-streamNNNNNNNNr   )Fr0   N)'r#   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   rn   r   r   r   r   r   r-   r"   r.   r/   rq   rq      s&   . 
 /3-/3"!HL (, 04'(%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'R #uS%Z0 # # $5e#4 $ $ $S S SX4 XD X8G  " ( (&+(  6($E ($G ($4 ($D ($r.   rq   r   )Dr'   rK   loggingr:   	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   localr4   r6   rA   rI   rP   r[   TimeoutErrorr]   rC   uvicorn.mainro   rl   ra   ImportErrorr   r   bytesdictContentSyncContentStreamAsyncContentStreamr   rq   r"   r.   r/   <module>r      sY  8     ( '    / 7 3 ( 9 9 3 = 
		8	$ " " " !^ .!&H
*d t $	| 	#8 #8L#!'!3!3I"..F UD/36
7W% "7+ (*;;<K$( K$  
LLRs   (D% %D?>D?