B
    3\F                 @   s   d Z ddlmZ ddlmZmZ ddlmZ ddlZddlm	Z	m
Z
 ddlmZ ddlmZm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mZmZmZmZ ddl m!Z!m"Z" ddl#m$Z$m%Z% ddl&m'Z' G dd deZ(dS )zfA MultiKernelManager for use in the notebook webserver

- raises HTTPErrors
- creates REST API models
    )defaultdict)datetime	timedelta)partialN)genweb)Future)IOLoopPeriodicCallback)Session)MultiKernelManager)AnyBoolDictListUnicode
TraitErrorIntegerFloatInstancedefaultvalidate)
to_os_pathexists)utcnow	isoformat)getcwdc                   s  e Zd ZdZeddd Zee ZeddZ	e
 ZdZdZed	d
d Zed	dd ZeddddZdZee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 Zeddd ZeeddZ  fddZ!dd  Z"d!d" Z#e$j%dA fd#d$	Z&d%d& Z'd'd( Z(d)d* Z)dB fd+d,	Z*e$j% fd-d.Z+d/d0 Z,d1d2 Z-d3d4 Z. fd5d6Z/d7d8 Z0d9d: Z1d;d< Z2d=d> Z3d?d@ Z4  Z5S )CMappingKernelManagerzEA KernelManager that handles notebook mapping and HTTP error handlingZkernel_manager_classc             C   s   dS )Nz)jupyter_client.ioloop.IOLoopKernelManager )selfr   r   Flib/python3.7/site-packages/notebook/services/kernels/kernelmanager.py_default_kernel_manager_class!   s    z2MappingKernelManager._default_kernel_manager_classT)configNFroot_dirc             C   s$   y| j jS  tk
r   t S X d S )N)parentZnotebook_dirAttributeErrorr   )r   r   r   r    _default_root_dir/   s    z&MappingKernelManager._default_root_dirc             C   sD   |d }t j|s t j|}t|r4t j|s@td| |S )z'Do a bit of validation of the root dir.valuez%kernel root dir %r is not a directory)ospathisabsabspathr   isdirr   )r   Zproposalr'   r   r   r    _update_root_dir6   s    z%MappingKernelManager._update_root_dirr   zTimeout (in seconds) after which a kernel is considered idle and ready to be culled.
        Values of 0 or lower disable culling. Very short timeouts may result in kernels being culled
        for users with poor network connections.)r"   helpi,  z^The interval (in seconds) on which to check for idle kernels exceeding the cull timeout value.zxWhether to consider culling kernels which have one or more connections.
        Only effective if cull_idle_timeout > 0.zdWhether to consider culling kernels which are busy.
        Only effective if cull_idle_timeout > 0.ae  Whether messages from kernels whose frontends have disconnected should be buffered in-memory.

        When True (default), messages are buffered and replayed on reconnect,
        avoiding lost messages due to interrupted connectivity.

        Disable if long-running kernels will produce too much output while
        no frontends are connected.
        <   a  Timeout for giving up on a kernel (in seconds).

        On starting and restarting kernels, we check whether the
        kernel is running and responsive by sending kernel_info_requests.
        This sets the timeout in seconds for how long the kernel can take
        before being presumed dead. 
        This affects the MappingKernelManager (which handles kernel restarts) 
        and the ZMQChannelsHandler (which handles the startup).
        _kernel_buffersc             C   s   t dd S )Nc               S   s   g di dS )N )buffersession_keychannelsr   r   r   r   r    <lambda>p       z>MappingKernelManager._default_kernel_buffers.<locals>.<lambda>)r   )r   r   r   r    _default_kernel_buffersn   s    z,MappingKernelManager._default_kernel_bufferszAThe last activity on any kernel, including shutting down a kernel)r.   c                s   t t| jf | t | _d S )N)superr   __init__r   last_kernel_activity)r   kwargs)	__class__r   r    r9   u   s    zMappingKernelManager.__init__c             C   s   | j d| | | dS )znotice that a kernel diedz"Kernel %s died, removing from map.N)logwarningZremove_kernel)r   	kernel_idr   r   r    _handle_kernel_died}   s    z(MappingKernelManager._handle_kernel_diedc             C   s8   t || j}x&tj|s2|| jkr2tj|}qW |S )z$Turn API path into absolute OS path.)r   r#   r(   r)   r,   dirname)r   r)   Zos_pathr   r   r    cwd_for_path   s    z!MappingKernelManager.cwd_for_pathc             +   s    dkr|dk	r ||d< tttjf |V  dj <   j	d   j
