
    tj?                     
   d dl Z d dlZd dlZd dlmZ d dlmZ d dlmZm	Z	m
Z
 ddlmZmZmZmZ ddlmZ ddlmZ  ej(                  e      Zd	gZd
Z G d d      Z G d d      Z G d d      Z G d d      Z G d d	      Zy)    N)deque)contextmanager)AnyIterableIterator   )asynchronous	is_dunder
is_privateshare)reload)
TranslatorStatez*__trame__: non_init_value_that_is_not_Nonec                   N    e Zd ZdZd
dedefdZd Zedefd       Ze	d        Z
y	)StateStatusz*
    Tracks status flags for a State.
    flushingreadyc                      || _         || _        y Nr   r   )selfr   r   s      ?/DATA/.local/lib/python3.12/site-packages/trame_server/state.py__init__zStateStatus.__init__   s     
    c                     d| _         y )NTr   r   s    r   
mark_readyzStateStatus.mark_ready   s	    
r   returnc                 8    | j                   xs | j                   S r   r   r   s    r   skip_flushingzStateStatus.skip_flushing!   s    }}.DJJ.r   c              #   J   K   d| _         	 d d| _         y# d| _         w xY ww)z*Context manager for flushing state safely.TNF)r   r   s    r   flushing_contextzStateStatus.flushing_context%   s%      	"!DMEDMs   # #	 #N)FF)__name__
__module____qualname____doc__boolr   r   propertyr!   r   r#    r   r   r   r      sN     d  /t / / " "r   r   c                       e Zd ZdZdeddfdZdefdZdedefdZde	e   fd	Z
deddfd
ZddZdeddfdZdee   ddfdZy)_OrderedSetz}
    Lightweight ordered set implementation based on dict to preserve insertion order
    without external dependencies.
    argsr   Nc                 B    i | _         |D ]  }| j                  |        y r   )_dataadd)r   r-   args      r   r   z_OrderedSet.__init__5   s    &(
CHHSM r   c                 ,    t        | j                        S r   )r(   r/   r   s    r   __bool__z_OrderedSet.__bool__:       DJJr   keyc                     || j                   v S r   r/   r   r5   s     r   __contains__z_OrderedSet.__contains__=   s    djj  r   c                 ,    t        | j                        S r   )iterr/   r   s    r   __iter__z_OrderedSet.__iter__@   r4   r   c                 "    d | j                   |<   y r   r7   r8   s     r   r0   z_OrderedSet.addC   s    

3r   c                 8    | j                   j                          y r   )r/   clearr   s    r   r?   z_OrderedSet.clearF   s    

r   c                 <    | j                   j                  |d        y r   )r/   popr8   s     r   discardz_OrderedSet.discardI   s    

