
    
dEH                     (   d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
mZ  ej        e          ZdZdZ G d de          Z G d	 d
          Zej        Zd Zee_         G d de          Z G d de          Z G d d          ZdS )a  
Class that handles communications between Spyder kernel and frontend.

Comms transmit data in a list of buffers, and in a json-able dictionnary.
Here, we only support a buffer list with a single element.

The messages exchanged have the following msg_dict:

    ```
    msg_dict = {
        'spyder_msg_type': spyder_msg_type,
        'content': content,
    }
    ```

The buffer is generated by cloudpickle using `PICKLE_PROTOCOL = 2`.

To simplify the usage of messaging, we use a higher level function calling
mechanism:
    - The `remote_call` method returns a RemoteCallHandler object
    - By calling an attribute of this object, the call is sent to the other
      side of the comm.
    - If the `_wait_reply` is implemented, remote_call can be called with
      `blocking=True`, which will wait for a reply sent by the other side.

The messages exchanged are:
    - Function call (spyder_msg_type = 'remote_call'):
        - The content is a dictionnary {
            'call_name': The name of the function to be called,
            'call_id': uuid to match the request to a potential reply,
            'settings': A dictionnary of settings,
            }
        - The buffer encodes a dictionnary {
            'call_args': The function args,
            'call_kwargs': The function kwargs,
            }
    - If the 'settings' has `'blocking' =  True`, a reply is sent.
      (spyder_msg_type = 'remote_call_reply'):
        - The buffer contains the return value of the function.
        - The 'content' is a dict with: {
                'is_error': a boolean indicating if the return value is an
                            exception to be raised.
                'call_id': The uuid from above,
                'call_name': The function name (mostly for debugging)
                }
    )print_functionN)PY2PY3      c                       e Zd ZdS )	CommErrorN)__name__
__module____qualname__     =lib/python3.11/site-packages/spyder_kernels/comms/commbase.pyr	   r	   J   s        Dr   r	   c                   4    e Zd Zd Zd Zd ZddZd Zd ZdS )	CommsErrorWrapperc                     || _         || _        t          j                    \  | _        | _        }t          j        |          | _        d S N)		call_namecall_idsysexc_infoetypeerror	traceback
extract_tbtb)selfr   r   r   s       r   __init__zCommsErrorWrapper.__init__O   s<    "%(\^^"
DJ&r**r   c                 ,    |                      |           )zL
        Raise the error while adding informations on the callback.
        )r   r   s    r   raise_errorzCommsErrorWrapper.raise_errorU   s    
 jjr   c                     d                     | j                  gt          j        | j                  z   t          j        | j        | j                  z   }|S )zf
        Format the error received from the other side and returns a list of
        strings.
        zException in comms call {}:
)formatr   r   format_listr   format_exception_onlyr   r   )r   liness     r   format_errorzCommsErrorWrapper.format_error\   sR    
 288HHI(11224:tzJJK r   Nc                 r    |t           j        }|                                 D ]}t          ||           dS )zK
        Print the error to file or to sys.stderr if file is None.
        N)file)r   stderrr'   print)r   r)   lines      r   print_errorzCommsErrorWrapper.print_errorf   sK     <:D%%'' 	# 	#D$T"""""	# 	#r   c                 *    t          | j                  S )zGet string representation.)strr   r    s    r   __str__zCommsErrorWrapper.__str__o   s    4:r   c                 *    t          | j                  S )z	Get repr.)reprr   r    s    r   __repr__zCommsErrorWrapper.__repr__s   s    DJr   r   )	r
   r   r   r   r!   r'   r-   r0   r3   r   r   r   r   r   N   ss        + + +    # # # #           r   r   c                    t          |j                  dk    rUt          |j        d         t                    r5t	          j        |           |j        d                                          d S t          | ||           d S )N   r   )lenargs
isinstancer   r   print_tbr-   sys_excepthook)typevaluer   s      r   comm_excepthookr=   |   sq    
5:!
5:a=:K L L2
1!!###4#####r   c                        e Zd ZdZ fdZddZddZddZddZd Z	dd	Z
	 	 dd
