U
    /eG-                     @  s  d Z ddlmZ ddlZeeZddlZddlm	Z	m
Z
mZmZmZmZmZmZ ddlmZ ddlm  mZ ddlmZ dd	lmZmZ e	rdd
lmZ dZG dd deZG dd deddZ e!Z"edZ#ee!e
f Z$ee"e%f Z&G dd deZ'G dd dee# Z(dS )a   Provide a base class for all Bokeh Server Protocol message types.

Boker messages are comprised of a sequence of JSON fragments. Specified as
Python JSON-like data, messages have the general form:

.. code-block:: python

    [
        # these are required
        b'{header}',        # serialized header dict
        b'{metadata}',      # serialized metadata dict
        b'{content},        # serialized content dict

        # these are optional, and come in pairs; header contains num_buffers
        b'{buf_header}',    # serialized buffer header dict
        b'array'            # raw buffer payload data
        ...
    ]

The ``header`` fragment will have the form:

.. code-block:: python

    header = {
        # these are required
        'msgid'       : <str> # a unique id for the message
        'msgtype'     : <str> # a message type, e.g. 'ACK', 'PATCH-DOC', etc

        # these are optional
        'num_buffers' : <int> # the number of additional buffers, if any
    }

The ``metadata`` fragment may contain any arbitrary information. It is not
processed by Bokeh for any purpose, but may be useful for external
monitoring or instrumentation tools.

The ``content`` fragment is defined by the specific message type.

    )annotationsN)TYPE_CHECKINGAnyClassVarDictGenericListTupleTypeVar)	TypedDict   )ID   )MessageErrorProtocolError) WebSocketClientConnectionWrapper)Messagec                   @  s   e Zd ZU ded< ded< dS )_Headerr   msgidstrmsgtypeN__name__
__module____qualname____annotations__ r   r   :/tmp/pip-unpacked-wheel-f5fndrjf/bokeh/protocol/message.pyr   e   s   
r   c                   @  s   e Zd ZU ded< ded< dS )Headerr   reqidintnum_buffersNr   r   r   r   r   r   i   s   
r   F)totalContentc                   @  s   e Zd ZdS )EmptyN)r   r   r   r   r   r   r   r$   w   s   r$   c                   @  s  e Zd ZU dZded< ded< ded< ded	< ded
< ded< ded< ded< dddddddZddddZedddddddZdddddd Z	ddddd!d"Z
dFd$d%d&d'd(d)ZedGd+dd,d-d.Zd$d&d/d0d1Zed%dd2d3Zeddd4d5Zejddd6d7d5Zeddd8d9Zeddd:d;Zejddd6d<d;Zeddd=d>Zeddd?d@Zejddd6dAd@ZedddBdCZedddDdEZd*S )Hr   z The Message base class encapsulates creating, assembling, and
    validating the integrity of Bokeh Server messages. Additionally, it
    provide hooks

    zClassVar[str]r   r   _headerz
str | None_header_jsonr#   _content_content_jsonMetadata	_metadata_metadata_jsonzList[BufferRef]_buffersNone)headermetadatacontentreturnc                 C  s   || _ || _|| _g | _dS )a   Initialize a new message from header, metadata, and content
        dictionaries.

        To assemble a message from existing JSON fragments, use the
        ``assemble`` method.

        To create new messages with automatically generated headers,
        use subclass ``create`` methods.

        Args:
            header (JSON-like) :

            metadata (JSON-like) :

            content (JSON-like) :

        N)r.   r/   r0   r,   )selfr.   r/   r0   r   r   r   __init__   s    zMessage.__init__r   )r1   c                 C  s   d| j d| jS )NzMessage z
 content: )r   r0   r2   r   r   r   __repr__   s    zMessage.__repr__zMessage[Content])header_jsonmetadata_jsoncontent_jsonr1   c                 C  s   zt |}W n tk
r*   tdY nX zt |}W n tk
rV   tdY nX zt |}W n tk
r   tdY nX | |||}||_||_||_|S )a   Creates a new message, assembled from JSON fragments.

        Args:
            header_json (``JSON``) :

            metadata_json (``JSON``) :

            content_json (``JSON``) :

        Returns:
            Message subclass

        Raises:
            MessageError

        zheader could not be decodedzmetadata could not be decodedzcontent could not be decoded)jsonloads
ValueErrorr   r&   r+   r(   )clsr6   r7   r8   r.   r/   r0   msgr   r   r   assemble   s"    zMessage.assembleBufferHeaderbytes)
buf_headerbuf_payloadr1   c                 C  sB   d| j kr| j d  d7  < n
d| j d< d| _| j||f dS )a   Associate a buffer header and payload with this message.

        Args:
            buf_header (``JSON``) : a buffer header
            buf_payload (``JSON`` or bytes) : a buffer payload

        Returns:
            None

        Raises:
            MessageError

        r!   r   N)r%   r&   r,   append)r2   rA   rB   r   r   r   