d|    fddd n  j	d	   js  t dS )
as  Start a kernel for a session and return its kernel_id.

        Parameters
        ----------
        kernel_id : uuid
            The uuid to associate the new kernel with. If this
            is not None, this kernel will be persistent whenever it is
            requested.
        path : API path
            The API path (unicode, '/' delimited) for the cwd.
            Will be transformed to an OS path relative to root_dir.
        kernel_name : str
            The name identifying which kernel spec to launch. This is ignored if
            an existing kernel is returned, but it may be checked in the future.
        Ncwdr   zKernel started: %szKernel args: %rc                  s
     S )N)r@   r   )r?   r   r   r    r5      r6   z3MappingKernelManager.start_kernel.<locals>.<lambda>deadzUsing existing kernel: %s)rB   r   maybe_futurer8   r   start_kernel_kernel_connectionsstart_watching_activityr=   infodebugadd_restart_callback_check_kernel_id_initialized_cullerinitialize_cullerZReturn)r   r?   r)   r;   )r<   )r?   r   r    rF      s"    


z!MappingKernelManager.start_kernelc                s   j s(x| D ]\}}|  qW dS jd|   j  | d< g  d< | d<  fdd}x$| D ]\}}|t	|| qW dS )a  Start buffering messages for a kernel

        Parameters
        ----------
        kernel_id : str
            The id of the kernel to stop buffering.
        session_key: str
            The session_key, if any, that should get the buffer.
            If the session_key matches the current buffered session_key,
            the buffer will be returned.
        channels: dict({'channel': ZMQStream})
            The zmq channels whose messages should be buffered.
        NzStarting buffering for %sr3   r2   r4   c                s&   j d|   d | |f d S )NzBuffering msg on %s:%sr2   )r=   rJ   append)channelZ	msg_parts)buffer_infor?   r   r   r    
buffer_msg   s    z8MappingKernelManager.start_buffering.<locals>.buffer_msg)
buffer_offline_messagesitemscloser=   rI   rL   stop_bufferingr0   on_recvr   )r   r?   r3   r4   rP   streamrR   r   )rQ   r?   r   r    start_buffering   s    


z$MappingKernelManager.start_bufferingc             C   sP   | j d| || jkrdS | j| }|d |krB| j| |S | | dS )as  Get the buffer for a given kernel

        Parameters
        ----------
        kernel_id : str
            The id of the kernel to stop buffering.
        session_key: str, optional
            The session_key, if any, that should get the buffer.
            If the session_key matches the current buffered session_key,
            the buffer will be returned.
        zGetting buffer for %sNr3   )r=   rJ   r0   poprV   )r   r?   r3   rQ   r   r   r    
get_buffer   s    

zMappingKernelManager.get_bufferc             C   s   | j d| | | || jkr&dS | j|}x.|d  D ]}| s@|d |  q@W |d }|r| j 	dt
||d  dS )zStop buffering kernel messages

        Parameters
        ----------
        kernel_id : str
            The id of the kernel to stop buffering.
        zClearing buffer for %sNr4   r2   z&Discarding %s buffered messages for %sr3   )r=   rJ   rL   r0   rZ   valuesclosedrW   rU   rI   len)r   r?   rQ   rX   Z
msg_bufferr   r   r    rV      s    


z#MappingKernelManager.stop_bufferingc                s^   |  | | j| }|jr*|j  d|_| | | j|d t | _t	t
| j||dS )zShutdown a kernel by kernel_idN)now)rL   _kernels_activity_streamrU   rV   rG   rZ   r   r:   r8   r   shutdown_kernel)r   r?   r_   kernel)r<   r   r    rb     s    



z$MappingKernelManager.shutdown_kernelc             #   s     tttV    t  fddfdd}fdd}fdd	d	 j
 d
  | t  j |V  dS )zRestart a kernel by kernel_idc                  s*      s    d dS )z:Common cleanup when restart finishes/fails for any reason.rD   N)r]   rU   Zremove_timeoutZremove_restart_callbackr   )rP   rc   loopon_restart_failedtimeoutr   r    finish%  s    
z3MappingKernelManager.restart_kernel.<locals>.finishc                s*   j d     s&|  d S )NzKernel info reply received: %s)r=   rJ   doneZ
set_result)msg)rg   futurer?   r   r   r    on_reply,  s    z5MappingKernelManager.restart_kernel.<locals>.on_replyc                  s0   j d     s,td d S )Nz)Timeout waiting for kernel_info_reply: %szTimeout waiting for restart)r=   r>   rh   set_exceptionr   TimeoutErrorr   )rg   rj   r?   r   r   r    
on_timeout2  s    z7MappingKernelManager.restart_kernel.<locals>.on_timeoutc                  s.   j d     s*td d S )NzRestarting kernel failed: %szRestart failed)r=   r>   rh   rl   RuntimeErrorr   )rg   rj   r?   r   r   r    re   8  s    z>MappingKernelManager.restart_kernel.<locals>.on_restart_failedrD   Zkernel_info_requestN)rL   r   rE   r8   r   restart_kernelZ
get_kernelZconnect_shellr   rK   sessionsendrW   r	   currentZadd_timeoutZtimekernel_info_timeout)r   r?   rk   rn   )r<   )	rP   rg   rj   rc   r?   rd   re   r   rf   r    rp     s    


