o
    tf1                     @   s   d 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	 G dd de
ZG dd dZG d	d
 d
eZG dd deZG dd dZdS )z(
Simple asynchronous process supervisor
    N   )add_handlerremove_handlerc                   @   s   e Zd ZdZdS )KilledProcessErrorz
    Raised when a process that has been explicitly killed is started again.

    Each SupervisedProcess can be killed only once.
    N)__name__
__module____qualname____doc__ r
   r
   \/var/www/html/software/conda/envs/catlas/lib/python3.10/site-packages/simpervisor/process.pyr      s    r   c                   @   s\   e Zd ZdZdZdZdZdd Zdd Zdd Z	d	d
 Z
dd Zedd Zedd ZdS )Processzb
    Abstract class to start, wait and send signals to running processes in a OS agnostic way
    Nc                 O   s   || _ || _d S N)	_proc_cmd_proc_kwargs)selfcmdkwargsr
   r
   r   __init__    s   
zProcess.__init__c                       t )z#
        Start the process
        NotImplementedErrorr   r
   r
   r   start$      zProcess.startc                    r   )zU
        Wait for the process to terminate and return the process exit code.
        r   r   r
   r
   r   wait*   r   zProcess.waitc                 C   s   t )zF
        Returns the preferred OS signal to kill the process.
        r   r   r
   r
   r   get_kill_signal0   s   zProcess.get_kill_signalc                 C   s   | j r| j | dS dS )z4
        Send the OS signal to the process.
        N)_procsend_signalr   signumr
   r
   r   r   6   s   zProcess.send_signalc                 C      | j r| j jS d S r   )r   pidr   r
   r
   r   r!   =      zProcess.pidc                 C   r    r   )r   
returncoder   r
   r
   r   r#   B   r"   zProcess.returncode)r   r   r   r	   r   r   r   r   r   r   r   r   propertyr!   r#   r
   r
   r
   r   r      s    
r   c                   @   (   e Zd ZdZdd Zdd Zdd ZdS )	POSIXProcesszG
    A process that uses asyncio-subprocess API to start and wait.
    c                    s"   t j| ji | jI dH | _dS )zL
        Start the process using asyncio.create_subprocess_exec API
        N)asynciocreate_subprocess_execr   r   r   r   r
   r
   r   r   M   s   zPOSIXProcess.startc                    s   | j  I dH S )zP
        Wait for the process to stop and return the process exit code.
        N)r   r   r   r
   r
   r   r   U   s   zPOSIXProcess.waitc                 C      t jS )zH
        Returns the OS signal used for kill the child process.
        )signalSIGKILLr   r
   r
   r   r   [   s   zPOSIXProcess.get_kill_signalNr   r   r   r	   r   r   r   r
   r
   r
   r   r&   H   s
    r&   c                   @   r%   )	WindowsProcesszS
    A process that uses subprocess API to start and wait (uses busy polling).
    c                    s"   t jt| jfi | j| _dS )z?
        Starts the process using subprocess.Popen API
        N)
subprocessPopenlistr   r   r   r   r
   r
   r   r   g   s    zWindowsProcess.startc                    s8   | j  du rtdI dH  | j  du s| j  S )a3  
        Wait for the process to stop and return the process exit code.

        subprocess.Popen.wait() is a blocking call which can cause the asyncio
        event loop to remain blocked until the child process is terminated.

        To circumvent this behavior, we use busy polling with asyncio.sleep to check
        whether the child process is alive or not and keeping the asyncio event
        loop running.

        See https://github.com/jupyter/jupyter_client/blob/main/jupyter_client/provisioning/local_provisioner.py#L54_L55 for similar use.
        Ng      ?)r   pollr'   sleepr   r   r
   r
   r   r   m   s
   
zWindowsProcess.waitc                 C   r)   )a  
        Returns the OS signal used for kill the child process.

        Windows doesn't support SIGKILL. subprocess.Popen.kill() is an alias of
        subprocess.Popen.terminate(), so we can use SIGTERM instead of SIGKILL
        on windows platform.
        )r*   SIGTERMr   r
   r
   r   r   ~   s   zWindowsProcess.get_kill_signalNr,   r
   r
   r
   r   r-   b   s
    r-   c                   @   sz   e Zd ZdddddddZdddZd	d
 Zdd Zdd Zdd Zdd Z	dd Z
