
    Vi0                     b   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#J$r$  \RJ                  " \&5      r'\ " S S5      5       r(\RR                  " 5       r*S\(4S jr+S r,SS jr-SS jr. " S S\/5      r0 " S S5      r1 S SK2J3r3  \3Rh                  \1l5        \1Rh                  \3l4        \\8\9\:\#\
4   r;\\;   r<\\;   r=\\=\<4   r> " S S\5      r?g! \6 a    \'Ro                  S5         N@f = f)    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)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!       d/home/james-whalen/.local/share/pipx/venvs/semgrep/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   r1   )states    r.   _get_shutdown_stater5   1   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hasattrr7   	Exception)handlerservers     r.   _get_uvicorn_serverr@   :   sa    ""6>>27J''%%Fv}--   s   AA 
A$#A$c                  f  #    [        5       n [        5       n  [        R                  (       a  O@Ub  UR                  (       a  S[        l        O [        R
                  " S5      I Sh  vN   MV  [        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)
r5   r@   	AppStatusr8   r(   sleeplistr   r'   r    )r4   uvicorn_serverevents      r.   _shutdown_watcherrG   Q   s       !E(*N&$$)n.H.H(,	%++c"""  %,,'EIIK ( !& # !&s/   B1AB% (B#)2B% B1#B% %	B..B1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)r5   r    asyncioget_running_loopcreate_taskrG   RuntimeError)r4   loops     r.   $_ensure_watcher_started_on_this_looprN   q   s^    !E   $	*++-D.01	 !
  	*$)E!	*s   .A A('A(c                       \ rS rSrSrg)SendTimeoutError~   r!   N)r"   r#   r$   r%   r,   r!   r-   r.   rP   rP   ~   s    r-   rP   c                   B    \ rS rSr% SrSrSr\\   \	S'   \
S 5       rSrg)rB      z\Helper to capture a shutdown signal from Uvicorn so we can gracefully terminate SSE streams.FNoriginal_handlerc                  j    S[         l        [         R                  b  [         R                  " U 0 UD6  g g )NT)rB   r8   rT   )argskwargss     r.   handle_exitAppStatus.handle_exit   s.     $	%%1&&77 2r-   r!   )r"   r#   r$   r%   r&   r8   rT   r   r
   r*   staticmethodrX   r,   r!   r-   r.   rB   rB      s,    fK+/hx(/8 8r-   rB   )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S4S jjr\S\\\4   4S j5       r\R.                  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S4S jr S\!S\S\SS4S  jr"S!r#g)%EventSourceResponse   z^
Streaming response that sends data conforming to the SSE (Server-Sent Events) specification.
   
Ncontentstatus_codeheaders
media_type
backgroundpingsepping_message_factorydata_sender_callable)NNNsend_timeoutclient_close_handler_callabler/   c                 0   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        S	U l        [*        R,                  " 5       U l        g )
N)Nr`   
z'sep must be one of: \r\n, \r, \n, got: zCache-Controlzno-storez
keep-alive
ConnectionnozX-Accel-BufferingT)
ValueErrorDEFAULT_SEPARATORrg   
isinstancer   body_iteratorr   rb   rd   re   ri   rj   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalrh   rk   activer(   Lock
_send_lock)selfra   rb   rc   rd   re   rf   rg   rh   ri   rj   rk   _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***,r-   c                     U R                   $ N)_ping_interval)r}   s    r.   ry   !EventSourceResponse.ping_interval   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)rs   intfloat	TypeErrorrq   r   )r}   r   s     r.   ry   r      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-   sendc                 
  #    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5      (       a"  U R                  R                  5       I Sh  vN   [        5       e N N Ns! , (       d  f       Nw= 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.zhttp.response.start)typestatusrc   Nz	chunk: %shttp.response.bodyTr   body	more_bodyacloseFr-   )rb   raw_headersrt   r   rg   loggerdebugr(   move_on_afterrj   cancel_calledr<   r   rP   r|   rz   )r}   r   datachunkcancel_scopes        r.   _stream_response$EventSourceResponse._stream_response   s6    -**++
 	
 	
 ,, 	)$ xx0ELLe,$$T%6%67<15tT   8
 | : : :4--x88,,33555&(('	
	) 87 6 - ???DK 4cPUVWWW #?????s   $FDFD$DD$AFDDD F1F8F<D"=FD$D
D	F$F5D86F:E)EE)F"E%#F)F /E20F <F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)z/Watch for a disconnect message from the client.Nr   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)rz   r   r   rk   )r}   r   messages      r.   _listen_for_disconnect*EventSourceResponse._listen_for_disconnect  sj     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)
rB   r8   rN   r5   r(   r)   r   adddiscardwait)r4   rF   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                   (       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 - )commentrg   zping: %sr   Tr   )rz   r(   rC   r   rh   r   r   nowr   utcrg   r   r   r   r|   )r}   r   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                 J  ^ ^^^#    [         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   T R                  b  T R                  5       I Sh  vN   gg N N-! , ISh  vN  (       d  f       NB= f N$7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
N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_finishQ  s&     f''..0 s   0. 0c                  &   > T R                  T5      $ r   )r   r}   r   s   r.   <lambda>.EventSourceResponse.__call__.<locals>.<lambda>U  s    D<Q<QRV<Wr-   c                  &   > T R                  T5      $ r   )r   r   s   r.   r   r   V  s    DJJt<Lr-   c                  &   > TR                  T 5      $ r   )r   )r   r}   s   r.   r   r   ^  s    $*E*Eg*Nr-   )r(   create_task_groupr
   r	   
start_soonr   ri   re   )r}   r   r   r   r   r   s   ` `` @r.   __call__EventSourceResponse.__call__H  s      **,,
1Xb)D/6I-J 1 !!"24WX!!"24LM!!"2D4P4PQ((%%d&?&?@ !! "N -,$ ??&//### '% -,,,& $sQ   D#DD#B(DD#D$D#<D!=D#D#DDDD#)r   r|   rz   re   rt   rk   ri   rd   ry   rh   rj   rg   rb   )
   Nztext/event-streamNNNNNNN)Fr/   N)$r"   r#   r$   r%   r&   rx   rr   ContentStreamr   r   r   strr   r
   r   r   r   r   r	   r   propertyr   ry   setterr+   r   r   r   r   r   rZ   r   r   r   r   r,   r!   r-   r.   r]   r]      s    
 /3-/3"!HL (, !;';' ;' '#s(+,	;'
 ;' ^,;' sm;' c];' 'xO0C'DE;' 'R#3445
;' uo;' (0gY	$/0(
;'" 
#;'z #uS%Z0 # # $5e#4 $ $ $S S SX4 XD X6	G 	 	 ( (&  6$E $G $4 $D $r-   r]   r   )@rI   loggingr9   	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.eventr   r   	getLoggerr"   r   r   localr3   r5   r@   rG   rN   TimeoutErrorrP   rB   uvicorn.mainr[   rX   rT   ImportErrorr   r   bytesdictContentSyncContentStreamAsyncContentStreamr   r]   r!   r-   r.   <module>r      s;       ( '    / 7 3 ( 9 9 = 
		8	$ " " " !^ .&@
*	| 	
8 
8#!'!3!3I"..F UD/36
7W% "7+ (*;;<C$( C$  
LLRs   (D D.-D.