
    ,h;              !       .   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jP                  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 jp                  d5ee*   d'ed:   fd;       Z9d<e-d=ed'dfd>Z:dAd?Z;y)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        j                  d| |       t                t	        | |||      }d}|r!t        j                  d|       t        |       	 t        |||||	d||dD ]5  }|xr  ||       |j                  |
|       t	        | ||||      }|dz  }7 	 |j                          |S # |j                          w xY w)	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                     P/var/www/html/Resume-Scraper/venv/lib/python3.12/site-packages/watchfiles/run.pyr   r      s    @ f(0
LL%v{;OFKv>GGJLYl%!%=	
 	G *'*LLLX#FKvwOGqLG	 	N 	s   &A	C C)r!   r"   r#   r$   r%   r&   r'   r(   r)   r,   r-   c           
        K   ddl }|dk(  rt        |       }t        j                  d| |       t	                t
        j                  j                  t        | |||       d{   }d}|r3t        j                  d|       t        j                  |       d{    t        |||||	|
|d2 3 d{   }|# ||      }|j                  |      r
| d{    t
        j                  j                  |j                         d{    t
        j                  j                  t        | ||||       d{   }|dz  }7 7 7 7 q7 B7 6 t
        j                  j                  |j                         d{  7   |S w)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 f(0
LL%v{;OOO,,]FKQUW]^^GGJLYkk,'''	!!9  g !A""1%oo&&w||44400UY[acjkk1/ _
 	( 4k" //
"
"7<<
000Ns   A F"E#6FEF.E2E3E6!FE	0FE	/F8E9FFE	FFF-F<E?=Fspawncmdc                     dd l }|j                         j                  j                         dk7  }t	        j
                  | |      S )Nr   windows)posix)platformunamesystemlowershlexsplit)rE   rI   rH   s      r<   	split_cmdrO      s6    NN##))+y8E;;s%((    zLiteral['function', 'command']r;   CombinedProcessc                 2   |d}n8t        j                  |D cg c]  \  }}|j                         |g c}}      }|t        j                  d<   |dk(  rb|xs i }t        | t              r| t               ||f}t        }i }n| }t        j                  |||      }	|	j                          t#        |	      S |s|rt        j                  d       t        | t              sJ d       t        |       }
t        j                   |
      }	t#        |	      S c c}}w )Nz[]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raw_strosenviron
isinstancestrget_tty_pathrun_functionspawn_contextProcessstartr5   warningrO   
subprocessPopenrQ   )r/   r#   r!   r"   r;   changes_env_varcptarget_r9   
popen_argss              r<   r7   r7      s     **7%K41aqyy{A&6%KL'6BJJ#$ j 2fc"<>47D"GFG''wT&'Q 7## 6NNJK&#&U(UUv&
"":.7##/ &Ls   D
c                 x    t        | t              sy| j                  d      ryt        j                  d|       ryy)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'`
    rT   )z.pyz.shcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+)rZ   r[   endswithre	fullmatch)r/   s    r<   r   r     s5    , fc"		(	8&	ArP   c                   x    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y)rQ   c                 8    || _         | j                  J d       y )Nzprocess not yet spawned_ppid)selfrf   s     r<   __init__zCombinedProcess.__init__>  s    xx#>%>>#rP   r*   r+   r0   Nc                    t         j                  j                  dd        | j                         rt        j                  d       t        j                  | j                  t        j                         	 | j                  |       | j                  Ut        j                  d       t        j                  | j                  t        j                         | j                  |       y t        j                  d       y t        j                  d| j                         y # t        j                  $ r t        j                  d|       Y w xY w)NrS   zstopping process...z!SIGINT timed out after %r secondsz+process has not terminated, sending SIGKILLzprocess stoppedz#process already dead, exit code: %d)rX   rY   popis_aliver5   r)   killrr   signalSIGINTjoinrb   TimeoutExpiredra   exitcodeSIGKILL)rs   r*   r+   s      r<   r8   zCombinedProcess.stopB  s    


+T2==?LL./GGDHHfmm,		.) }}$LM&..1		/*./NN@$--P ,,  BNS	s   5D )E
	E
c                     t        | j                  t              r| j                  j                         S | j                  j	                         d u S N)rZ   rq   r   rw   pollrs   s    r<   rw   zCombinedProcess.is_aliveZ  s8    dgg|,77##%%77<<>T))rP   c                 .    | j                   j                  S r   rp   r   s    r<   rr   zCombinedProcess.pid`  s     ww{{rP   timeoutc                     t        | j                  t              r| j                  j                  |       y | j                  j	                  |       y r   )rZ   rq   r   r{   wait)rs   r   s     r<   r{   zCombinedProcess.joine  s0    dgg|,GGLL!GGLL!rP   c                     t        | j                  t              r| j                  j                  S | j                  j                  S r   )rZ   rq   r   r}   
returncoder   s    r<   r}   zCombinedProcess.exitcodek  s0    dgg|,77###77%%%rP   )rf   z,Union[SpawnProcess, subprocess.Popen[bytes]])r    r   )__name__
__module____qualname__rt   intr8   boolrw   propertyrr   r{   r   r}   r   rP   r<   rQ   rQ   =  s~    ?Q3 QS Q Q0*$ * S  "C "D " &(3- & &rP   rT   tty_pathc                 j    t        |      5  t        |       } ||i | d d d        y # 1 sw Y   y xY wr   )set_ttyr   )rT   r   r!   r"   funcs        r<   r]   r]   s  s6    		 X&df  s   )2dotted_pathc                    	 | j                  d      j                  dd      \  }}t	        |      }	 t        ||      S # t        $ r}t        d|  d      |d}~ww xY w# 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"?"?Q"GZ ;'Fgvz**  VAk]*LMNTUUV  gH[M1FzlR]^_effgs,   $> A 	AAA	B (A;;B c                      	 t        j                  t        j                  j	                               S # t
        $ r Y yt        $ r Y y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)rX   ttynamesysstdinfilenoOSErrorr   r   rP   r<   r\   r\     s@    zz#))**,--  s   03 	A	A	A	)NNNc              #      K   | r&	 t        |       5 }|t        _        d  d d d        y y d  y # 1 sw Y   xY w# t        $ r d  Y y w xY wwr   )openr   r   r   )r   ttys     r<   r   r     sR     	h 3	  	   		s5   A< 0< A9< A	AAAsignum_framec                 `    t         j                  dt        j                  |              t        )Nz-received signal %s, raising KeyboardInterrupt)r5   ra   ry   SignalsKeyboardInterrupt)r   r   s     r<   raise_keyboard_interruptr     s!    
NNBFNNSYDZ[
rP   c                      t         j                  dt        j                                t	        j                  t        j
                  t               y)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)   rX   getpidry   SIGTERMr   r   rP   r<   r6   r6     s,     LLKRYY[Y
MM&..":;rP   r   )r0   N)<
contextlibrU   loggingrX   rl   rM   ry   rb   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   r^   rO   r7   r   rQ   r]   r   r\   contextmanagerr   r   r6   r   rP   r<   <module>r      sA      	 	    
 # ' 0   c c c  " 3 3
N			,	- '+<B<@<IO %*}$)}#xS))*} S/} T#s(^$	}
 :} xZ 14 789} 8VSM4$789} } } } D>} } } } #}  	!}F '+<B;?<IO %*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d G$)3 )49 ) *.!$#xS))*!$1!$ S/!$ T#s(^$	!$
 c*o&!$ !$HuS(38*<%<= Bb @3& 3&l3 (3- uS#X X\]`be]eXf ko gs gs g"hsm   hsm 	2B(C  S # $ 
<rP   