z#MappingKernelManager.restart_kernelc             C   s    || j kr| j |  d7  < dS )z#Notice a new connection to a kernel   N)rG   )r   r?   r   r   r    notify_connectF  s    
z#MappingKernelManager.notify_connectc             C   s    || j kr| j |  d8  < dS )z$Notice a disconnection from a kernelru   N)rG   )r   r?   r   r   r    notify_disconnectK  s    
z&MappingKernelManager.notify_disconnectc             C   s8   |  | | j| }||jt|j|j| j| d}|S )zqReturn a JSON-safe dict representing a kernel

        For use in representing kernels in the JSON APIs.
        )idnamelast_activityexecution_stateconnections)rL   r`   kernel_namer   rz   r{   rG   )r   r?   rc   modelr   r   r    kernel_modelP  s    

z!MappingKernelManager.kernel_modelc                s8   g }t t|  }x |D ]}| |}|| qW |S )z1Returns a list of kernel_id's of kernels running.)r8   r   Zlist_kernel_idsr   rO   )r   ZkernelsZ
kernel_idsr?   r~   )r<   r   r    list_kernelsa  s    

z!MappingKernelManager.list_kernelsc             C   s   || krt dd| dS )z5Check a that a kernel_id exists and raise 404 if not.i  zKernel does not exist: %sN)r   Z	HTTPError)r   r?   r   r   r    rL   k  s    z%MappingKernelManager._check_kernel_idc                sX   j   d _t  _   _t jj jj	d fdd} j
| dS )zStart watching IOPub messages on a kernel for activity.
        
        - update last_activity on every message
        - record execution_state from status messages
        Zstarting)r"   keyc                s\   t   _ _| \}}|}|d d }jd| |dkrX|d d  _dS )z.Record an IOPub message arriving from a kernelheadermsg_typezactivity on %s: %sZstatusZcontentr{   N)r   r:   rz   Zfeed_identitiesZdeserializer=   rJ   r{   )Zmsg_listZidentsZfed_msg_listri   r   )rc   r?   r   rq   r   r    record_activity  s    
zEMappingKernelManager.start_watching_activity.<locals>.record_activityN)r`   r{   r   rz   Zconnect_iopubra   r   rq   r"   r   rW   )r   r?   r   r   )rc   r?   r   rq   r    rH   r  s    

z,MappingKernelManager.start_watching_activityc             C   s   | j s| jdkr| jdkrt }| jdkrH| jd| j| j | j| _t	| j
d| j | _| jd| j| j | jr| jd | jr| jd | j  d| _ dS )	zStart idle culler if 'cull_idle_timeout' is greater than zero.

        Regardless of that value, set flag that we've been here.
        r   NzKInvalid value for 'cull_interval' detected (%s) - using default value (%s).i  zKCulling kernels with idle durations > %s seconds at %s second intervals ...zCulling kernels even if busyz+Culling kernels even with connected clientsT)rM   cull_idle_timeout_culler_callbackr	   rs   cull_intervalr=   r>   cull_interval_defaultr
   cull_kernelsrI   	cull_busycull_connectedstart)r   rd   r   r   r    rN     s"    


z&MappingKernelManager.initialize_cullerc             C   sn   | j d| j| j xTt| jD ]F}y| | W q  tk
rd } z| j d|| W d d }~X Y q X q W d S )Nz9Polling every %s seconds for kernels idle > %s seconds...zYThe following exception was encountered while checking the idle duration of kernel %s: %s)	r=   rJ   r   r   listr`   cull_kernel_if_idle	ExceptionZ	exception)r   r?   er   r   r    r     s    z!MappingKernelManager.cull_kernelsc       
      C   s   | j | }| jd||j|j |jd k	rt }||j }|t| jdk}| jpX|j	dk}| j
|d}| jpr| }|r|r|rt| }	| jd|j	|j|||	 | | d S )Nz.kernel_id=%s, kernel_name=%s, last_activity=%s)ZsecondsZbusyr   zRCulling '%s' kernel '%s' (%s) with %d connections due to %s seconds of inactivity.)r`   r=   rJ   r}   rz   r   r   r   r   r{   rG   getr   intZtotal_secondsr>   rb   )
r   r?   rc   Zdt_nowZdt_idleZis_idle_timeZis_idle_executer|   Zis_idle_connectedZidle_durationr   r   r    r     s    


z(MappingKernelManager.cull_kernel_if_idle)NN)F)6__name__
__module____qualname____doc__r   r!   r   r   Zkernel_argvr#   r   rG   r   rM   r&   r   r-   r   r   r   r   r   r   r   rS   r   rt   r   r0   r7   r   r   r:   r9   r@   rB   r   	coroutinerF   rY   r[   rV   rb   rp   rv   rw   r   r   rL   rH   rN   r   r   __classcell__r   r   )r<   r    r      sZ   

		*'+
r   ))r   collectionsr   r   r   	functoolsr   r(   Ztornador   r   Ztornado.concurrentr   Ztornado.ioloopr	   r
   Zjupyter_client.sessionr   Z!jupyter_client.multikernelmanagerr   Z	traitletsr   r   r   r   r   r   r   r   r   r   r   Znotebook.utilsr   r   Znotebook._tzr   r   Zipython_genutils.py3compatr   r   r   r   r   r    <module>   s   4