3
da                 @   s|  d Z 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Zddlm	Z	 ddl
Z
ddlZddlmZmZ ddlmZ dd	lmZ d*adad
d Zdd ZG dd deZG dd deZe ZejZejZejZ da!da"d+ddZ#dd Z$dd Z%ej&dd Z'G dd deZ(G dd de(Z)G dd de(Z*G d d! d!eZ+G d"d# d#e+Z,G d$d% d%e+Z-G d&d' d'e+Z.d(d) Z/dS ),zM Classes that implement and support the Traits change notification mechanism
    N)local)Thread)
MethodType   )ComparisonMode	TraitKind)Uninitialized)TraitNotificationErrorc             C   s   | a tj jadS )z0 Sets up the user interface thread handler.
    N)
ui_handler	threadingcurrent_threadident	ui_thread)handler r   8/tmp/pip-build-7vycvbft/traits/traits/trait_notifiers.pyset_ui_handler%   s    r   c             O   s.   t j jtkr| || nt| f|| d S )N)r   r   r   r   r
   )r   argskwr   r   r   ui_dispatch.   s    r   c               @   s   e Zd Zdd ZdS )!NotificationExceptionHandlerStatec             C   s   || _ || _|| _d S )N)r   reraise_exceptionslocked)selfr   r   r   r   r   r   __init__6   s    z*NotificationExceptionHandlerState.__init__N)__name__
__module____qualname__r   r   r   r   r   r   5   s   r   c               @   sF   e Zd Zdd ZdddZdd Zd	d
 Zdd Zdd Zdd Z	dS )NotificationExceptionHandlerc             C   s   d | _ d | _t | _d S )N)traits_loggermain_threadthread_local)r   r   r   r   r   =   s    z%NotificationExceptionHandler.__init__NFc             C   sD   | j  }| j| |dkr | j}|jt||| |r<|| _|d S )a~   Pushes a new traits notification exception handler onto the stack,
            making it the new exception handler. Returns a
            NotificationExceptionHandlerState object describing the previous
            exception handler.

            Parameters
            ----------
            handler : handler
                The new exception handler, which should be a callable or
                None. If None (the default), then the default traits
                notification exception handler is used. If *handler* is not
                None, then it must be a callable which can accept four
                arguments: object, trait_name, old_value, new_value.
            reraise_exceptions : bool
                Indicates whether exceptions should be reraised after the
                exception handler has executed. If True, exceptions will be
                re-raised after the specified handler has been executed.
                The default value is False.
            main : bool
                Indicates whether the caller represents the main application
                thread. If True, then the caller's exception handler is
                made the default handler for any other threads that are
                created. Note that a thread can explicitly set its own
                exception handler if desired. The *main* flag is provided to
                make it easier to set a global application policy without
                having to explicitly set it for each thread. The default
                value is False.
            locked : bool
                Indicates whether further changes to the Traits notification
                exception handler state should be allowed. If True, then
                any subsequent calls to _push_handler() or _pop_handler() for
                that thread will raise a TraitNotificationError. The default
                value is False.
        N   )_get_handlers_check_lock_log_exceptionappendr   r    )r   r   r   mainr   handlersr   r   r   _push_handlerD   s    %
z*NotificationExceptionHandler._push_handlerc             C   s4   | j  }| j| t|dkr(|j  ntddS )a   Pops the traits notification exception handler stack, restoring
            the exception handler in effect prior to the most recent
            _push_handler() call. If the stack is empty or locked, a
            TraitNotificationError exception is raised.

            Note that each thread has its own independent stack. See the
            description of the _push_handler() method for more information on
            this.
        r   zFAttempted to pop an empty traits notification exception handler stack.N)r$   r%   lenpopr	   )r   r)   r   r   r   _pop_handlerw   s    


z)NotificationExceptionHandler._pop_handlerc             C   sH   t j dd \}}| j d }|j|||| |js@t|trD|dS )z Handles a traits notification exception using the handler defined
            by the topmost stack entry for the corresponding thread.
        Nr"   r   )sysexc_infor$   r   r   
isinstancer	   )r   object
trait_nameoldnew
excp_classexcpZhandler_infor   r   r   _handle_exception   s    z.NotificationExceptionHandler._handle_exceptionc             C   s   | j }t|tr&tj j}|j|}nt|dd}|dkr~| jdk	rP| jd }nt	| j
dd}|g}t|trx|||< n||_|S )z_ Returns the handler stack associated with the currently executing
            thread.
        r)   Nr   Fr.   )r!   r1   dictr   r   r   getgetattrr    r   r&   r)   )r   r!   idr)   r   r   r   r   r$      s    




