
    69j;              !          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
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 d dl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jF                  d      Z$dddd e       d ddddd	ddddee%z  de%e
def   z  de&edf   de'e%ef   dz  ddde
e(e   gdf   dz  de
ee%ge)f   dz  d e*d!e+d"e+d#e)dz  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%z  de%e
def   z  de&edf   de'e%ef   dz  ddde
e(e   gef   dz  de
ee%ge)f   dz  d e*d!e+d"e+d#e)dz  d&e)d'e)d(e+fd+Z- ed,      Z.d-e%d(e/e%   fd.Z0	 dAde%e
def   z  dd/de&edf   de'e%ef   dz  d0e(e   dz  d(d1fd2Z1de%e
def   z  d(d/fd3Z2 G d4 d1      Z3d5e%d6e%dz  de&edf   de'e%ef   d(df
d7Z4d8e%d(efd9Z5d(e%dz  fd:Z6e jn                  d6e%dz  d(ed;   fd<       Z8d=e+d>ed(dfd?Z9dBd@Z:y)C    N)Callable	Generator)import_module)get_context)SpawnProcess)Path)sleep)TYPE_CHECKINGAny   )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                     N/media/conek/DATA/Code/OCR/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   r,   r-   )r   r!   r"   r#   r&   r'   r   )inspectr   r/   r#   r0   anyio	to_threadrun_syncr1   r	   r   isawaitabler2   )r)   r   r   r   r   r   r    r!   r"   r#   r&   r'   r(   r8   r3   r4   r5   rs                     r6   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)r?   rC   rB   s      r6   	split_cmdrI      s6    NN##))+y8E;;s%((    zLiteral['function', 'command']r5   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startr/   warningrI   
subprocessPopenrK   )r)   r   r   r   r5   changes_env_varcptarget_r3   
popen_argss              r6   r1   r1      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'`
    rN   )z.pyz.shcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+)rT   rU   endswithre	fullmatch)r)   s    r6   r   r     s5    , fc"		(	8&	ArJ   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dz  fd       Zy)rK   c                 8    || _         | j                  J d       y )Nzprocess not yet spawned_ppid)selfr`   s     r6   __init__zCombinedProcess.__init__?  s    xx#>%>>#rJ   r$   r%   r*   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)NrM   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_aliver/   r#   killrl   signalSIGINTjoinr\   TimeoutExpiredr[   exitcodeSIGKILL)rm   r$   r%   s      r6   r2   zCombinedProcess.stopC  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)rT   rk   r   rq   pollrm   s    r6   rq   zCombinedProcess.is_alive[  s8    dgg|,77##%%77<<>T))rJ   c                 .    | j                   j                  S rz   rj   r|   s    r6   rl   zCombinedProcess.pida  s     ww{{rJ   timeoutc                     t        | j                  t              r| j                  j                  |       y | j                  j	                  |       y rz   )rT   rk   r   ru   wait)rm   r~   s     r6   ru   zCombinedProcess.joinf  s0    dgg|,GGLL!GGLL!rJ   c                     t        | j                  t              r| j                  j                  S | j                  j                  S rz   )rT   rk   r   rw   
returncoder|   s    r6   rw   zCombinedProcess.exitcodel  s0    dgg|,77###77%%%rJ   )r`   z&SpawnProcess | subprocess.Popen[bytes])r   r   )__name__
__module____qualname__rn   intr2   boolrq   propertyrl   ru   rw   r   rJ   r6   rK   rK   >  s~    ?Q3 QS Q Q0*$ * S  "C "D " &#* & &rJ   rN   tty_pathc                 j    t        |      5  t        |       } ||i | d d d        y # 1 sw Y   y xY wrz   )set_ttyr   )rN   r   r   r   funcs        r6   rW   rW   t  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        r6   r   r   z  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)rR   ttynamesysstdinfilenoOSErrorr   r   rJ   r6   rV   rV     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rz   )openr   r   r   )r   ttys     r6   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)r/   r[   rs   SignalsKeyboardInterrupt)r   r   s     r6   raise_keyboard_interruptr     s!    
NNBFNNSYDZ[
rJ   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)r/   r#   rR   getpidrs   SIGTERMr   r   rJ   r6   r0   r0     s,     LLKRYY[Y
MM&..":;rJ   rz   )r*   N);
contextlibrO   loggingrR   rf   rG   rs   r\   r   collections.abcr   r   	importlibr   multiprocessingr   multiprocessing.contextr   pathlibr   timer	   typingr
   r   r9   filtersr   mainr   r   r   r   r   __all__	getLoggerr/   rU   tupledictsetr   floatr   r   r   rX   listrI   r1   r   rK   rW   r   rV   contextmanagerr   r   r0   r   rJ   r6   <module>r      s       	 	    
 / # ' 0   %  " 3 3
N			,	- $(<B9=9F%*}3J}(38$$} S/} cNT!	}
 :} J($./$6} FC=$./$6} } } } $;} } } } #}  	!}F $(<B8<9F%*M3JM(38$$M S/M cNT!	M
 :M J(#-.5M FC=$./$6M M M M $;M M #M 	Md G$)3 )49 ) '+!$(38$$!$1!$ S/!$ cNT!	!$
 _t#!$ !$HsXc3h%77 <\ @3& 3&l3 #* E#s(O UYZ]_bZbUc hl gs gs g"cDj   cDj Y/?%@  S # $ 
<rJ   