o
    ZhEH                     @   s   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eZdZdZG dd deZG d	d
 d
ZejZdd Zee_G dd deZG dd deZG d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                   @   s   e Zd ZdS )	CommErrorN)__name__
__module____qualname__ r   r   T/var/www/html/lang_env/lib/python3.10/site-packages/spyder_kernels/comms/commbase.pyr   J   s    r   c                   @   s>   e Zd Zdd Zdd Zdd Zddd	Zd
d Zdd ZdS )CommsErrorWrapperc                 C   s.   || _ || _t \| _| _}t|| _d S N)		call_namecall_idsysexc_infoetypeerror	traceback
extract_tbtb)selfr   r   r   r   r   r   __init__O   s   zCommsErrorWrapper.__init__c                 C   s
   |  | )zL
        Raise the error while adding informations on the callback.
        )r   r   r   r   r   raise_errorU   s   
zCommsErrorWrapper.raise_errorc                 C   s.   d | jgt| j t| j| j }|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   linesr   r   r   format_error\   s   
zCommsErrorWrapper.format_errorNc                 C   s,   |du rt j}|  D ]}t||d qdS )zK
        Print the error to file or to sys.stderr if file is None.
        N)file)r   stderrr    print)r   r!   liner   r   r   print_errorf   s
   zCommsErrorWrapper.print_errorc                 C   
   t | jS )zGet string representation.)strr   r   r   r   r   __str__o      
zCommsErrorWrapper.__str__c                 C   r&   )z	Get repr.)reprr   r   r   r   r   __repr__s   r)   zCommsErrorWrapper.__repr__r   )	r   r	   r
   r   r   r    r%   r(   r+   r   r   r   r   r   N   s    

	r   c                 C   sJ   t |jdkrt|jd trt| |jd   d S t| || d S )N   r   )lenargs
isinstancer   r   print_tbr%   sys_excepthook)typevaluer   r   r   r   comm_excepthook|   s
   
r4   c                       s   e Zd ZdZ fddZd6ddZd6ddZd6d	d
Zd6ddZdd Z	d7ddZ
		d8ddZdd Zedd Zdd Zdd Zdd Zdd Zdd  Zd!d" Zd9d$d%Zd6d&d'Zd(d) Zd*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 Zd4d5 Z  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                    s`   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)superr5   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_handlerr8   r   	__class__r   r   r      s    zCommBase.__init__Nc                 C   s$   |du rt | j }|S |g}|S )zGet a list of comms id.N)listr;   keysr   comm_idid_listr   r   r   get_comm_id_list   s
   zCommBase.get_comm_id_listc              	   C   sH   |  |}|D ]}z| j| d   | j|= W q ty!   Y qw dS )z)Close the comm and notify the other side.commN)rK   r;   closeKeyErrorrH   r   r   r   rM      s   
zCommBase.closec                 C   s    |du rt | jdkS || jv S )z!Check to see if the comm is open.Nr   )r-   r;   )r   rI   r   r   r   is_open   s   
zCommBase.is_openc                    s0     |}t|dkrdS t fd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                    s   g | ]} j | d  dkqS )statusreadyr;   ).0Zcidr   r   r   
<listcomp>   s    z%CommBase.is_ready.<locals>.<listcomp>)rK   r-   allrH   r   r   r   is_ready   s   
zCommBase.is_readyc                 C   s$   |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)r=   pop)r   r   handlerr   r   r   rC      s   zCommBase.register_call_handlerc                 K   s   t | ||fi |S )zGet a handler for remote calls.)RemoteCallFactory)r   rI   callbacksettingsr   r   r   r6      s   zCommBase.remote_callc                 C   sv   |  |s	td| |}|D ](}||| j| d tjd}tj|| j| d dg}| j| d j||d qd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contentr]   python_version)protocolrL   )buffersN)	rO   r   rK   r;   r   versioncloudpickledumpssend)r   r^   r_   datarI   rJ   msg_dictrb   r   r   r   _send_message   s   

zCommBase._send_messagec                 C   s0   t |tj}|| j| j d< d| j| j d< dS )z*Set the pickle protocol used to send data.r]   rQ   rP   N)minpickleHIGHEST_PROTOCOLr;   r:   )r   ra   r   r   r   r8      s   zCommBase._set_pickle_protocolc                 C   s   dS )z=
        Get the name used for the underlying comms.
        Z
spyder_apir   r   r   r   r   
_comm_name  s   zCommBase._comm_namec                 C   s(   |du r| 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)r<   rW   )r   Z
message_idrX   r   r   r   r@     s   z"CommBase._register_message_handlerc                 C   s0   | | j || j |tdd| j|j< dS )z0
        Open a new comm to the kernel.
        Zopening)rL   r]   rP   N)Zon_msg_comm_messageZon_close_comm_closeDEFAULT_PICKLE_PROTOCOLr;   rI   )r   rL   r   r   r   _register_comm  s   zCommBase._register_commc                 C   s   |d d }| j |= dS )zClose comm.r_   rI   NrR   )r   msgrI   r   r   r   ro   (  s   zCommBase._comm_closec              
   C   s   |d d | _ |d d }ztrtj|d d dd}n	t|d d }W n. tyT } z"tdt|  t|d d	 |d d
 }d|d d< W Y d}~nd}~ww |d }|| j	v rh| j	| || dS td|  dS )z2
        Handle internal spyder messages.
        r_   rI   rg   rb   r   zlatin-1)encodingz#Exception in cloudpickle.loads : %sr   r   Tis_errorNr^   zNo such spyder message type: %s)
