
    2'&h;              !       0   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rd dlm&Z& dZ' ej(        d          Z)dddd e             d dddddddddeee*f         dee*edef         f         deedf         deee*ef                  dddeeee#         gdf                  deee"e*ge+f                  de,d e-d!e-d"ee+         d#e-d$e-d%e+d&e+d'e-f d(Z.dddd e             d dddddd)deee*f         dee*edef         f         deedf         deee*ef                  dddeeee#         gef                  deee"e*ge+f                  de,d e-d!e-d"ee+         d%e+d&e+d'e-fd*Z/ ed+          Z0d,e*d'ee*         fd-Z1	 d@dee*edef         f         dd.deedf         deee*ef                  d/eee#                  d'd0fd1Z2dee*edef         f         d'd.fd2Z3 G d3 d0          Z4d4e*d5ee*         deedf         dee*ef         d'df
d6Z5d7e*d'efd8Z6d'ee*         fd9Z7e j8        d5ee*         d'ed:         fd;            Z9d<e-d=ed'dfd>Z:dAd?Z;dS )B    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      TF)argskwargstarget_typecallbackwatch_filtergrace_perioddebouncestepdebugsigint_timeoutsigkill_timeout	recursiveignore_permission_deniedpathstarget.r!   r"   r#   z&Literal['function', 'command', 'auto']r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   returnc                    |dk    rt          |           }t                              d| |           t                       t	          | |||          }d}|r*t                              d|           t          |           	 t          |||||	d||dD ]>}|o
 ||           |                    |