sD!r   iterablec                 4    |D ]  }| j                  |        y r   r0   )r   rC   items      r   updatez_OrderedSet.updateL   s    DHHTN r   r   N)r$   r%   r&   r'   r   r   r(   r3   r9   r   r<   r0   r?   rB   r   rG   r*   r   r   r,   r,   /   s    
c d 
 $  ! ! ! (3-  s t "3 "4 "x}  r   r,   c                   *    e Zd Zd Zd Zd Zd Zd Zy)StateChangeHandlerc                 0    || _         t               | _        y r   )_all_listenersr,   	_currents)r   	listenerss     r   r   zStateChangeHandler.__init__R   s    '$r   c                     || j                   v r0| j                   |   D ]  }| j                  j                  |        y y r   )rL   rM   r0   )r   r5   callbacks      r   r0   zStateChangeHandler.addV   s<    $%%% //4""8, 5 &r   c                 4    |D ]  }| j                  |        y r   rE   )r   keysr5   s      r   add_allzStateChangeHandler.add_all[   s    CHHSM r   c                 8    | j                   j                          y r   )rM   r?   r   s    r   r?   zStateChangeHandler.clear_   s    r   c                 >    t        t        | j                              S r   )r;   listrM   r   s    r   r<   zStateChangeHandler.__iter__b   s    D())r   N)r$   r%   r&   r   r0   rS   r?   r<   r*   r   r   rJ   rJ   Q   s    '-
*r   rJ   c                   z    e Zd ZdZd ZdeddfdZdeddfdZdeddfd	Zdd
Z	ddZ
defdZdedefdZddZy)_SuppressListenersChangeStackzW
    Helper class to handle change listener keys to suppress and which to trigger.
    c                 N    t               | _        d | _        t               | _        y r   )r   _deque_suppressed_keysr,   _listener_keysr   s    r   r   z&_SuppressListenersChangeStack.__init__k   s    */'48+6=r   r5   r   Nc                 ^    | j                  |      s| j                  j                  |       y y r   )_is_suppressedr\   r0   r8   s     r   on_pending_key_addedz2_SuppressListenersChangeStack.on_pending_key_addedp   s)    ""3'##C( (r   c                 :    | j                   j                  |       y r   )r\   rB   r8   s     r   on_pending_key_removedz4_SuppressListenersChangeStack.on_pending_key_removedt   s    ##C(r   rR   c                 f    | j                   j                  t        |        | j                          y r   )rZ   appendr,   _update_suppressed_keysr   rR   s     r   pushz"_SuppressListenersChangeStack.pushw   s%    ;-.$$&r   c                 r    | j                   sy | j                   j                          | j                          y r   )rZ   rA   rd   r   s    r   rA   z!_SuppressListenersChangeStack.pop{   s&    {{$$&r   c                 "    t               | _        y r   )r,   r\   r   s    r   r?   z#_SuppressListenersChangeStack.clear   s    )mr   c                     | j                   S r   )r\   r   s    r   get_change_listener_keysz6_SuppressListenersChangeStack.get_change_listener_keys   s    """r   c                 R    | j                   y| j                   sy|| j                   v S )NFT)r[   r8   s     r   r^   z,_SuppressListenersChangeStack._is_suppressed   s.      ($$d++++r   c                     | j                   sd| _        yt               | _        | j                   D ];  }|s| j                  j                           y| j                  j	                  |       = y)aI  
        Updates the suppressed keys set.
        If the stack is empty, the suppressed keys will be reset to None.
        If the stack contains one empty set (catch all) the suppressed set will be updated to an empty set (catch all).
        Otherwise, the suppressed keys will represent the union of the stack's sets.
        N)rZ   r[   r,   r?   rG   )r   d_sets     r   rd   z5_SuppressListenersChangeStack._update_suppressed_keys   sZ     {{$(D! +[[E%%++-!!((/	 !r   rH   )r$   r%   r&   r'   r   strr_   ra   rf   rA   r?   r,   rj   r(   r^   rd   r*   r   r   rX   rX   f   ss    9
) ) ))# )$ )'# '$ '',#+ #,# ,$ ,0r   rX   c                      e Zd ZdZ	 	 	 	 	 ddZedefd       Zedefd       Z	ddZ
d Zd	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd Zd Zed        Zdee   fdZd Zed        Zd Zd Zd Z e!defd       Z"y) r   z
    Flexible dictionary managing a server shared state.
    Variables can be accessed with either the `[]` or `.` notation.

    :examples:

    >>> with state:
    ...     state.a = 1
    ...     state.b = 2
    ... # state is flushed()
    Nc                    || _         || _        |r|n	t               | _        t	        |dt                     | _        t	        |di       | _        t	        |di       | _        t	        |di       | _	        t	        |dt        | j                              | _        t	        |dt        |            | _        || _        g | _        |r|j                  j!                  |        t#               | _        y )N_modified_keys_change_callbacks_pending_update_pushed_state_state_listeners_statusr   )_push_state_fn_hot_reloadr   _translatorr   setrq   rr   rs   rt   rJ   ru   r   rv   _parent_state_children_staterc   rX   _suppress_change_stack)r   
translatorinternal	commit_fn
hot_reloadr   s         r   r   zState.__init__   s     (%)3:#H.>F!&x1Db!I$X/@"E"8_bA %(*<T=S=S*T!
 Xy+E2JK%!$$++D1&C&E#r   r   c                 .    | j                   j                  S )zJReturn True is the instance is ready for synchronization, False otherwise.)rv   r   r   s    r   is_readyzState.is_ready   s     ||!!!r   c                     | j                   S )zDReturn the translator instance used to namespace the variable names.)ry   r   s    r   r~   zState.translator   s     r   c                 r    | j                   ry| j                  j                          | j                          y)z,Mark the state as ready for synchronization.N)r   rv   r   flushr   s    r   r   zState.ready   s%    ==!

r   c                     | j                   j                  |      }| j                  j                  || j                  j                  |            S r   )ry   translate_keyrs   getrt   r8   s     r   __getitem__zState.__getitem__   sB    ,,S1##''T-?-?-C-CC-HIIr   c                 >   | j                   j                  |      }|| j                  v rJ|| j                  |   k(  r8| j                  j	                  |d        | j
                  j                  |       y || j                  |<   | j
                  j                  |       y r   )ry   r   rt   rs   rA   r}   ra   r_   r   r5   values      r   __setitem__zState.__setitem__   s    ,,S1$$$$**3//$$((d3++BB3G$)S!##88=r   c                     t        |      rt        t        |      S t        |      r| j                  j                  |      S | j                  |      S r   )r
   getattrobjectr   __dict__r   r   r8   s     r   __getattr__zState.__getattr__   sC    S>63''c?==$$S))$$r   c                 ^    t        |      r|| j                  |<   y | j                  ||       y r   )r   r   r   r   s      r   __setattr__zState.__setattr__   s'    c?!&DMM#S%(r   c                 :   | j                   j                  |      }d}g | j                  j                  dg       | j                  j                  dg       }|D ]  }||vs|j                  |       |dz  } |r || j                  d<   | j                          yy)at  
        Tag a given set of variable name(s) to be client only.
        This means that when they get changed on the client side,
        the server will not be aware of their change and no network
        bandwidth will be used to maintain the server in sync with
        the client state.

        :param *_args: A list a variable name
        :type *_args: str
        r   trame__client_onlyr   N)ry   translate_listrs   r   rt   rc   r   )r   _argschange_detected	full_listnames        r   client_onlyzState.client_only   s       //6