z*NotificationExceptionHandler._get_handlersc             C   s   |d j rtddS )zG Raises an exception if the specified handler stack is locked.
        r   zLThe traits notification exception handler is locked. No changes are allowed.Nr.   )r   r	   )r   r)   r   r   r   r%      s    
z(NotificationExceptionHandler._check_lockc             C   s   t j dd \}}|tkrbt|jdkrb|jd dkrbt jjd||||djtj	t j  f  | j
}|dkrtjd | _
}y|jd||||f  W n tk
r   Y nX dS )	a   Logs any exceptions generated in a trait notification handler.

        This method defines the default notification exception handling
        behavior of traits. However, it can be completely overridden by pushing
        a new handler using the '_push_handler' method.
        Nr"   r   z maximum recursion depth exceededznException occurred in traits notification handler for object: %s, trait: %s, old value: %s, new value: %s.
%s
 ZtraitsziException occurred in traits notification handler for object: %s, trait: %s, old value: %s, new value: %s)r/   r0   RuntimeErrorr+   r   
__stderr__writejoin	tracebackformat_exceptionr   logging	getLogger	exception	Exception)r   r2   r3   r4   r5   r6   r7   loggerr   r   r   r&      s(    	z+NotificationExceptionHandler._log_exception)NFFF)
r   r   r   r   r*   r-   r8   r$   r%   r&   r   r   r   r   r   <   s   
2	r   c             C   s   | a |adS )a   Set the global trait change event tracers.

    The global tracers are called whenever a trait change event is dispatched.
    There are two tracers: `pre_tracer` is called before the notification is
    sent; `post_tracer` is called after the notification is sent, even if the
    notification failed with an exception (in which case the `post_tracer` is
    called with a reference to the exception, then the exception is sent to
    the `notification_exception_handler`).

    The tracers should be a callable taking 5 arguments:
    ::
      tracer(obj, trait_name, old, new, handler)

    `obj` is the source object, on which trait `trait_name` was changed from
    value `old` to value `new`. `handler` is the function or method that will
    be notified of the change.

    The post-notification tracer also has a keyword argument, `exception`,
    that is `None` if no exception has been raised, and the a reference to the
    raise exception otherwise.
    ::
      post_tracer(obj, trait_name, old, new, handler, exception=None)

    Note that for static trait change listeners, `handler` is not a method, but
    rather the function before class creation, since this is the way Traits
    works at the moment.
    N)_pre_change_event_tracer_post_change_event_tracer)
pre_tracerpost_tracerr   r   r   set_change_event_tracers   s    rM   c               C   s   t tfS )z= Get the currently active global trait change event tracers. )rI   rJ   r   r   r   r   get_change_event_tracers  s    rN   c               C   s   da dadS )z- Clear the global trait change event tracer. N)rI   rJ   r   r   r   r   clear_change_event_tracers  s    rO   c             c   s0   t  \}}t| | z
dV  W dt|| X dS )zA Context manager to temporarily change the global event tracers. N)rN   rM   )rK   rL   Zold_pre_tracerZold_post_tracerr   r   r   change_event_tracers"  s
    


rP   c               @   s,   e Zd ZdZi Zdd Zdd Zdd ZdS )	!AbstractStaticChangeNotifyWrappera   
    Concrete implementation must define the 'argument_transforms' class
    argument, a dictionary mapping the number of arguments in the event
    handler to a function that takes the arguments (obj, trait_name, old, new)
    and returns the arguments tuple for the actual handler.
    c             C   s8   |j j}|dkr"td|j|f | j| | _|| _d S )N   zInvalid number of arguments for the static anytrait change notification handler: %s. A maximum of 4 arguments is allowed, but %s were specified.)__code__co_argcountr	   r   argument_transformsargument_transformr   )r   r   	arg_countr   r   r   r   7  s    z*AbstractStaticChangeNotifyWrapper.__init__c             C   s   t ||||r| j||||}tdk	r8t||||| j y| j|  W nN tk