Zd Zed             Zd Zd Zd Zd Zd Zd Zd dZddZd Zd Zd Zd Zd Zd Zd Z xZS )!CommBasez
    Class with the necessary attributes and methods to handle
    communications between a kernel and a frontend.
    Subclasses must open a comm and register it with `self._register_comm`.
    c                 L   t          t          |                                            d | _        i | _        i | _        i | _        i | _        i | _        | 	                    d| j
                   | 	                    d| j                   |                     d| j                   d S )Nremote_callremote_call_reply_set_pickle_protocol)superr?   r   calling_comm_id_comms_message_handlers_remote_call_handlers_reply_inbox_reply_waitlist_register_message_handler_handle_remote_call_handle_remote_call_replyregister_call_handlerrC   )r   	__class__s    r   r   zCommBase.__init__   s    h&&(((#!#%'"!&&43	5 	5 	5&&!?	A 	A 	A""#9#'#<	> 	> 	> 	> 	>r   Nc                 ^    |'t          | j                                                  }n|g}|S )zGet a list of comms id.)listrF   keysr   comm_idid_lists      r   get_comm_id_listzCommBase.get_comm_id_list   s0    ?4;++--..GGiGr   c                     |                      |          }|D ]@}	 | j        |         d                                          | j        |= 1# t          $ r Y =w xY wdS )z)Close the comm and notify the other side.commN)rV   rF   closeKeyErrorrS   s      r   rY   zCommBase.close   s~    ''00 	 	GG$V,22444K((   		 	s   -A		
AAc                 H    |t          | j                  dk    S || j        v S )z!Check to see if the comm is open.Nr   )r6   rF   )r   rT   s     r   is_openzCommBase.is_open   s)    ?t{##a''$+%%r   c                                            |          }t          |          dk    rdS t           fd|D                       S )z
        Check to see if the other side replied.

        The check is made with _set_pickle_protocol as this is the first call
        made. If comm_id is not specified, check all comms.
        r   Fc                 >    g | ]}j         |         d          dk    S )statusreadyrF   ).0cidr   s     r   
<listcomp>z%CommBase.is_ready.<locals>.<listcomp>   s*    MMMcDK$X.'9MMMr   )rV   r6   allrS   s   `  r   is_readyzCommBase.is_ready   sQ     ''00w<<15MMMMWMMMNNNr   c                 X    |s| j                             |d           dS || j         |<   dS )a  
        Register a remote call handler.

        Parameters
        ----------
        call_name : str
            The name of the called function.
        handler : callback
            A function to handle the request, or `None` to unregister
            `call_name`.
        N)rH   pop)r   r   handlers      r   rN   zCommBase.register_call_handler   s=      	&**9d;;;F07"9---r   c                      t          | ||fi |S )zGet a handler for remote calls.)RemoteCallFactory)r   rT   callbacksettingss       r   rA   zCommBase.remote_call   s     wEEHEEEr   c                 f   |                      |          st          d          |                     |          }|D ]t}||| j        |         d         t          j        d}t          j        || j        |         d                   g}| j        |         d                             ||           udS )a  
        Publish custom messages to the other side.

        Parameters
        ----------
        spyder_msg_type: str
            The spyder message type
        content: dict
            The (JSONable) content of the message
        data: any
            Any object that is serializable by cloudpickle (should be most
            things). Will arrive as cloudpickled bytes in `.buffers[0]`.
        comm_id: int
            the comm to send to. If None sends to all comms.
        The comm is not connected.pickle_protocol)spyder_msg_typecontentrp   python_version)protocolrX   )buffersN)	r\   r	   rV   rF   r   versioncloudpickledumpssend)r   rq   rr   datarT   rU   msg_dictru   s           r   _send_messagezCommBase._send_message   s    " ||G$$ 	:8999''00 		I 		IG#2"#';w#78I#J"%+	 H #(t{734EFH H H IGK (--h-HHHH		I 		Ir   c                     t          |t          j                  }|| j        | j                 d<   d| j        | j                 d<   dS )z*Set the pickle protocol used to send data.rp   r`   r_   N)minpickleHIGHEST_PROTOCOLrF   rE   )r   rt   s     r   rC   zCommBase._set_pickle_protocol   sC    x!899?GD()*;<6=D()(333r   c                     dS )z=
        Get the name used for the underlying comms.
        
spyder_apir   r    s    r   
_comm_namezCommBase._comm_name  s	    
 |r   c                 X    || j                             |d           dS || j         |<   dS )a  
        Register a message handler.

        Parameters
        ----------
        message_id : str
            The identifier for the message
        handler : callback
            A function to handle the message. This is called with 3 arguments:
                - msg_dict: A dictionary with message information.
                - buffer: The data transmitted in the buffer
            Pass None to unregister the message_id
        N)rG   rh   )r   