!!%%&:B?
##$8"=
	 D9$  &1$ 
 9BD  !56JJL r   c                 :    | j                          | j                  S )zq
        Flush current state modification and return the resulting
        state state as a python dict.
        )r   rt   r   s    r   to_dictzState.to_dict  s    
 	

!!!r   c                     | j                   j                  |      }|| j                  v xs || j                  v }t        j                  d|||       |S )z2Check is a key is currently available in the statezhas(%s => %s) = %s)ry   r   rt   rs   loggerinfo)r   r5   _keyresults       r   hasz	State.has  sP    --c2+++Ktt7K7K/K(#tV<r   c                     | j                   j                  |      }|| j                  v r| j                  |   S | j                  j	                  ||      S )z~
        Set an initial value if the key is not present yet
        :returns the value in the state for the given key
        )ry   r   rt   rs   
setdefaultr   s      r   r   zState.setdefault#  sR    
 ,,S1$$$$%%c**##..sE::r   c                 h    | j                   j                  |      }|D ]  }|| j                  v s y y)zu
        Check if any provided key name(s) still has a pending
        changed that will need to be flushed.
        TF)ry   r   rs   )r   r   r   s      r   is_dirtyzState.is_dirty-  s:    
   //6Dt+++  r   c                     d}| j                   j                  |      }|D ]  }|| j                  v s|dz  } |t        |      k(  S )zo
        Check if all provided key name(s) has a pending
        changed that will need to be flushed.
        r   r   )ry   r   rs   len)r   r   countr   s       r   is_dirty_allzState.is_dirty_all9  sQ    
   //6Dt+++
  E
