U
    /e-                     @  sB  d Z ddlmZ ddlZee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mZmZmZmZ ddlmZ erddlmZ d	d
lmZ ddlmZ erd	dlmZ d	dlmZ d	dl m!Z! d	dl"m#Z$ ddlm%Z%m&Z& ddl'm(Z( dZ)edZ*ededef dZ+dddddZ,ddddZ-G dd  d Z.dS )!z( Provides the ``ServerSession`` class.

    )annotationsN)copywraps)TYPE_CHECKINGAny	AwaitableCallableListSetTypeVar)locks)IOLoop   )generate_jwt_token   )DocumentCallbackGroup)ID)Document)DocumentPatchedEvent)messages)CallbackSessionCallback)ServerConnection)current_timeServerSessionTF.)bound)funcreturnc                   s   t  dd fdd}|S )zDecorator that adds the necessary locking and post-processing
       to manipulate the session's document. Expects to decorate a
       method on ServerSession and transforms it into a coroutine
       if it wasn't already.
    r   selfc              	     s   | j rtd d S |   z| j I d H j | jd k	rBtdg | _z( | f||}t	
|rn|I d H }W 5 | j}d | _X |D ]}|I d H  qW 5 Q R X |W S |   X d S )Nz6Ignoring locked callback on already-destroyed session.zUinternal class invariant violated: _pending_writes should be None if lock is not held)	destroyedlogdebugblock_expirationunblock_expiration_lockacquire_pending_writesRuntimeErrorinspectisawaitable)r"   argskwargsZpending_writesresultpr    8/tmp/pip-unpacked-wheel-f5fndrjf/bokeh/server/session.py_needs_document_lock_wrapperN   s&    


z:_needs_document_lock.<locals>._needs_document_lock_wrapperr   )r   r5   r3   r2   r4   _needs_document_lockH   s     r6   floatr    c                   C  s   t  d S )zWReturn the time in milliseconds since the epoch as a floating
       point number.
    i  )time	monotonicr3   r3   r3   r4   r   u   s    r   c                   @  s  e Zd ZU dZded< ded< ded< d^d	d
ddddddZed
dddZed	dddZeddddZ	eddddZ
eddddZeddddZed dd!d"Zddd#d$Zddd%d&Zddd'd(Zddd)d*Zd+dd,d-d.Zd+dd,d/d0Zed dd1d2Zed3dd4d5Zed6d7d7d8d9d:d;Zd<d<d=d>d?Zd@d@d=dAdBZdCddDdEdFZedGd+dHdIdJdKZd@dLdMdNZdOdP ZedGd+dHdIdQdRZedSd+dTdIdUdVZ edSd+dTdIdWdXZ!edYd+dTdIdZd[Z"edYd+dTdId\d]Z#dS )_r   zu Hosts an application "instance" (an instantiated Document) for one or more connections.

    .. autoclasstoc::

    zSet[ServerConnection]_subscribed_connectionszServerConnection | None_current_patch_connectionzList[Awaitable[None]] | Noner*   Nr   r   zIOLoop | Nonez
str | NoneNone)
session_iddocumentio_looptokenr    c                   s   |d krt d|d kr t d| _| _| _| _t  _t  _t	
  _d  _ jj  t| _d  _d _d _d _ fdd jjD } j| d S )NzSessions must have an idzSessions must have a documentFr   c                   s   g | ]}  |qS r3   )_wrap_session_callback).0cbr!   r3   r4   
<listcomp>   s     z*ServerSession.__init__.<locals>.<listcomp>)
ValueError_id_token	_documentZ_loopsetr;   r   _last_unsubscribe_timer   Lockr(   r<   	callbacksZon_change_dispatch_tor   
_callbacksr*   
_destroyed_expiration_requested_expiration_blocked_countZsession_callbacksZadd_session_callbacks)r"   r>   r?   r@   rA   Zwrapped_callbacksr3   r!   r4   __init__   s(    

zServerSession.__init__r8   c                 C  s   | j S N)rI   r!   r3   r3   r4   r?      s    zServerSession.documentc                 C  s   | j S rS   )rG   r!   r3   r3   r4   id   s    zServerSession.idstrc                 C  s   | j r| j S t| jS )z* A JWT token to authenticate the session. )rH   r   rT   r!   r3   r3   r4   rA      s    zServerSession.tokenboolc                 C  s   | j S rS   )rO   r!   r3   r3   r4   r#      s    zServerSession.destroyedc                 C  s   | j S rS   rP   r!   r3   r3   r4   expiration_requested   s    z"ServerSession.expiration_requestedc                 C  s
   | j dkS )Nr   rQ   r!   r3   r3   r4   expiration_blocked   s    z ServerSession.expiration_blockedintc                 C  s   | j S rS   rY   r!   r3   r3   r4   expiration_blocked_count   s    z&ServerSession.expiration_blocked_countc                 C  s(   d| _ | j|  | `| j  | `d S )NT)rO   rI   destroyrN   Zremove_all_callbacksr!   r3   r3   r4   r]      s
    
