U
    ={f;                     @   sH  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Zd dlZd dlZd dl	m
Z
 d 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mZmZmZmZmZmZmZmZ d dlZdd	lm Z  dd
l!m"Z"m#Z#m$Z$m%Z% erd dlm&Z& dZ'e(dZ)dddde  d ddddddddeee*f ee*edef f eedf eee*ef  deeee# gdf  eee"e*ge+f  e,e-e-e+e-e-e+e+e-dddZ.dddde  d ddddddeee*f ee*edef f eedf eee*ef  deeee# gef  eee"e*ge+f  e,e-e-e+e+e+e-dddZ/edZ0e*ee* d d!d"Z1d>ee*edef f d#eedf eee*ef  eee#  d$d%d&d'Z2ee*edef f d#d(d)d*Z3G d+d$ d$Z4e*ee* eedf ee*ef dd,d-d.Z5e*ed/d0d1Z6ee* d2d3d4Z7e j8ee* ed5 d6d7d8Z9e-edd9d:d;Z:dd2d<d=Z;dS )?    N)import_module)get_context)SpawnProcess)Path)sleep)
TYPE_CHECKINGAnyCallableDict	GeneratorListOptionalSetTupleUnion   )DefaultFilter)Change
FileChangeawatchwatch)Literal)run_processarun_processdetect_target_typeimport_stringzwatchfiles.main autoi@  2   F   T)argskwargstarget_typecallbackwatch_filtergrace_perioddebouncestepdebugsigint_timeoutsigkill_timeout	recursiveignore_permission_denied.z&Literal['function', 'command', 'auto'])pathstargetr    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   returnc              
   G   s   |dkrt | }td| | t  t| |||}d}|rNtd| t| zVt|||||	d||dD ]6}|ox|| |j|
|d t| ||||}|d7 }qjW 5 |  X |S )	u  
    Run a process and restart it upon file changes.

    `run_process` can work in two ways:

    * Using `multiprocessing.Process` † to run a python function
    * Or, using `subprocess.Popen` to run a command

    !!! note

        **†** technically `multiprocessing.get_context('spawn').Process` to avoid forking and improve
        code reload/import.

    Internally, `run_process` uses [`watch`][watchfiles.watch] with `raise_interrupt=False` so the function
    exits cleanly upon `Ctrl+C`.

    Args:
        *paths: matches the same argument of [`watch`][watchfiles.watch]
        target: function or command to run
        args: arguments to pass to `target`, only used if `target` is a function
        kwargs: keyword arguments to pass to `target`, only used if `target` is a function
        target_type: type of target. Can be `'function'`, `'command'`, or `'auto'` in which case
            [`detect_target_type`][watchfiles.run.detect_target_type] is used to determine the type.
        callback: function to call on each reload, the function should accept a set of changes as the sole argument
        watch_filter: matches the same argument of [`watch`][watchfiles.watch]
        grace_period: number of seconds after the process is started before watching for changes
        debounce: matches the same argument of [`watch`][watchfiles.watch]
        step: matches the same argument of [`watch`][watchfiles.watch]
        debug: matches the same argument of [`watch`][watchfiles.watch]
        sigint_timeout: the number of seconds to wait after sending sigint before sending sigkill
        sigkill_timeout: the number of seconds to wait after sending sigkill before raising an exception
        recursive: matches the same argument of [`watch`][watchfiles.watch]

    Returns:
        number of times the function was reloaded.

    ```py title="Example of run_process running a function"
    from watchfiles import run_process

    def callback(changes):
        print('changes detected:', changes)

    def foobar(a, b):
        print('foobar called with:', a, b)

    if __name__ == '__main__':
        run_process('./path/to/dir', target=foobar, args=(1, 2), callback=callback)
    ```

    As well as using a `callback` function, changes can be accessed from within the target function,
    using the `WATCHFILES_CHANGES` environment variable.

    ```py title="Example of run_process accessing changes"
    from watchfiles import run_process

    def foobar(a, b, c):
        # changes will be an empty list "[]" the first time the function is called
        changes = os.getenv('WATCHFILES_CHANGES')
        changes = json.loads(changes)
        print('foobar called due to changes:', changes)

    if __name__ == '__main__':
        run_process('./path/to/dir', target=foobar, args=(1, 2, 3))
    ```

    Again with the target as `command`, `WATCHFILES_CHANGES` can be used
    to access changes.

    ```bash title="example.sh"
    echo "changers: ${WATCHFILES_CHANGES}"
    ```

    ```py title="Example of run_process running a command"
    from watchfiles import run_process

    if __name__ == '__main__':
        run_process('.', target='./example.sh')
    ```
    r   running "%s" as %sr   3sleeping for %s seconds before watching for changesF)r$   r&   r'   r(   Zraise_interruptr+   r,   )r)   r*   r   )r   loggerr(   catch_sigtermstart_processr   stopr   )r.   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   processreloadschangesr   r   M/var/www/html/Darija-Ai-API/env/lib/python3.8/site-packages/watchfiles/run.pyr      s4    `

