
    עi0@                     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#m$Z$  ejJ                  e&      Z'e G d d             Z( ejR                         Z*de(fdZ+d Z,ddZ-ddZ. G d de/      Z0 G d d      Z1	 d dl2m3Z3 e3jh                  e1_5        e1jh                  e3_4        ee8e9e:e#e
f   Z;ee;   Z<ee;   Z=ee=e<f   Z> G d de      Z?y# e6$ r e'jo                  d       Y ?w xY w)    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                   V    e Zd ZU dZ ee      Zeej                     e
d<   dZee
d<   y)_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     R/sites/domain-asset-master/.venv/lib/python3.12/site-packages/sse_starlette/sse.pyr   r   !   s+      %S9FC9!OT!r*   r   returnc                  V    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_stater2   1   s+    M#3T:E} ',$Lr*   c                      	 t        j                  t         j                        } t        | d      r| j                  }t        |d      r|S y# t
        $ r Y yw xY w)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hasattrr4   	Exception)handlerservers     r+   _get_uvicorn_serverr=   :   s[    ""6>>27J'%%Fv}-   s   AA 	AAc                  j  K   t               } t               }	 	 t        j                  rnHt        j                  r||j                  rdt        _        nt        j                  d       d{    Yt        | j                        D ]  }|j                           	 d| _
        y7 8# d| _
        w xY w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)r2   r=   	AppStatusr5   enable_automatic_graceful_drainr%   sleeplistr   r$   r   )r1   uvicorn_serverevents      r+   _shutdown_watcherrE   Q   s       !E(*N&$$ 99"."..(,	%++c"""  %,,' 	EIIK	 !& # !&s/   B3AB' ,B%-/B' B3%B' '	B00B3c                      t               } | j                  s6d| _        	 t        j                         }|j	                  t                      yy# t        $ r
 d| _        Y yw xY w)zDEnsure the shutdown watcher is running for this thread (event loop).TFN)r2   r   asyncioget_running_loopcreate_taskrE   RuntimeError)r1   loops     r+   $_ensure_watcher_started_on_this_looprL   u   s^    !E   $	*++-D.01	 !
  	*$)E!	*s   -A A! A!c                       e Zd Zy)SendTimeoutErrorN)r    r!   r"   r)   r*   r+   rN   rN      s    r*   rN   c                   ^    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y)	r?   z\Helper to capture a shutdown signal from Uvicorn so we can gracefully terminate SSE streams.FTNoriginal_handlerc                      dt         _        y)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?   r@   r)   r*   r+    disable_automatic_graceful_drainz*AppStatus.disable_automatic_graceful_drain   s     5:	1r*   c                      dt         _        y)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().
        TNrR   r)   r*   r+   $enable_automatic_graceful_drain_modez.AppStatus.enable_automatic_graceful_drain_mode   s     59	1r*   c                      t         j                  rdt         _        t         j                  t        j                  | i | y y )NT)r?   r@   r5   rP   )argskwargss     r+   handle_exitzAppStatus.handle_exit   s7    44$(I!%%1&&77 2r*   )r    r!   r"   r#   r5   r@   rP   r   r
   r'   staticmethodrS   rU   rY   r)   r*   r+   r?   r?      sZ    fK&*#+/hx(/	: 	: 9 9 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j0                  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$y)&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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                 Z   |dvrt        d|       |xs | j                  | _        t        |t              r|| _        nt        |      | _        || _        || j                  n|| _        || _	        |	| _
        |
| _        t               }||j                  |       |j                  dd       d|d<   d|d<   | j                  |       || j                   n|| _        || _        || _        |d	k  rt        d
      || _        || _        d| _        t/        j0                         | _        y )N)Nr_   
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_SEPARATORrf   
isinstancer   body_iteratorr   ra   rc   rd   rh   ri   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalrg   rj   _shutdown_event_shutdown_grace_periodactiver%   Lock
_send_lock)selfr`   ra   rb   rc   rd   re   rf   rg   rh   ri   rj   rk   rl   _headerss                  r+   __init__zEventSourceResponse.__init__   s3   * 00J3%PQQ0$00 g}-!(D!6w!?D&-7-?$//Z$$8!( "#OOG$ 	OZ8!-(,$%(#;?<T77T$8!-J* !1$ABB-&;#**,r*   c                     | j                   S N)_ping_intervalr   s    r+   rz   z!EventSourceResponse.ping_interval   s    """r*   valuec                 t    t        |t        t        f      st        d      |dk  rt	        d      || _        y )Nzping interval must be intr   z$ping interval must be greater than 0)rt   intfloat	TypeErrorrr   r   )r   r   s     r+   rz   z!EventSourceResponse.ping_interval$  s7    %#u.78819CDD#r*   forcec                     t        d      )Nz-Compression is not supported for SSE streams.)NotImplementedError)r   r   s     r+   enable_compressionz&EventSourceResponse.enable_compression,  s    !"QRRr*   sendc                   K    |d| j                   | j                  d       d{    | j                  2 3 d{   }t        || j                        }t
        j                  d|       t        j                  | j                        5 } |d|dd       d{    ddd       st|j                  st        | j                  dd      }| |        d{    t               7 7 7 R# 1 sw Y   QxY w7  6 | j                  4 d{  7   d	| _         |dd
d	d       d{  7   ddd      d{  7   y# 1 d{  7  sw Y   yxY ww)zHSend out SSE data to the client as it becomes available in the iterator.zhttp.response.start)typestatusrb   Nz	chunk: %shttp.response.bodyTr   body	more_bodyacloseFr*   )ra   raw_headersru   r   rf   loggerdebugr%   move_on_afterri   cancel_calledr/   rN   r   r}   )r   r   datachunkcancel_scoper   s         r+   _stream_responsez$EventSourceResponse._stream_response/  sU    -**++
 	
 	
 ,, 	) 	)$ txx0ELLe,$$T%6%67 <15tT  
  : : !3!3XtD% (NN&(()	
	)  # - ?? 	X 	XDK 4cPUVWWW	X 	X 	X 	X 	Xs   $EC*EC>C,C>AEC0C.C0 
E+E8#EC<E,C>.C00C9	5E>EDEE+D.,E1E<D?=EE	E
EEreceivec                    K   | j                   rg |        d{   }|d   dk(  rBd| _         t        j                  d       | j                  r| j                  |       d{    y| j                   rfyy7 ]7 w)z/Watch for a disconnect message from the client.Nr   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)r}   r   r   rj   )r   r   messages      r+   _listen_for_disconnectz*EventSourceResponse._listen_for_disconnectK  sj     kk#IoGv"33#JK55<<WEEE kk%
 Fs(   A;A7AA;"A9#A;5A;9A;c                    K   t         j                  ryt                t               } t	        j
                         }| j                  j                  |       	 t         j                  r	 | j                  j                  |       y|j                          d{    | j                  j                  |       y7  # | j                  j                  |       w xY ww)z0Wait for shutdown signal via the shared watcher.N)
r?   r5   rL   r2   r%   r&   r   adddiscardwait)r1   rD   s     r+   _listen_for_exit_signalz+EventSourceResponse._listen_for_exit_signalV  s        ,.#%	($$ LL  ' **,LL  ' LL  's<   ACB; )CB; B9B; C9B; ;CCc                   K   | j                          d{    | j                  r| j                  j                          | j                  dkD  r_t	        j
                  | j                        5  | j                  r*t	        j                  d       d{    | j                  r*ddd       yy7 7 # 1 sw Y   yxY ww)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}   rA   r   s    r+   "_listen_for_exit_signal_with_gracez6EventSourceResponse._listen_for_exit_signal_with_gracej  s      **,,,   $$& &&*$$T%@%@A +kk++c*** kk+ + + 	- ++ +s:   C B0AC /%B4B2B4&C 2B44B=9C c                 X  K   | j                   rt        j                  | j                         d{    | j                  r| j	                         n:t        dt        j                  t        j                         | j                        }t        || j                        }t        j                  d|       | j                  4 d{    | j                   r |d|dd       d{    ddd      d{    | j                   ryy7 7 E7 '7 # 1 d{  7  sw Y   )xY ww)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 - )commentrf   zping: %sr   Tr   )r}   r%   rA   r   rg   r   r   nowr   utcrf   r   r   r   r   )r   r   sse_ping
ping_bytess       r+   _pingzEventSourceResponse._ping~  s     
 kk++d11222 ,, ))+$%hll8<<&@%AB  &h9JLLZ0  ;;$8$.)-    kk2   sp   /D*DBD*	D