dd Zedd Zedd ZdS )SupervisedProcessFN   )always_restart
ready_funcready_timeoutlogc                O   s`   || _ || _|| _|| _|| _|| _d | _|d u r td| _	n|| _	d| _
d| _t | _d S )NZsimpervisorF)r6   name
_proc_argsr   r7   r8   proclogging	getLoggerr9   running_killedr'   Lock
_proc_lock)r   r:   r6   r7   r8   r9   argsr   r
   r
   r   r      s   
zSupervisedProcess.__init__c                 G   s<   || j | j| jd}|r|| | jj|j| |d dS )zn
        Log debug message with some added meta information.

        Makes structured logging easier
        )actionzproccess-namezprocess-argszprocess-kwargs)extraN)r:   r;   r   updater9   debugformat)r   rD   messageextrasrC   Zbase_extrasr
   r
   r   
_debug_log   s   
zSupervisedProcess._debug_logc                 C   s*   | j | d| _| ddi || j d S )NTr*   zPropagated signal {} to {})r<   r   r@   rK   r:   )r   r*   r
   r
   r   _handle_signal   s   z SupervisedProcess._handle_signalc              	      s  | j 4 I dH n | jr	 W d  I dH  dS | jr$td| j d| ddi | j tjdkr=t| j	i | j
| _n
t| j	i | j
| _| j I dH  | ddi | j d	| _d
| _t|  | _t| j W d  I dH  dS 1 I dH s|w   Y  dS )z
        Start the process if it isn't already running.

        If the process is already running, this is a noop. If the process
        has already been killed, this raises an exception
        NProcess # has already been explicitly killedz	try-startzTrying to start {}win32startedz
Started {}FT)rB   r?   r@   r   r:   rK   sysplatformr-   r;   r   r<   r&   r   r'   ensure_future_restart_process_if_needed_restart_process_futurer   rL   r   r
   r
   r   r      s,   
.zSupervisedProcess.startc                    sh   | j  I dH }t| j | ddd|i| j| d| _| js0| js'|dkr2| 	 I dH  dS dS dS )z
        Watch for process to exit & restart it if needed.

        This is a long running task that keeps running until the process
        exits. If we restart the process, `start()` sets this up again.
        NZexitedz{} exited with code {}codeFr   )
r<   r   r   rL   rK   r:   r?   r@   r6   r   )r   retcoder
   r
   r   rT      s   
z,SupervisedProcess._restart_process_if_neededc              	      s~   | j 4 I dH * | j| d| _| j  | j I dH  d| _t| j	 W d  I dH  dS 1 I dH s8w   Y  dS )a  
        Send a SIGTERM or SIGKILL to the child process & reap it.

        - Send the signal to the process
        - Make sure we don't restart it when it stops
        - Wait for it to stop
        - Remove signal handler for it after we are done.
        NTF)
rB   r<   r   r@   rU   cancelr   r?   r   rL   r   r
   r
   r   _signal_and_wait  s   
.z"SupervisedProcess._signal_and_waitc                    s,   | j rtd| j d| tjI dH S )zz
        Send SIGTERM to process & reap it.

        Might take a while if the process catches & ignores SIGTERM.
        rM   rN   N)r@   r   r:   rY   r*   r3   r   r
   r
   r   	terminate  s   zSupervisedProcess.terminatec                    s4   | j rtd| j d| j }| |I dH S )z3
        Send SIGKILL to process & reap it
        rM   rN   N)r@   r   r:   r<   r   rY   r   r
   r
   r   kill*  s   
zSupervisedProcess.killc                    s   t   }d}	 t   | | jkrdS | js| jsdS zt| | dI dH }W n tjy5   d}Y nw t   | }| dd|||d||| |rNdS t	|I dH  d	| }t   | || j krp|| j t    d }q)
z4
        Wait for process to become 'ready'
        g{Gz?TFr5   Nz
ready-waitz1Readyness: {} after {} seconds, next check in {}s)	wait_timereadyZelapsed_time   )
timer8   r@   r<   r'   wait_forr7   TimeoutErrorrK   r2   )r   
start_timer\   Zis_readyZcur_timer
   r
   r   r]   5  s:   
zSupervisedProcess.readyc                 C      | j jS r   )r<   r!   r   r
   r
   r   r!   o     zSupervisedProcess.pidc                 C   rc   r   )r<   r#   r   r
   r
   r   r#   s  rd   zSupervisedProcess.returncoder   )r   r   r   r   rK   rL   r   rT   rY   rZ   r[   r]   r$   r!   r#   r
   r
   r
   r   r4      s$    
$	+:
r4   )r	   r'   r=   r*   r.   rQ   r_   Zatexitasyncr   r   	Exceptionr   r   r&   r-   r4   r
   r
   r
   r   <module>   s    2'