o
    Þ­jô;  ã                !   @   s¤  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rxd dlm&Z& dZ'e (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(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+d,„Z/ed-ƒZ0d.e*d'ee* fd/d0„Z1	dKdee*edef f dd1deedf deee*ef  d2eee#  d'd3fd4d5„Z2dee*edef f d'd1fd6d7„Z3G d8d3„ d3ƒZ4d9e*d:ee* deedf dee*ef d'df
d;d<„Z5d=e*d'efd>d?„Z6d'ee* fd@dA„Z7e j8d:ee* d'edB fdCdD„ƒZ9dEe-dFed'dfdGdH„Z:dLdIdJ„Z;dS )Mé    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              
   G   sº   |dkrt | ƒ}t d| |¡ tƒ  t| |||ƒ}d}|r't d|¡ t|ƒ z0t|||||	d||dœŽD ]}|o<||ƒ |j|
|d� t| ||||ƒ}|d7 }q5W | ¡  |S | ¡  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Úchangesr   r   úK/var/www/html/CropPilot/venv/lib/python3.10/site-packages/watchfiles/run.pyr      s:   `ø

€
ó
ÿ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}|r6t d|¡ t 	|¡I dH  t
|||||	|
|dœŽ2 z43 dH W }|durZ||ƒ}| |¡rZ|I dH  tj |j¡I dH  tj t| ||||¡I dH }|d7 }qB6 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   r3   r(   r4   ÚanyioÚ	to_threadÚrun_syncr5   r   r   Úisawaitabler6   )r.   r    r!   r"   r#   r$   r%   r&   r'   r(   r+   r,   r-   r;   r7   r8   r9   Úrr   r   r:   r   �   s<   €-ù	


ðr   ÚspawnÚcmdc                 C   s(   dd l }| ¡ j ¡ dk}tj| |d�S )Nr   Úwindows)Úposix)ÚplatformÚunameÚsystemÚlowerÚshlexÚsplit)rB   rE   rD   r   r   r:   Ú	split_cmdò   s   rK   zLiteral['function', 'command']r9   ÚCombinedProcessc           	      C   sÀ   |d u rd}n
t  dd„ |D ƒ¡}|tjd< |dkrA|pi }t| tƒr/| tƒ ||f}t}i }n| }tj	|||d�}| 
¡  t|ƒS |sE|rJt d¡ t| tƒsSJ dƒ‚t| ƒ}t |¡}t|ƒS )	Nz[]c                 S   s   g | ]
\}}|  ¡ |g‘qS r   )Úraw_str)Ú.0ÚcÚpr   r   r:   Ú
<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Ústartr3   ÚwarningrK   Ú
subprocessÚPopenrL   )	r.   r"   r    r!   r9   Úchanges_env_varÚtarget_r7   Ú
popen_argsr   r   r:   r5   ù   s(   

ú

r5   c                 C   s0   t | tƒsdS |  d¡rdS t 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'`
    rS   )z.pyz.shÚcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+)rX   rY   ÚendswithÚreÚ	fullmatch)r.   r   r   r:   r     s   

r   c                   @   sv   e Zd Zddd„Zddeded	d
fdd„Zd	efdd„Zed	efdd„ƒZ	ded	d
fdd„Z
ed	ee fdd„ƒZd
S )rL   rP   ú,Union[SpawnProcess, subprocess.Popen[bytes]]c                 C   s   || _ | jd usJ dƒ‚d S )Nzprocess not yet spawned©Ú_pÚpid)ÚselfrP   r   r   r:   Ú__init__>  s   zCombinedProcess.__init__r   r   r)   r*   r/   Nc                 C   s²   t j dd ¡ |  ¡ rPt d¡ t  | jtj	¡ z|  
|¡ W n tjy/   t d|¡ Y nw | jd u rIt d¡ t  | jtj¡ |  
|¡ d S t d¡ d S t d| j¡ d S )NrR   zstopping process...z!SIGINT timed out after %r secondsz+process has not terminated, sending SIGKILLzprocess stoppedz#process already dead, exit code: %d)rV   rW   ÚpopÚis_aliver3   r(   Úkillrl   ÚsignalÚSIGINTÚjoinr`   ÚTimeoutExpiredr_   ÚexitcodeÚSIGKILL)rm   r)   r*   r   r   r:   r6   B  s    
ü

zCombinedProcess.stopc                 C   s$   t | jtƒr| j ¡ S | j ¡ d u S ©N)rX   rk   r   rp   Úpoll©rm   r   r   r:   rp   Z  s   
zCombinedProcess.is_alivec                 C   s   | j jS rx   rj   rz   r   r   r:   rl   `  s   zCombinedProcess.pidÚtimeoutc                 C   s,   t | jtƒr| j |¡ d S | j |¡ d S rx   )rX   rk   r   rt   Úwait)rm   r{   r   r   r:   rt   e  s   zCombinedProcess.joinc                 C   s   t | jtƒr
| jjS | jjS rx   )rX   rk   r   rv   Ú
returncoderz   r   r   r:   rv   k  s   zCombinedProcess.exitcode)rP   ri   )r   r   )Ú__name__Ú
__module__Ú__qualname__rn   Úintr6   Úboolrp   Úpropertyrl   rt   r   rv   r   r   r   r:   rL   =  s    
rS   Útty_pathc                 C   sD   t |ƒ� t| ƒ}||i |¤Ž W d   ƒ d S 1 sw   Y  d S rx   )Úset_ttyr   )rS   r„   r    r!   Úfuncr   r   r:   r[   s  s   
"þr[   Údotted_pathc              
   C   sŒ   z|   d¡ dd¡\}}W n ty" } z	td| › d�ƒ|‚d}~ww t|ƒ}zt||ƒW S  tyE } ztd|› d|› d	�ƒ|‚d}~w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Úmoduler   r   r:   r   y  s   €ÿ€ÿr   c                   C   s:   z	t  tj ¡ ¡W S  ty   Y dS  ty   Y dS w )zr
    Return the path to the current TTY, if any.

    Virtually impossible to test in pytest, hence no cover.
    z/dev/ttyN)rV   ÚttynameÚsysÚstdinÚfilenoÚOSErrorr�   r   r   r   r:   rZ   Š  s   þrZ   )NNNc                 c   sj   � | r0zt | ƒ�}|t_d V  W d   ƒ W d S 1 sw   Y  W d S  ty/   d V  Y d S w d V  d S rx   )Úopenr–   r—   r™   )r„   Úttyr   r   r:   r…   š  s   €
&þþ
r…   ÚsignumÚ_framec                 C   s   t  dt | ¡¡ t‚)Nz-received signal %s, raising KeyboardInterrupt)r3   r_   rr   ÚSignalsÚKeyboardInterrupt)rœ   r�   r   r   r:   Ú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)r3   r(   rV   Úgetpidrr   ÚSIGTERMr    r   r   r   r:   r4   ®  s   r4   rx   )r/   N)<Ú
contextlibrT   ÚloggingrV   rg   rI   rr   r`   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__Ú	getLoggerr3   rY   r‚   Úfloatr�   r   r   r\   rK   r5   r   rL   r[   r   rZ   Úcontextmanagerr…   r    r4   r   r   r   r:   Ú<module>   s   0
ñ
ÿþ
ýüûúùø	÷
öõôóòñ
ð ó
ÿþ
ýüûúùø	÷
öõôó
òRûÿþ
ýü
û
ú"$ 26