
    Q
i0@                        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j%        e&          Z'e G d d                      Z( ej)                    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 e3j4        e1_5        e1j4        e3_4        n # e6$ r e'7                    d           Y nw xY wee8e9e:e#e
f         Z;ee;         Z<ee;         Z=ee=e<f         Z> G d de          Z?dS )    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                   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     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\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_stater2   1   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hasattrr4   	Exception)handlerservers     r+   _get_uvicorn_serverr=   :   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)r2   r=   	AppStatusr5   enable_automatic_graceful_drainr%   sleeplistr   r$   r   )r1   uvicorn_serverevents      r+   _shutdown_watcherrE   Q   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)r2   r   asyncioget_running_loopcreate_taskrE   RuntimeError)r1   loops     r+   $_ensure_watcher_started_on_this_looprL   u   s    !!E  * $	*+--D.0011111 	* 	* 	*$)E!!!!	** *s   4A A)(A)c                       e Zd ZdS )SendTimeoutErrorN)r    r!   r"   r)   r*   r+   rN   rN      s        Dr*   rN   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?   r@   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().
        TNrR   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?   r@   r5   rP   )argskwargss     r+   handle_exitzAppStatus.handle_exit   sB    4 	)$(I!%1&777777 21r*   )r    r!   r"   r#   r5   r@   rP   r   r
   r'   staticmethodrS   rU   rY   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)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_SEPARATORrh   
isinstancer   body_iteratorr   rc   re   rf   rj   rk   r   update
setdefaultinit_headersDEFAULT_PING_INTERVALping_intervalri   rl   _shutdown_event_shutdown_grace_periodactiver%   Lock
_send_lock)selfrb   rc   rd   re   rf   rg   rh   ri   rj   rk   rl   rm   rn   _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_interval   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)rv   intfloat	TypeErrorrt   r   )r   r   s     r+   r|   z!EventSourceResponse.ping_interval$  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*   sendc                 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.zhttp.response.start)typestatusrd   Nz	chunk: %shttp.response.bodyTr   body	more_bodyacloseFr*   )rc   raw_headersrw   r   rh   loggerdebugr%   move_on_afterrk   cancel_calledr/   rN   r   r   )r   r   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 )z/Watch for a disconnect message from the client.Nr   zhttp.disconnectFz+Got event: http.disconnect. Stop streaming.)r   r   r   rl   )r   r   messages      r+   _listen_for_disconnectz*EventSourceResponse._listen_for_disconnectK  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?   r5   rL   r2   r%   r&   r   adddiscardwait)r1   rD   s     r+   _listen_for_exit_signalz+EventSourceResponse._listen_for_exit_signalV  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   rA   r   s    r+   "_listen_for_exit_signal_with_gracez6EventSourceResponse._listen_for_exit_signal_with_gracej  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 - )commentrh   zping: %sr   Tr   )r   r%   rA   r   ri   r   r   nowr   utcrh   r   r   r   r   )r   r   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   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    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
        Ncoroc                 ^   K    |              d {V  j                                          d S r   )r   cancel)r   
task_groups    r+   cancel_on_finishz6EventSourceResponse.__call__.<locals>.cancel_on_finish  s9      dff'..00000r*   c                  .                                    S r   )r   r   r   s   r+   <lambda>z.EventSourceResponse.__call__.<locals>.<lambda>  s    D<Q<QRV<W<W r*   c                  .                                    S r   )r   r   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    DJJt<L<L r*   c                  .                                    S r   )r   )r   r   s   r+   r   z.EventSourceResponse.__call__.<locals>.<lambda>  s    $*E*Eg*N*N r*   )r%   create_task_groupr
   r	   
start_soonr   rj   rf   )r   r   r   r   r   r   s   ` `` @r+   __call__zEventSourceResponse.__call__  s      *,, 	 	 	 	 	 	 	
1Xb)D/6I-J 1 1 1 1 1 1 !!"24W4W4W4W4WXXX!!"24L4L4L4L4LMMM!! $"I   ( A%%d&?@@@ !! "N"N"N"N"N  !	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	( ?&//########### '&s   B+C
C'*C')r`   Nra   NNNNNNNNr   )Fr,   N)%r    r!   r"   r#   r{   ru   ContentStreamr   r   r   strr   r
   r   r   r   r   r	   r%   r&   r   propertyr   r|   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' 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*   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      sL          ( ( ( ( ( ( ( ( ' ' ' ' ' ' ' '                         / / / / / / 7 7 7 7 7 7 3 3 3 3 3 3 ( ( ( ( ( ( 9 9 9 9 9 9 9 9 9 9 9 9 = = = = = = = = 
	8	$	$ " " " " " " " "  	!!^      .!& !& !& !&H
* 
* 
* 
*	 	 	 	 	| 	 	 	#8 #8 #8 #8 #8 #8 #8 #8L######!'!3I".F   
LLR    
 UD/36
7W% "7+ (*;;<y$ y$ y$ y$ y$( y$ y$ y$ y$ y$s   
C) )DD