|           t	          | ||||          }|dz  }?	 |                                 n# |                                 w xY w|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)   raise_interruptr,   r-   )r*   r+   r   )r   loggerr)   catch_sigtermstart_processr   r   stop)r/   r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   processreloadschangess                     I/var/www/html/reinick/venv/lib/python3.11/site-packages/watchfiles/run.pyr   r      s3   @ f(00
LL%v{;;;OOOFKv>>GG JLYYYl%!%=	
 	
 	
 	 	G *'**LLLXXX#FKvwOOGqLGG	 	Ns   AC( (C>)r!   r"   r#   r$   r%   r&   r'   r(   r)   r,   r-   c           
        K   ddl }|dk    rt          |           }t                              d| |           t	                       t
          j                            t          | |||           d{V }d}|r5t                              d|           t          j	        |           d{V  t          |||||	|
|d2 3 d{V }|( ||          }|                    |          r| d{V  t
          j                            |j                   d{V  t
          j                            t          | ||||           d{V }|dz  }6 t
          j                            |j                   d{V  |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   r2   r3   )r%   r'   r(   r)   r,   r-   r   )inspectr   r5   r)   r6   anyio	to_threadrun_syncr7   r   r   isawaitabler8   )r/   r!   r"   r#   r$   r%   r&   r'   r(   r)   r,   r-   r.   r>   r9   r:   r;   rs                     r<   r   r      s     Z NNNf(00
LL%v{;;;OOOO,,]FKQUW]^^^^^^^^GG (JLYYYk,'''''''''	!!9         g !!A""1%% o&&w|44444444400UY[acjkkkkkkkk1!" /
"
"7<
0
00000000Ns   >Espawncmdc                     dd l }|                                j                                        dk    }t	          j        | |          S )Nr   windows)posix)platformunamesystemlowershlexsplit)rE   rI   rH   s      r<   	split_cmdrO      sC    OOONN#))++y8E;s%((((    zLiteral['function', 'command']r;   CombinedProcessc                 "   |d}nt          j        d |D                       }|t          j        d<   |dk    ri|pi }t	          | t
                    r| t                      ||f}t          }i }n| }t          	                    |||          }|
                                 n`|s|rt                              d           t	          | t
                    s
J d            t          |           }t          j        |          }t!          |          S )Nz[]c                 @    g | ]\  }}|                                 |gS r   )raw_str).0cps      r<   
<listcomp>z!start_process.<locals>.<listcomp>  s)    %K%K%K41aqyy{{A&6%K%K%KrP   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startr5   warningrO   
subprocessPopenrQ   )	r/   r#   r!   r"   r;   changes_env_vartarget_r9   
popen_argss	            r<   r7   r7      s    *%K%K7%K%K%KLL'6BJ#$ j  2fc"" 	<>>47D"GFFG''wT&'QQ 	L6 	LNNJKKK&#&&UU(UUUUv&&
":..7###rP   c                     t          | t                    sdS |                     d          rdS t          j        d|           r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'`
    rZ   )z.pyz.shcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+)r_   r`   endswithre	fullmatch)r/   s    r<   r   r     sS    , fc"" z		(	( y	8&	A	A zyrP   c                       e Zd ZddZddededd	fd
ZdefdZedefd            Z	dedd	fdZ
edee         fd            Zd	S )rQ   rW   ,Union[SpawnProcess, subprocess.Popen[bytes]]c                 6    || _         | j        
J d            d S )Nzprocess not yet spawned_ppid)selfrW   s     r<   __init__zCombinedProcess.__init__>  s&    x##%>#####rP   r    r   r*   r+   r0   Nc                    t           j                            dd            |                                 rt                              d           t          j        | j        t          j	                   	 | 
                    |           n0# t          j        $ r t                              d|           Y nw xY w| j        Ut                              d           t          j        | j        t          j                   | 
                    |           d S t                              d           d S t                              d| j                   d S )NrY   zstopping process...z!SIGINT timed out after %r secondsz+process has not terminated, sending SIGKILLzprocess stoppedz#process already dead, exit code: %d)r]   r^   popis_aliver5   r)   killrv   signalSIGINTjoinrg   TimeoutExpiredrf   exitcodeSIGKILL)rw   r*   r+   s      r<   r8   zCombinedProcess.stopB  s(   

+T222==?? 	QLL.///GDHfm,,,		.)))),    BNSSS	 }$LMMM&.111		/*****./////NN@$-PPPPPs   4B
 
*B76B7c                     t          | j        t                    r| j                                        S | j                                        d u S N)r_   ru   r   r{   pollrw   s    r<   r{   zCombinedProcess.is_aliveZ  s?    dg|,, 	*7##%%%7<<>>T))rP   c                     | j         j        S r   rt   r   s    r<   rv   zCombinedProcess.pid`  s     w{rP   timeoutc                     t          | j        t                    r| j                            |           d S | j                            |           d S r   )r_   ru   r   r   wait)rw   r   s     r<   r   zCombinedProcess.joine  sJ    dg|,, 	"GLL!!!!!GLL!!!!!rP   c                 f    t          | j        t                    r| j        j        S | j        j        S r   )r_   ru   r   r   
returncoder   s    r<   r   zCombinedProcess.exitcodek  s,    dg|,, 	&7##7%%rP   )rW   rr   )r    r   )__name__
__module____qualname__rx   intr8   boolr{   propertyrv   r   r   r   r   rP   r<   rQ   rQ   =  s        ? ? ? ?Q Q3 QS Q Q Q Q Q0*$ * * * * S    X"C "D " " " " &(3- & & & X& & &rP   rZ   tty_pathc                     t          |          5  t          |           } ||i | d d d            d S # 1 swxY w Y   d S r   )set_ttyr   )rZ   r   r!   r"   funcs        r<   rb   rb   s  s    			  X&&df                 s   599dotted_pathc                 <   	 |                      d                              dd          \  }}n&# t          $ r}t          d|  d          |d}~ww xY wt	          |          }	 t          ||          S # t          $ r}t          d| d| d	          |d}~ww xY w)
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modules        r<   r   r   y  s    
V"-"3"3C"8"8"?"?Q"G"GZZ V V VMkMMMNNTUUV ;''Fgvz*** g g g^[^^z^^^__effgs,   ,/ 
AAA%A5 5
B?BBc                      	 t          j        t          j                                                  S # t
          $ r Y dS t          $ r Y dS w xY w)zr
    Return the path to the current TTY, if any.

    Virtually impossible to test in pytest, hence no cover.
    z/dev/ttyN)r]   ttynamesysstdinfilenoOSErrorr   r   rP   r<   ra   ra     s`    z#)**,,---   zz   tts   /2 
A	AA)NNNc              #      K   | rP	 t          |           5 }|t          _        d V  d d d            d S # 1 swxY w Y   d S # t          $ r d V  Y d S w xY wd V  d S r   )openr   r   r   )r   ttys     r<   r   r     s       
	h 3	                   	 	 	EEEEEE	
 	s,   A  3A  7A  7A   AAsignum_framec                 j    t                               dt          j        |                      t          )Nz-received signal %s, raising KeyboardInterrupt)r5   rf   r}   SignalsKeyboardInterrupt)r   r   s     r<   raise_keyboard_interruptr     s(    
NNBFNSYDZDZ[[[
rP   c                      t                               dt          j                               t	          j        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)r5   r)   r]   getpidr}   SIGTERMr   r   rP   r<   r6   r6     s:     LLKRY[[YYY
M&.":;;;;;rP   r   )r0   N)<
contextlibr[   loggingr]   ro   rM   r}   rg   r   	importlibr   multiprocessingr   multiprocessing.contextr   pathlibr   timer   typingr   r	   r
   r   r   r   r   r   r   r   r?   filtersr   mainr   r   r   r   r   __all__	getLoggerr5   r`   r   floatr   r   r   rc   rO   r7   r   rQ   rb   r   ra   contextmanagerr   r   r6   r   rP   r<   <module>r      sI         				 				       



 # # # # # # ' ' ' ' ' ' 0 0 0 0 0 0             c c c c c c c c c c c c c c c c c c c c c c c c  " " " " " " 3 3 3 3 3 3 3 3 3 3 3 3 
N		,	-	- '+<B<@<IMOO %*} } }$)}#xS))*} S/} T#s(^$	}
 :} xZ 14 789} 8VSM4$789} } } } D>} } } } #}  	!} } } }F '+<B;?<IMOO %*M M M$)M#xS))*M S/M T#s(^$	M
 :M xZ 13 678M 8VSM4$789M M M M D>M M #M 	M M M Md G$$)3 )49 ) ) ) ) *.!$ !$#xS))*!$1!$ S/!$ T#s(^$	!$
 c*o&!$ !$ !$ !$ !$HuS(38*<%<= Bb    @3& 3& 3& 3& 3& 3& 3& 3&l3 (3- uS#X X\]`be]eXf ko    gs gs g g g g"hsm      hsm 	2B(C    S # $    
< < < < < <rP   