r:   r   rd   loads	Exceptionloggerdebugr'   r   r<   )r   rr   rh   bufferer^   r   r   r   rn   -  s4   


	
zCommBase._comm_messagec                 C   sv   |d }|  | z| |d |d |d }| || W dS  ty:   t|d |d }| j||dd Y dS w )	zHandle a remote call.r_   r   	call_argscall_kwargsr   T)rt   N)on_incoming_call_remote_callback_set_call_return_valuerv   r   )r   rr   ry   rh   return_valueZ	exc_infosr   r   r   rA   R  s   
zCommBase._handle_remote_callc                 C   s*   || j v r| j | |i |S td| )z/Call the callback function for the remote call.zNo such spyder call type: %s)r=   r   )r   r   r{   r|   r   r   r   r~   a  s   
zCommBase._remote_callbackFc                 C   sl   |d }d|v o|d }|r|r|   d|v o|d }|s dS ||d |d d}| jd||| jd	 dS )
zq
        A remote call has just been processed.

        This will reply if settings['blocking'] == True
        r[   display_error
send_replyNr   r   )rt   r   r   r7   r_   rg   rI   )r%   ri   r:   )r   	call_dictrg   rt   r[   r   r   r_   r   r   r   r   i  s    

zCommBase._set_call_return_valuec                 C   sB   |d }d|v o|d }|d }|s|dur||f| j |< dS dS )zI
        Register the call so the reply can be properly treated.
        r[   blockingr   N)r?   )r   r   rZ   r[   r   r   r   r   r   _register_call  s   zCommBase._register_callc                 C   s   t j|d< |S )zA message is about to be sentpickle_highest_protocol)rk   rl   r   r   r   r   r   on_outgoing_call  s   
zCommBase.on_outgoing_callc                 C   s   d|v r|  |d  dS dS )zA call was receivedr   N)r8   r   r   r   r   r}     s   zCommBase.on_incoming_callc           
      C   s   |  |}| jd|||d |d }d|v o|d }|sdS |d }|d }d|v r5|d dur5|d }nt}| ||| | j|}	|	d	 rO| |	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.
        r6   r   r[   r   Nr   r   timeoutrt   r3   )r   ri   TIMEOUT_wait_replyr>   rW   _sync_error)
r   r   	call_datarI   r[   r   r   r   r   Zreplyr   r   r   _get_call_return_value  s&   

zCommBase._get_call_return_valuec                 C      t )z0
        Wait for the other side reply.
        NotImplementedError)r   r   r   r   r   r   r   r     s   zCommBase._wait_replyc           	      C   s   |d }|d }|d }|d }|| j vr'|r| |S td|| dS | j |\}}|r8|s8| |S |durB|sB|| |rO|||d| j|< dS dS )z3
        A blocking call received a reply.
        r_   r   r   rt   z!Got an unexpected reply {}, id:{}N)rt   r3   r_   )r?   _async_errorrw   rx   r   rW   r>   )	r   rh   ry   r_   r   r   rt   r   rZ   r   r   r   rB     s,   


z"CommBase._handle_remote_call_replyc                 C      |   dS )zR
        Handle an error that was raised on the other side asyncronously.
        N)r%   r   Zerror_wrapperr   r   r   r        zCommBase._async_errorc                 C   r   )zQ
        Handle an error that was raised on the other side syncronously.
        N)r   r   r   r   r   r     r   zCommBase._sync_errorr   )NN)NNN)F)r   r	   r
   __doc__r   rK   rM   rO   rV   rC   r6   ri   r8   propertyrm   r@   rq   ro   rn   rA   r~   r   r   r   r}   r   r   rB   r   r   __classcell__r   r   rD   r   r5      s<    






%


%$r5   c                       s0   e Zd ZdZ fddZdd Zdd Z  ZS )rY   zClass to create `RemoteCall`s.c                    sL   t t| d| t t| d| t t| d| t t| d| d S )N_comms_wrapper_comm_id	_callback	_settings)r9   rY   __setattr__)r   comms_wrapperrI   rZ   r[   rD   r   r   r     s   
zRemoteCallFactory.__init__c                 C   s   t || j| j| j| jS )z'Get a call for a function named 'name'.)
RemoteCallr   r   r   r   )r   namer   r   r   __getattr__  s   zRemoteCallFactory.__getattr__c                 C   r   )z#Set an attribute to the other side.r   )r   r   r3   r   r   r   r     s   zRemoteCallFactory.__setattr__)r   r	   r
   r   r   r   r   r   r   r   rD   r   rY     s
    rY   c                   @   s    e Zd ZdZdd Zdd ZdS )r   z:Class to call the other side of the comms like a function.c                 C   s"   || _ || _|| _|| _|| _d S r   )_namer   r   r   r   )r   r   r   rI   rZ   r[   r   r   r   r     s
   
zRemoteCall.__init__c                 O   s   d| j v o	| j d }|p| jdu| j d< t j}| j|| j d}||d}| j| js=|r3t	dt
d| j  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   r[   )r{   r|   r\   zCall to unconnected comm: %s)r   r   uuiduuid4hexr   r   rO   r   r   rw   rx   r   r   )r   r.   kwargsr   r   r   r   r   r   r   __call__  s&   
zRemoteCall.__call__N)r   r	   r
   r   r   r   r   r   r   r   r     s    r   )r   
__future__r   rd   rk   loggingr   r   r   Zspyder_kernels.py3compatr   r   	getLoggerr   rw   rp   r   RuntimeErrorr   r   
excepthookr1   r4   objectr5   rY   r   r   r   r   r   <module>   s,   .
+  n