message_idri   s      r   rK   z"CommBase._register_message_handler  s:     ?"&&z4888F-4z***r   c                     |                     | j                   |                    | j                   |t          dd| j        |j        <   dS )z0
        Open a new comm to the kernel.
        opening)rX   rp   r_   N)on_msg_comm_messageon_close_comm_closeDEFAULT_PICKLE_PROTOCOLrF   rT   )r   rX   s     r   _register_commzCommBase._register_comm  sR     	D&'''d&'''6% %DL!!!r   c                 2    |d         d         }| j         |= dS )zClose comm.rr   rT   Nra   )r   msgrT   s      r   r   zCommBase._comm_close(  s     i.+K   r   c                 V   |d         d         | _         |d         d         }	 t          r#t          j        |d         d         d          }n t          j        |d         d                   }nt# t          $ rg}t
                              dt          |          z             t          |d         d	         |d         d
                   }d|d         d<   Y d}~nd}~ww xY w|d         }|| j	        v r | j	        |         ||           dS t
                              d|z             dS )z2
        Handle internal spyder messages.
        rr   rT   rz   ru   r   zlatin-1)encodingz#Exception in cloudpickle.loads : %sr   r   Tis_errorNrq   zNo such spyder message type: %s)
rE   r   rw   loads	Exceptionloggerdebugr/   r   rG   )r   r   r{   buffererq   s         r   r   zCommBase._comm_message-  sl     #9~i8 y>&)	3 >
 %*3y>!+<4=? ? ? %*3y>!+<== 	3 	3 	3LL5A>@ @ @&#K0#I.0 0F /3HY
++++++	3 ##45d4443D"?3&" " " " " LL:_LMMMMMs   A
A. .
C8ACCc                 J   |d         }|                      |           	 |                     |d         |d         |d                   }|                     ||           dS # t          $ r8 t	          |d         |d                   }|                     ||d           Y dS w xY w)	zHandle a remote call.rr   r   	call_argscall_kwargsr   T)r   N)on_incoming_call_remote_callback_set_call_return_valuer   r   )r   r   r   r{   return_value	exc_infoss         r   rL   zCommBase._handle_remote_callR  s    y>h'''		L00[);'=)+ +L '',????? 	L 	L 	L)%x	':< <I'')d'KKKKKK	Ls   ?A   >B"!B"c                 ^    || j         v r | j         |         |i |S t          d|z            )z/Call the callback function for the remote call.zNo such spyder call type: %s)rH   r	   )r   r   r   r   s       r   r   zCommBase._remote_callbacka  sM    22284-i8+)+ + + 6BCCCr   Fc                     |d         }d|v o|d         }|r|r|                                  d|v o|d         }|sdS ||d         |d         d}|                     d||| j        	           dS )
zq
        A remote call has just been processed.

        This will reply if settings['blocking'] == True
        rm   display_error
send_replyNr   r   )r   r   r   rB   rr   rz   rT   )r-   r|   rE   )r   	call_dictrz   r   rm   r   r   rr   s           r   r   zCommBase._set_call_return_valuei  s     Z((H4 3!/2 	 	 	!X-H(<2H
 	F  +";/
 
 	.d#'#7 	 	9 	9 	9 	9 	9r   c                 b    |d         }d|v o|d         }|d         }|s|||f| j         |<   dS dS )zI
        Register the call so the reply can be properly treated.
        rm   blockingr   N)rJ   )r   r   rl   rm   r   r   s         r   _register_callzCommBase._register_call  sY     Z()Bhz.BI& 	?x+,4h,>D ))) ,+r   c                 $    t           j        |d<   |S )zA message is about to be sentpickle_highest_protocol)r   r   r   r   s     r   on_outgoing_callzCommBase.on_outgoing_call  s    /5/F	+,r   c                 H    d|v r|                      |d                    dS dS )zA call was receivedr   N)rC   r   s     r   r   zCommBase.on_incoming_call  s5    $	11%%i0I&JKKKKK 21r   c                    |                      |          }|                     d|||           |d         }d|v o|d         }|sdS |d         }|d         }d|v r|d         	|d         }nt          }|                     |||           | j                            |          }	|	d	         r|                     |	d
                   S |	d
         S )z
        Send a remote call and return the reply.

        If settings['blocking'] == True, this will wait for a reply and return
        the replied value.
        rA   r   rm   r   Nr   r   timeoutr   r<   )r   r|   TIMEOUT_wait_replyrI   rh   _sync_error)