r } z2tdk	rvt||||| j|d t|||| W Y dd}~X n X tdk	rt||||| jdd dS )z- Dispatch to the appropriate handler method. N)rF   )_change_acceptedrV   rI   r   rG   rJ   handle_exception)r   r2   r3   r4   r5   r   er   r   r   __call__F  s(     z*AbstractStaticChangeNotifyWrapper.__call__c             C   s   dS )NFr   )r   r   r   r   r   equalsh  s    z(AbstractStaticChangeNotifyWrapper.equalsN)r   r   r   __doc__Zarguments_transformsr   r[   r\   r   r   r   r   rQ   -  s
   "rQ   c               @   s0   e Zd Zdd dd dd dd dd dZdS )	!StaticAnytraitChangeNotifyWrapperc             C   s   f S )Nr   )objnamer4   r5   r   r   r   <lambda>r  s    z*StaticAnytraitChangeNotifyWrapper.<lambda>c             C   s   | fS )Nr   )r_   r`   r4   r5   r   r   r   ra   s  s    c             C   s   | |fS )Nr   )r_   r`   r4   r5   r   r   r   ra   t  s    c             C   s
   | ||fS )Nr   )r_   r`   r4   r5   r   r   r   ra   u  s    c             C   s   | |||fS )Nr   )r_   r`   r4   r5   r   r   r   ra   v  s    )r   r   r"      rR   N)r   r   r   rU   r   r   r   r   r^   l  s
   r^   c               @   s0   e Zd Zdd dd dd dd dd dZdS )	StaticTraitChangeNotifyWrapperc             C   s   f S )Nr   )r_   r`   r4   r5   r   r   r   ra     s    z'StaticTraitChangeNotifyWrapper.<lambda>c             C   s   | fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s   | |fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s
   | ||fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s   | |||fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    )r   r   r"   rb   rR   N)r   r   r   rU   r   r   r   r   rc   z  s
   rc   c               @   s   e Zd ZdZdd dd dd dd dd dZdd
dZdddZdd Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zd	S ) TraitChangeNotifyWrapperz Dynamic change notify wrapper.

    This class is in charge to dispatch trait change events to dynamic
    listener, typically created using the `on_trait_change` method, or
    the decorator with the same name.
    c             C   s   f S )Nr   )r_   r`   r4   r5   r   r   r   ra     s    z!TraitChangeNotifyWrapper.<lambda>c             C   s   |fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s   ||fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s
   | ||fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    c             C   s   | |||fS )Nr   )r_   r`   r4   r5   r   r   r   ra     s    )r   r   r"   rb   rR   Nc             C   s   | j ||| d S )N)init)r   r   ownertargetr   r   r   r     s    z!TraitChangeNotifyWrapper.__init__c             C   s   t |tkr|j}|j}|d k	rtj|| j| _|j| _	|| _
|jjd }|dkrdtd|j|f t | j| _| j| | _|S n|d k	rtj|| j| _|| _
|jj}|dkrtd|j|f d | _	|| _t | j| _| j| | _|S )Nr   rR   zInvalid number of arguments for the dynamic trait change notification handler: %s. A maximum of 4 arguments is allowed, but %s were specified.)typer   __func____self__weakrefreflistener_deletedr2   r   r`   rf   rS   rT   r	   _notify_method_listenernotify_listenerrU   rV   r   _notify_function_listener)r   r   rf   rg   funcr2   rW   r   r   r   re     s8    zTraitChangeNotifyWrapper.initc             C   s   | j | |||| dS )z Dispatch to the appropriate method.

        We do explicit dispatch instead of assigning to the .__call__ instance
        attribute to avoid reference cycles.
        N)ro   )r   r2   r3   r4   r5   r   r   r   r[     s    
z!TraitChangeNotifyWrapper.__call__c             G   s   ||  dS )z Dispatch the event to the listener.

        This method is normally the only one that needs to be overridden in
        a subclass to implement the subclass's dispatch mechanism.
        Nr   )r   r   r   r   r   r   dispatch  s    z!TraitChangeNotifyWrapper.dispatchc             C   sP   || krdS t |tkr<|jd k	r<|j| jko:|j| j kS | jd koN|| jkS )NT)rh   r   rj   r   r`   r2   r   )r   r   r   r   r   r\     s    zTraitChangeNotifyWrapper.equalsc             C   s6   y| j j|  W n tk
r$   Y nX d  | _| _ d S )N)rf   remove
ValueErrorr2   )r   rl   r   r   r   rm     s
    z)TraitChangeNotifyWrapper.listener_deletedc             C   s
   d | _ d S )N)r2   )r   r   r   r   dispose  s    z TraitChangeNotifyWrapper.disposec             C   s   | j ||||}tdk	r(t||||| y| j|f|  W nL tk
r } z0tdk	rjt||||||d t|||| W Y dd}~X nX tdk	rt|||||dd dS )z: Prepare and dispatch a trait change event to a listener. N)rF   )rV   rI   rr   rG   rJ   rY   )r   r2   r3   r4   r5   r   r   rZ   r   r   r   _dispatch_change_event   s     z/TraitChangeNotifyWrapper._dispatch_change_eventc             C   sL   | j }|dk	rHt||||rH| }|dk	rHt|| j}| j||||| dS )z5 Dispatch a trait change event to a method listener. N)r2   rX   r;   r`   rv   )r   r2   r3   r4   r5   obj_weak_refr_   listenerr   r   r   rn     s    z0TraitChangeNotifyWrapper._notify_method_listenerc             C   s&   t ||||r"| j||||| j dS )z7 Dispatch a trait change event to a function listener. N)rX   rv   r   )r   r2   r3   r4   r5   r   r   r   rp   ,  s    z2TraitChangeNotifyWrapper._notify_function_listener)N)N)r   r   r   r]   rU   r   re   r[   rr   r\   rm   ru   rv   rn   rp   r   r   r   r   rd     s    