r   )r    r!   r"   r#   r$   r%   r&   r'   r(   r+   r,   )r-   r.   r    r!   r"   r#   r$   r%   r&   r'   r(   r+   r,   r/   c              	      s  ddl }|dkrt| }td| | t  tjt| |||I dH }d}|rjtd| t	|I dH  t
|||||	|
|d2 zh3 dH W }|dk	r||}||r|I dH  tj|jI dH  tjt| ||||I dH }|d7 }q6 tj|jI dH  |S )a  
    Async equivalent of [`run_process`][watchfiles.run_process], all arguments match those of `run_process` except
    `callback` which can be a coroutine.

    Starting and stopping the process and watching for changes is done in a separate thread.

    As with `run_process`, internally `arun_process` uses [`awatch`][watchfiles.awatch], however `KeyboardInterrupt`
    cannot be caught and suppressed in `awatch` so these errors need to be caught separately, see below.

    ```py title="Example of arun_process usage"
    import asyncio
    from watchfiles import arun_process

    async def callback(changes):
        await asyncio.sleep(0.1)
        print('changes detected:', changes)

    def foobar(a, b):
        print('foobar called with:', a, b)

    async def main():
        await arun_process('.', target=foobar, args=(1, 2), callback=callback)

    if __name__ == '__main__':
        try:
            asyncio.run(main())
        except KeyboardInterrupt:
            print('stopped via KeyboardInterrupt')
    ```
    r   Nr   r0   r1   )r$   r&   r'   r(   r+   r,   r   )inspectr   r2   r(   r3   anyioZ	to_threadZrun_syncr4   r   r   isawaitabler5   )r.   r    r!   r"   r#   r$   r%   r&   r'   r(   r+   r,   r-   r:   r6   r7   r8   rr   r   r9   r      s8    -	

r   spawn)cmdr/   c                 C   s(   dd l }| j dk}tj| |dS )Nr   windows)posix)platformunamesystemlowershlexsplit)r?   rB   rA   r   r   r9   	split_cmd   s    rH   zLiteral['function', 'command']CombinedProcess)r.   r"   r    r!   r8   r/   c           	      C   s   |d krd}nt dd |D }|tjd< |dkr||p:i }t| tr^| t ||f}t}i }n| }tj	|||d}|
  n6|s|rtd t| tstdt| }t|}t|S )	Nz[]c                 S   s   g | ]\}}|  |gqS r   )Zraw_str).0cpr   r   r9   
<listcomp>  s     z!start_process.<locals>.<listcomp>WATCHFILES_CHANGESfunction)r.   r    r!   z-ignoring args and kwargs for "command" targetz+target must be a string to run as a command)jsondumpsosenviron
isinstancestrget_tty_pathrun_functionspawn_contextProcessstartr2   warningAssertionErrorrH   
subprocessPopenrI   )	r.   r"   r    r!   r8   Zchanges_env_varZtarget_r6   
popen_argsr   r   r9   r4      s&    




r4   )r.   r/   c                 C   s4   t | tsdS | drdS td| r,dS dS dS )a^  
    Used by [`run_process`][watchfiles.run_process], [`arun_process`][watchfiles.arun_process]
    and indirectly the CLI to determine the target type with `target_type` is `auto`.

    Detects the target type - either `function` or `command`. This method is only called with `target_type='auto'`.

    The following logic is employed:

    * If `target` is not a string, it is assumed to be a function
    * If `target` ends with `.py` or `.sh`, it is assumed to be a command
    * Otherwise, the target is assumed to be a function if it matches the regex `[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+`

    If this logic does not work for you, specify the target type explicitly using the `target_type` function argument
    or `--target-type` command line argument.

    Args:
        target: The target value

    Returns:
        either `'function'` or `'command'`
    rO   )z.pyz.shcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+N)rT   rU   endswithre	fullmatch)r.   r   r   r9   r     s    