D*D)D*D.D*9D:D*D*D*DD*D'DD'#D*scopec                 (   K   t        j                         4 d{   dt        g t        d   f   ffd}j	                  | fd       j	                  | fd       j	                  | j
                          j                  rj	                   j                         j	                  | fd       ddd      d{     j                   j                          d{    yy7 7 ,# 1 d{  7  sw Y   <xY w7 w)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
        Ncoroc                 d   K    |         d {    j                   j                          y 7 wr   )r   cancel)r   
task_groups    r+   cancel_on_finishz6EventSourceResponse.__call__.<locals>.cancel_on_finish  s'     f''..0 s   0. 0c                  &     j                        S r   )r   r   r   s   r+   <lambda>z.EventSourceResponse.__call__.<locals>.<lambda>  s    D<Q<QRV<W r*   c                  &     j                        S r   )r   r   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    DJJt<L r*   c                  &    j                         S r   )r   )r   r   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    $*E*Eg*N r*   )r%   create_task_groupr
   r	   
start_soonr   rh   rd   )r   r   r   r   r   r   s   ` `` @r+   __call__zEventSourceResponse.__call__  s      **, 	 	
1Xb)D/6I-J 1 !!"24WX!!"24LM!! $"I"I ((%%d&?&?@ !! "N!	 	( ??&//### ')	 	 	 	 	* $sQ   DC7DBC;DC9#D0D1D9D;DDD	D)   Nztext/event-streamNNNNNNNNr   )Fr,   N)%r    r!   r"   r#   ry   rs   ContentStreamr   r   r   strr   r
   r   r   r   r   r	   r%   r&   r   propertyr   rz   setterr(   r   r   r   r   r   rZ   r   r   r   r   r   r)   r*   r+   r]   r]      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*   r]   r   )@rG   loggingr6   	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   localr0   r2   r=   rE   rL   TimeoutErrorrN   r?   uvicorn.mainr[   rY   rP   ImportErrorr   r   bytesdictContentSyncContentStreamAsyncContentStreamr   r]   r)   r*   r+   <module>r      s@       ( '    / 7 3 ( 9 9 = 
		8	$ " " "  	!^ .!&H
*	| 	#8 #8L#!'!3!3I"..F UD/36
7W% "7+ (*;;<y$( y$  
LLRs   (D D*)D*