6
rd   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	 ExtendedTraitChangeNotifyWrappera]   Change notify wrapper for "extended" trait change events..

    The "extended notifiers" are set up internally when using extended traits,
    to add/remove traits listeners when one of the intermediate traits changes.

    For example, in a listener for the extended trait `a.b`, we need to
    add/remove listeners to `a:b` when `a` changes.
    c             C   sL   | j ||||}y| j|f|  W n" tk
rF   t|||| Y nX dS )z: Prepare and dispatch a trait change event to a listener. N)rV   rr   rG   rY   )r   r2   r3   r4   r5   r   r   r   r   r   rv   ?  s
    z7ExtendedTraitChangeNotifyWrapper._dispatch_change_eventc             C   s>   | j }|dk	r:| }|dk	r:t|| j}| j||||| dS )z5 Dispatch a trait change event to a method listener. N)r2   r;   r`   rv   )r   r2   r3   r4   r5   rw   r_   rx   r   r   r   rn   K  s    z8ExtendedTraitChangeNotifyWrapper._notify_method_listenerc             C   s   | j ||||| j dS )z7 Dispatch a trait change event to a function listener. N)rv   r   )r   r2   r3   r4   r5   r   r   r   rp   [  s    z:ExtendedTraitChangeNotifyWrapper._notify_function_listenerN)r   r   r   r]   rv   rn   rp   r   r   r   r   ry   5  s   ry   c               @   s   e Zd ZdZdd ZdS )FastUITraitChangeNotifyWrappera   Dynamic change notify wrapper, dispatching on the UI thread.

    This class is in charge to dispatch trait change events to dynamic
    listener, typically created using the `on_trait_change` method and the
    `dispatch` parameter set to 'ui' or 'fast_ui'.
    c             G   s*   t j jtkr||  nt|f|  d S )N)r   r   r   r   r
   )r   r   r   r   r   r   rr   i  s    
z'FastUITraitChangeNotifyWrapper.dispatchN)r   r   r   r]   rr   r   r   r   r   rz   a  s   rz   c               @   s   e Zd ZdZdd ZdS )NewTraitChangeNotifyWrapperz Dynamic change notify wrapper, dispatching on a new thread.

    This class is in charge to dispatch trait change events to dynamic
    listener, typically created using the `on_trait_change` method and the
    `dispatch` parameter set to 'new'.
    c             G   s   t ||dj  d S )N)rg   r   )r   start)r   r   r   r   r   r   rr   x  s    z$NewTraitChangeNotifyWrapper.dispatchN)r   r   r   r]   rr   r   r   r   r   r{   p  s   r{   c             C   sX   |t krdS | j|d}|jtjjkrT|jtjkrTyt	||kS  t
k
rR   Y nX dS )a   Return true if notifications should be emitted for the change.

    Parameters
    ----------
    object : HasTraits
        The object on which the trait is changed.
    name : str
        The name of the trait changed.
    old : any
        The old value
    new : any
        The new value

    Returns
    -------
    accepted : bool
        Whether the event should be emitted.
    Fr"   T)r   Z_traitrh   r   traitr`   Zcomparison_moder   ZequalityboolrG   )r2   r`   r4   r5   r}   r   r   r   rX   |  s    rX   r.   )NN)0r]   
contextlibrD   r   r   r!   r   rB   typesr   rk   r/   	constantsr   r   Z
trait_baser   Ztrait_errorsr	   r   r
   r   r   r2   r   r   Znotification_exception_handlerr*   Zpush_exception_handlerr-   Zpop_exception_handlerr8   rY   rI   rJ   rM   rN   rO   contextmanagerrP   rQ   r^   rc   rd   ry   rz   r{   rX   r   r   r   r   <module>   sL   	 ,
"? .,