r   c                   @   st   e Zd ZddddZdeeddd	d
ZedddZeedddZ	eddddZ
eee dddZdS )rI   z,Union[SpawnProcess, subprocess.Popen[bytes]])rL   c                 C   s   || _ | jd k	stdd S )Nzprocess not yet spawned)_ppidr\   )selfrL   r   r   r9   __init__>  s    zCombinedProcess.__init__r   r   N)r)   r*   r/   c                 C   s   t jdd  |  rtd t | jtj	 z| 
| W n" tjk
r`   td| Y nX | jd krtd t | jtj | 
| qtd ntd| j d S )NrN   zstopping process...z!SIGINT timed out after %r secondsz+process has not terminated, sending SIGKILLzprocess stoppedz#process already dead, exit code: %d)rR   rS   popis_aliver2   r(   killre   signalSIGINTjoinr]   TimeoutExpiredr[   exitcodeSIGKILL)rf   r)   r*   r   r   r9   r5   B  s    


zCombinedProcess.stopr/   c                 C   s(   t | jtr| j S | j d kS d S N)rT   rd   r   ri   pollrf   r   r   r9   ri   Z  s    
zCombinedProcess.is_alivec                 C   s   | j jS rr   )rd   re   rt   r   r   r9   re   `  s    zCombinedProcess.pid)timeoutr/   c                 C   s*   t | jtr| j| n| j| d S rr   )rT   rd   r   rm   wait)rf   ru   r   r   r9   rm   e  s    zCombinedProcess.joinc                 C   s    t | jtr| jjS | jjS d S rr   )rT   rd   r   ro   
returncodert   r   r   r9   ro   k  s    zCombinedProcess.exitcode)r   r   )__name__
__module____qualname__rg   intr5   boolri   propertyre   rm   r   ro   r   r   r   r9   rI   =  s   )rO   tty_pathr    r!   r/   c              	   C   s*   t | t| }||| W 5 Q R X d S rr   )set_ttyr   )rO   r~   r    r!   funcr   r   r9   rW   s  s    
rW   )dotted_pathr/   c              
   C   s   z|  ddd\}}W n4 tk
rN } ztd|  d|W 5 d}~X Y nX t|}zt||W S  tk
r } ztd| d| d	|W 5 d}~X Y nX dS )
z
    Stolen approximately from django. Import a dotted module path and return the attribute/class designated by the
    last name in the path. Raise ImportError if the import fails.
     .r   "z!" doesn't look like a module pathNzModule "z" does not define a "z" attribute)striprsplit
ValueErrorImportErrorr   getattrAttributeError)r   module_path
class_nameemoduler   r   r9   r   y  s    $r   rq   c                   C   sB   zt tj W S  tk
r(   Y dS  tk
r<   Y dS X dS )zr
    Return the path to the current TTY, if any.

    Virtually impossible to test in pytest, hence no cover.
    z/dev/ttyN)rR   ttynamesysstdinfilenoOSErrorr   r   r   r   r9   rV     s    rV   )NNN)r~   r/   c              	   c   sP   | rFz$t | }|t_d V  W 5 Q R X W qL tk
rB   d V  Y qLX nd V  d S rr   )openr   r   r   )r~   ttyr   r   r9   r     s    
r   )signum_framer/   c                 C   s   t dt|  td S )Nz-received signal %s, raising KeyboardInterrupt)r2   r[   rk   SignalsKeyboardInterrupt)r   r   r   r   r9   raise_keyboard_interrupt  s    r   c                   C   s"   t dt  ttjt dS )a  
    Catch SIGTERM and raise KeyboardInterrupt instead. This means watchfiles will stop quickly
    on `docker compose stop` and other cases where SIGTERM is sent.

    Without this the watchfiles process will be killed while a running process will continue uninterrupted.
    z8registering handler for SIGTERM on watchfiles process %dN)r2   r(   rR   getpidrk   SIGTERMr   r   r   r   r9   r3     s    r3   )N)<
contextlibrP   loggingrR   rb   rF   rk   r]   r   	importlibr   multiprocessingr   Zmultiprocessing.contextr   pathlibr   timer   typingr   r   r	   r
   r   r   r   r   r   r   r;   filtersr   mainr   r   r   r   r   __all__	getLoggerr2   rU   r|   floatr{   r   r   rX   rH   r4   r   rI   rW   r   rV   contextmanagerr   r   r3   r   r   r   r9   <module>   s   0


 

R 

$  6*