zServerSession.destroyc                 C  s
   d| _ dS )zK Used in test suite for now. Forces immediate expiration if no connections.TNrW   r!   r3   r3   r4   request_expiration   s    z ServerSession.request_expirationc                 C  s   |  j d7  _ d S )Nr   rY   r!   r3   r3   r4   r&      s    zServerSession.block_expirationc                 C  s$   | j dkrtd|  j d8  _ d S )Nr   z0mismatched block_expiration / unblock_expirationr   )rQ   r+   r!   r3   r3   r4   r'      s    
z ServerSession.unblock_expirationr   )
connectionr    c                 C  s   | j | dS )zgThis should only be called by ``ServerConnection.subscribe_session`` or our book-keeping will be brokenN)r;   addr"   r_   r3   r3   r4   	subscribe   s    zServerSession.subscribec                 C  s   | j | t | _dS )ziThis should only be called by ``ServerConnection.unsubscribe_session`` or our book-keeping will be brokenN)r;   discardr   rK   ra   r3   r3   r4   unsubscribe   s    zServerSession.unsubscribec                 C  s
   t | jS rS   )lenr;   r!   r3   r3   r4   connection_count   s    zServerSession.connection_countr7   c                 C  s   t  | j S rS   )r   rK   r!   r3   r3   r4   #milliseconds_since_last_unsubscribe   s    z1ServerSession.milliseconds_since_last_unsubscribezCallable[..., T]r   r   )r   r.   r/   r    c                 O  s
   |||S )zH Asynchronously locks the document and runs the function with it locked.r3   )r"   r   r.   r/   r3   r3   r4   with_document_locked   s    z"ServerSession.with_document_lockedr   )callbackr    c                   s*   t  ddr S ddd fdd}|S )NZnolockFr   r.   r/   c                    s   j  f| |S rS   )rh   rj   ri   r"   r3   r4   wrapped_callback   s    z?ServerSession._wrap_document_callback.<locals>.wrapped_callback)getattr)r"   ri   rl   r3   rk   r4   _wrap_document_callback   s    z%ServerSession._wrap_document_callbackr   c                 C  s   t |}| |j|_|S rS   )r   rn   ri   Z	_callback)r"   ri   wrappedr3   r3   r4   rB      s    z$ServerSession._wrap_session_callbackr   )eventr    c                 C  sN   |j | k}| jd krtd| jD ]&}|r6|| jkr6q"| j|| q"d S )Nzv_pending_writes should be non-None when we have a document lock, and we should have the lock when the document changes)setterr*   r+   r;   r<   appendZsend_patch_document)r"   rp   Zmay_suppressr_   r3   r3   r4   _document_patched   s    


zServerSession._document_patchedzmsg.pull_doc_reqzmsg.pull_doc_reply)messager_   r    c                 C  s*   t d| j |jd|jd | jS )Nz$Sending pull-doc-reply from session zPULL-DOC-REPLYmsgid)r$   r%   rT   protocolcreateheaderr?   r"   rt   r_   r3   r3   r4   _handle_pull  s    zServerSession._handle_pull)rp   c                 C  s   |  |j}| j| d S rS   )rB   ri   rN   Zadd_session_callback)r"   rp   ro   r3   r3   r4   _session_callback_added  s    z%ServerSession._session_callback_addedc                 C  s   | j |j d S rS   )rN   Zremove_session_callbackri   )r"   rp   r3   r3   r4   _session_callback_removed
  s    z'ServerSession._session_callback_removedc                 C  s   |j ||S )z? Handle a PULL-DOC, return a Future with work to be scheduled. )sessionrz   clsrt   r_   r3   r3   r4   pull  s    zServerSession.pullzmsg.push_doczmsg.okc                 C  s(   t d| j || j ||S )Nzpushing doc to session )r$   r%   rT   Zpush_to_documentr?   okry   r3   r3   r4   _handle_push  s    zServerSession._handle_pushc                 C  s   |j ||S )z? Handle a PUSH-DOC, return a Future with work to be scheduled. )r}   r   r~   r3   r3   r4   push  s    zServerSession.pushzmsg.patch_docc                 C  s,   || _ z|| j|  W 5 d | _ X ||S rS   )r<   Zapply_to_documentr?   r   ry   r3   r3   r4   _handle_patch  s
    zServerSession._handle_patchc                 C  s   |j ||S )z@ Handle a PATCH-DOC, return a Future with work to be scheduled. )r}   r   r~   r3   r3   r4   patch'  s    zServerSession.patch)NN)$__name__
__module____qualname____doc____annotations__rR   propertyr?   rT   rA   r#   rX   rZ   r\   r]   r^   r&   r'   rb   rd   rf   rg   r6   rh   rn   rB   rs   rz   r{   r|   classmethodr   r   r   r   r   r3   r3   r3   r4   r   {   s`   
		r   )/r   
__future__r   logging	getLoggerr   r$   r,   r9   r   	functoolsr   typingr   r   r   r	   r
   r   r   Ztornador   Ztornado.ioloopr   Z
util.tokenr   rM   r   Z
core.typesr   Zdocument.documentr   Zdocument.eventsr   rv   r   msgr   r   r_   r   __all__r   r   r6   r   r   r3   r3   r3   r4   <module>   s4   
$	-