add_buffer   s
    

zMessage.add_bufferc                 C  s>   | j dd}|t| jkr*td| | j||f dS )a   Add a buffer header and payload that we read from the socket.

        This differs from add_buffer() because we're validating vs.
        the header's num_buffers, instead of filling in the header.

        Args:
            buf_header (``JSON``) : a buffer header
            buf_payload (``JSON`` or bytes) : a buffer payload

        Returns:
            None

        Raises:
            ProtocolError
        r!   r   z$too many buffers received expecting N)r.   getlenr,   r   rC   )r2   rA   rB   r!   r   r   r   assemble_buffer   s    zMessage.assemble_bufferTr   boolr    )connlockedr1   c                   sf   |dkrt dd}| jD ]F\}}|j||dI dH  |j|d|dI dH  |t|t| 7 }q|S )aa   Write any buffer headers and payloads to the given connection.

        Args:
            conn (object) :
                May be any object with a ``write_message`` method. Typically,
                a Tornado ``WSHandler`` or ``WebSocketClientConnection``

            locked (bool) :

        Returns:
            int : number of bytes sent

        Nz'Cannot write_buffers to connection Noner   rJ   T)binaryrJ   )r;   r,   write_messagerF   )r2   rI   rJ   sentr.   payloadr   r   r   write_buffers   s    zMessage.write_buffersNz	ID | None)
request_idr1   c                 C  s&   t t | jd}|dk	r"||d< |S )z Return a message header fragment dict.

        Args:
            request_id (str or None) :
                Message ID of the message this message replies to

        Returns:
            dict : a message header

        )r   r   Nr   )r   bkserialZmake_idr   )r<   rQ   r.   r   r   r   create_header  s    zMessage.create_header)rI   r1   c              
     s   |dkrt d|j I dH  d}|j| jddI dH  |t| j7 }|j| jddI dH  |t| j7 }|j| jddI dH  |t| j7 }|| j|ddI dH 7 }|W  5 Q R  S Q R X dS )z Send the message on the given connection.

        Args:
            conn (WebSocketHandler) : a WebSocketHandler to send messages

        Returns:
            int : number of bytes sent

        NzCannot send to connection Noner   FrK   )	r;   Z
write_lockacquirerM   r6   rF   r7   r8   rP   )r2   rI   rN   r   r   r   send)  s    
zMessage.sendc                 C  s6   | j dk	o4| jdk	o4| jdk	o4| j ddt| jkS )z Returns whether all required parts of a message are present.

        Returns:
            bool : True if the message is complete, False otherwise

        Nr!   r   )r.   r/   r0   rE   rF   r,   r4   r   r   r   completeL  s    
zMessage.completec                 C  s   | j S N)r%   r4   r   r   r   r.   [  s    zMessage.header)valuer1   c                 C  s   || _ d | _d S rW   )r%   r&   r2   rX   r   r   r   r.   _  s    c                 C  s   | j st| j| _ | j S rW   )r&   r9   dumpsr.   r4   r   r   r   r6   d  s    zMessage.header_jsonc                 C  s   | j S rW   )r'   r4   r   r   r   r0   l  s    zMessage.contentc                 C  s   || _ d | _d S rW   )r'   r(   rY   r   r   r   r0   p  s    c                 C  s   | j st| j| _ | j S rW   )r(   r9   rZ   r0   r4   r   r   r   r8   u  s    zMessage.content_jsonc                 C  s   | j S rW   )r*   r4   r   r   r   r/   }  s    zMessage.metadatac                 C  s   || _ d | _d S rW   )r*   r+   rY   r   r   r   r/     s    c                 C  s   | j st| j| _ | j S rW   )r+   r9   rZ   r/   r4   r   r   r   r7     s    zMessage.metadata_jsonc                 C  s   | j S rW   )r,   r4   r   r   r   buffers  s    zMessage.buffers)T)N)r   r   r   __doc__r   r3   r5   classmethodr>   rD   rG   rP   rS   rU   propertyrV   r.   setterr6   r0   r8   r/   r7   r[   r   r   r   r   r   z   sR   
)#r   ))r\   
__future__r   logging	getLoggerr   logr9   typingr   r   r   r   r   r   r	   r
   Ztyping_extensionsr   Zbokeh.util.serializationutilZserializationrR   Z
core.typesr   
exceptionsr   r   Zclient.websocketr   __all__r   r   r   r?   r#   r)   r@   Z	BufferRefr$   r   r   r   r   r   <module>   s(   +
(