r   r   	call_datarT   rm   r   r   r   r   replys
             r   _get_call_return_valuezCommBase._get_call_return_value  s    )))44	99 	 	 	 	 Z()Bhz.B 	FI&k*	   Xi%8%Dy)GGG)W555!%%g.. 	4##E'N333W~r   c                     t           )z0
        Wait for the other side reply.
        NotImplementedError)r   r   r   r   s       r   r   zCommBase._wait_reply  s
     "!r   c                    |d         }|d         }|d         }|d         }|| j         vrG|r|                     |          S t                              d                    ||                     dS | j                             |          \  }}|r|s|                     |          S ||s ||           |r|||d| j        |<   dS dS )z3
        A blocking call received a reply.
        rr   r   r   r   z!Got an unexpected reply {}, id:{}N)r   r<   rr   )rJ   _async_errorr   r   r#   rh   rI   )	r   r{   r   rr   r   r   r   r   rl   s	            r   rM   z"CommBase._handle_remote_call_reply  s    9%)$K(	:& $... )((000@GGw( ( ) ) )F!155g>>(  	-H 	-$$V,,, HV  	 (#&* *Dg&&&	 	r   c                 .    |                                  dS )zR
        Handle an error that was raised on the other side asyncronously.
        N)r-   r   error_wrappers     r   r   zCommBase._async_error       	!!#####r   c                 .    |                                  dS )zQ
        Handle an error that was raised on the other side syncronously.
        N)r!   r   s     r   r   zCommBase._sync_error  r   r   r   )NN)NNN)F)r
   r   r   __doc__r   rV   rY   r\   rf   rN   rA   r|   rC   propertyr   rK   r   r   r   rL   r   r   r   r   r   r   r   rM   r   r   __classcell__rO   s   @r   r?   r?      s        > > > > >$   	 	 	 	& & & &
O 
O 
O 
O8 8 8$F F F F
 AE"I I I I>> > >   X5 5 5(
 
 
! ! !
#N #N #NJL L LD D D9 9 9 94? ? ? ?  
L L L
# # #J" " "" " "H$ $ $$ $ $ $ $ $ $r   r?   c                   .     e Zd ZdZ fdZd Zd Z xZS )rk   zClass to create `RemoteCall`s.c                 P   t          t          |                               d|           t          t          |                               d|           t          t          |                               d|           t          t          |                               d|           d S )N_comms_wrapper_comm_id	_callback	_settings)rD   rk   __setattr__)r   comms_wrapperrT   rl   rm   rO   s        r   r   zRemoteCallFactory.__init__  s    &&22m	- 	- 	-&&22:wGGG&&22;III&&22;IIIIIr   c                 P    t          || j        | j        | j        | j                  S )z'Get a call for a function named 'name'.)
RemoteCallr   r   r   r   )r   names     r   __getattr__zRemoteCallFactory.__getattr__  s(    $ 3T].$.: : 	:r   c                     t           )z#Set an attribute to the other side.r   )r   r   r<   s      r   r   zRemoteCallFactory.__setattr__  s    !!r   )r
   r   r   r   r   r   r   r   r   s   @r   rk   rk     sa        ((J J J J J: : :
" " " " " " "r   rk   c                       e Zd ZdZd Zd ZdS )r   z:Class to call the other side of the comms like a function.c                 L    || _         || _        || _        || _        || _        d S r   )_namer   r   r   r   )r   r   r   rT   rl   rm   s         r   r   zRemoteCall.__init__  s*    
+!!r   c                    d| j         v o| j         d         }|p| j        du| j         d<   t          j                    j        }| j        || j         d}||d}| j                            | j                  s5|rt          d          t                              d| j        z             dS | j                            || j                   | j                            ||| j                  S )zw
        Transmit the call to the other side of the tunnel.

        The args and kwargs have to be picklable.
        r   Nr   )r   r   rm   )r   r   ro   zCall to unconnected comm: %s)r   r   uuiduuid4hexr   r   r\   r   r	   r   r   r   r   )r   r7   kwargsr   r   r   r   s          r   __call__zRemoteCall.__call__  s    /NDN:4N'/'M4>3M|$*,," 	 ! 	
 "**4=99 	 > <===LL7$*DEEEF**9dnEEE"99y$-1 1 	1r   N)r
   r   r   r   r   r   r   r   r   r   r     s8        DD" " "1 1 1 1 1r   r   )r   
__future__r   rw   r   loggingr   r   r   spyder_kernels.py3compatr   r   	getLoggerr
   r   r   r   RuntimeErrorr	   r   
excepthookr:   r=   objectr?   rk   r   r   r   r   <module>r      s  - -\ & % % % % %       



      - - - - - - - - 
	8	$	$   	 	 	 	 	 	 	 	'  '  '  '  '  '  '  ' V $ $ $ !i$ i$ i$ i$ i$v i$ i$ i$X" " " " " " " "*&1 &1 &1 &1 &1 &1 &1 &1 &1 &1r   