""r   c                     | j                   j                  |      }|D ]R  }| j                  j                  || j                  j                  |             | j                  j                  |       T y)z
        Mark existing variable name(s) to be modified in a way that
        they will be pushed again at flush time.
        Note that the variable(s) will be unmarked automatically when reset
        to its previous value.
        N)ry   r   rs   r   rt   r   r}   r_   r   r   r5   s      r   dirtyzState.dirtyF  s_       //6C  ++C1C1C1G1G1LM''<<SA r   c                     | j                   j                          | j                  j                  |      }|D ]T  }|| j                  v s| j                  j                  |      | j                  |<   | j                   j                  |       V y)z
        Save pending variable(s) and unmark them as dirty.
        This will prevent change listener(s) to react or the client
        to be aware of any change.
        N)r}   r?   ry   r   rs   rA   rt   ra   r   s      r   cleanzState.cleanR  sy     	##))+  //6Cd****.*>*>*B*B3*G""3'++BB3G r   c                 p   | j                   j                  |      }| j                  j                  |       |D ]{  }||   | j                  j                  |t              k(  r8| j                  j                  |d       | j                  j                  |       a| j                  j                  |       } y)z3Update the current state dict with the provided oneN)ry   translate_dictrs   rG   rt   r   TRAME_NON_INIT_VALUErA   r}   ra   r_   )r   _dictr5   s      r   rG   zState.update_  s      //6##E*CSzT//33C9MNN$$((d3++BB3G++@@E r   c                     | j                   S )a  
        Return the set of state's keys that are modified
        for the current state.change update.

        Usage example:
        --------------

        >>> NAMES = ["a", "b", "c"]
        >>> state.update({"a": 1, "b": 2, "c": 3})

        >>> @state.change(*NAMES)
        ... def on_change(*_):
        ...     for name in state.modified_keys:
        ...         print(f"{name} value updated to {state[name]}")

        >>> with state:
        ...     state.a += 1

        >>> with state:
        ...     state.a += 1
        ...     state.b += 2

        >>>  with state:
        ...    state.a += 1
        ...    state.b += 2
        ...    state.c += 3

        )rq   r   s    r   modified_keyszState.modified_keysj  s    > """r   c                    t        | j                  j                               }| j                  j	                          | xj                  |z  c_        | j
                  r| j                  | j                         | j                  j                  | j                         | j                  j	                          | j                  j                  | j                  j                                | j                  j	                          | j                  D ]  \  }}t        |t        j                        r
 |       }|*|}| j                  r t!        j"                  |      st%        |      }|j'                  | j                        } |di |}t!        j(                  |      st+        j,                  |        | j                  j	                          |S )Nr*   )rz   rs   rR   rq   r?   rw   rt   rG   ru   rS   r}   rj   
isinstanceweakref
WeakMethodrx   inspectiscoroutinefunctionr   reverse_translate_dictisawaitabler	   create_task)r   _keysfnr~   rP   reverse_translated_state	coroutines          r   _flush_pending_keyszState._flush_pending_keys  s   D((--/0 	!!#u$  4 45!!$"6"67""$ 	%%''@@B	

 	##))+"33NB
"g0014#228<%h/H'1'H'H""($ !<#;<I""9-((3# 4& 	##%r   c                 $   | j                   j                  ryt               }| j                   j                         5  t	        | j
                        r)|| j                         z  }t	        | j
                        r)ddd       |S # 1 sw Y   |S xY w)a  
        Force pushing modified state and execute any @state.change listener
        if the variable value is different (by value AND reference) from its
        previous value or if `dirty` has been flagged on the variable and it has
        not been unflagged since.
        N)rv   r!   rz   r#   r(   rs   r   re   s     r   r   zState.flush  su     <<%%u\\**,t++,0022 t++, - 	 - s   >BBc                     | j                   j                  | j                         | j                  j                          | j                   S )z3Return the initial state without triggering a flush)rt   rG   rs   r?   r   s    r   initialzState.initial  s<     	!!$"6"67""$!!!r   c                     | S r   r*   r   s    r   	__enter__zState.__enter__  s    r   c                 $    | j                          y r   )r   )r   exc_type	exc_valueexc_tracebacks       r   __exit__zState.__exit__  s    

r   c                       fd}|S )a  
        Use as decorator `@server.change(key1, key2, ...)` so the decorated function
        will be called like so `_fn(**state)` when any of the listed key name
        is getting modified from either client or server.

        Can also be used as a function to decorate method functions (see
        2nd example below)

        :param *_args: A list of variable name to monitor
        :type *_args: str
        :examples:
        >>> @state.change("a", "b")  # for functions
        ... def on_change(a, b, **kwargs):
        ...     pass

        >>> state.change("a")(self.on_a_change)  # for methods
        :see-also TrameApp
        c                     D ]d  }j                   j                  |      }|j                  vrg j                  |<   j                  |   j                  | j                   f       f | S r   )ry   r   rr   rc   )funcnr   r   r   s      r   register_change_callbackz.State.change.<locals>.register_change_callback  sl    ''55a8t55535D**40&&t,33T4;K;K4LM  Kr   r*   )r   r   _kwargsr   s   ``  r   changezState.change  s    (	 ('r   rR   c              '     K    | j                   j                  |D cg c]  }| j                  j                  |       c}  	 d | j                   j	                          yc c}w # | j                   j	                          w xY ww)a  
        Suppresses input keys from triggering a state.change callback event in the scope of the context manager.
        Suppression only impacts the server and any state changed will be properly sent to the client.

        If called without arguments, suppress_change_listeners will suppress all listener changes in its scope.
        When used in a child state, the input key should be the untranslated state key.
        N)r}   rf   r~   r   rA   )r   rR   ks      r   suppress_change_listenerszState.suppress_change_listeners  ss      	)##((8<=1doo++A.=	
	.''++- >
 ''++-s'   B"A#BA(  B(BB)NNNFFrH   )#r$   r%   r&   r'   r   r)   r(   r   r   r~   r   r   r   r   r   r   r   r   r   r   r   r   r   rG   r   rz   rn   r   r   r   r   r   r   r   r   r*   r   r   r   r      s   
 F4 "$ " "  J    J	>%)4";
#
BH	F # #@)SX )V" " "(> .s . .r   )r   loggingr   collectionsr   
contextlibr   typingr   r   r   utilsr	   r
   r   r   utils.hot_reloadr   utils.namespacer   	getLoggerr$   r   __all__r   r   r,   rJ   rX   r   r*   r   r   <module>r      s        % * * = = $ '			8	$  D " "4 D* **90 90xe. e.r   