o
    é’OjŒ;  ã                !   @   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
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rpd 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%B de%e
def B de&edf de'e%ef dB ddde
e(e gdf dB de
ee%ge)f dB d e*d!e+d"e+d#e)dB 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%B de%e
def B de&edf de'e%ef dB ddde
e(e gef dB de
ee%ge)f dB d e*d!e+d"e+d#e)dB d&e)d'e)d(e+fd,d-„Z-ed.ƒZ.d/e%d(e/e% fd0d1„Z0	dLde%e
def B dd2de&edf de'e%ef dB d3e(e dB d(d4fd5d6„Z1de%e
def B d(d2fd7d8„Z2G d9d4„ d4ƒZ3d:e%d;e%dB de&edf de'e%ef d(df
d<d=„Z4d>e%d(efd?d@„Z5d(e%dB fdAdB„Z6e j7d;e%dB d(edC fdDdE„ƒZ8dFe+dGed(dfdHdI„Z9dMdJdK„Z:dS )Né    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              
   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   úT/var/www/html/pharmsmart-cdr-gen/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   r*   r+   )r   r    r!   r"   r%   r&   r   )Úinspectr   r-   r"   r.   ÚanyioÚ	to_threadÚrun_syncr/   r   r   Úisawaitabler0   )r(   r   r   r   r   r   r   r    r!   r"   r%   r&   r'   r5   r1   r2   r3   Úrr   r   r4   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)r<   r?   r>   r   r   r4   Ú	split_cmdó   s   rE   zLiteral['function', 'command']r3   Ú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   r4   Ú
<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Ústartr-   ÚwarningrE   Ú
subprocessÚPopenrF   )	r(   r   r   r   r3   Úchanges_env_varÚtarget_r1   Ú
popen_argsr   r   r4   r/   ú   s(   

ú

r/   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'`
    rM   )z.pyz.shÚcommandz[a-zA-Z0-9_]+(\.[a-zA-Z0-9_]+)+)rR   rS   ÚendswithÚreÚ	fullmatch)r(   r   r   r4   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d
B fdd„ƒZd
S )rF   rJ   ú&SpawnProcess | subprocess.Popen[bytes]c                 C   s   || _ | jd usJ dƒ‚d S )Nzprocess not yet spawned©Ú_pÚpid)ÚselfrJ   r   r   r4   Ú__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 )NrL   zstopping process...z!SIGINT timed out after %r secondsz+process has not terminated, sending SIGKILLzprocess stoppedz#process already dead, exit code: %d)rP   rQ   ÚpopÚis_aliver-   r"   Úkillrf   ÚsignalÚSIGINTÚjoinrZ   ÚTimeoutExpiredrY   ÚexitcodeÚSIGKILL)rg   r#   r$   r   r   r4   r0   C  s    
ü

zCombinedProcess.stopc                 C   s$   t | jtƒr| j ¡ S | j ¡ d u S ©N)rR   re   r   rj   Úpoll©rg   r   r   r4   rj   [  s   
zCombinedProcess.is_alivec                 C   s   | j jS rr   rd   rt   r   r   r4   rf   a  s   zCombinedProcess.pidÚtimeoutc                 C   s,   t | jtƒr| j |¡ d S | j |¡ d S rr   )rR   re   r   rn   Úwait)rg   ru   r   r   r4   rn   f  s   zCombinedProcess.joinc                 C   s   t | jtƒr
| jjS | jjS rr   )rR   re   r   rp   Ú
returncodert   r   r   r4   rp   l  s   zCombinedProcess.exitcode)rJ   rc   )r   r   )Ú__name__Ú
__module__Ú__qualname__rh   Úintr0   Úboolrj   Úpropertyrf   rn   rp   r   r   r   r4   rF   >  s    
rM   Útty_pathc                 C   sD   t |ƒ� t| ƒ}||i |¤Ž W d   ƒ d S 1 sw   Y  d S rr   )Úset_ttyr   )rM   r~   r   r   Úfuncr   r   r4   rU   t  s   
"þrU   Ú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   r4   r   z  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)rP   ÚttynameÚsysÚstdinÚfilenoÚOSErrorrŠ   r   r   r   r4   rT   ‹  s   þrT   )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 rr   )Úopenr�   r‘   r“   )r~   Úttyr   r   r4   r   ›  s   €
&þþ
r   ÚsignumÚ_framec                 C   s   t  dt | ¡¡ t‚)Nz-received signal %s, raising KeyboardInterrupt)r-   rY   rl   ÚSignalsÚKeyboardInterrupt)r–   r—   r   r   r4   Ú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)r-   r"   rP   Úgetpidrl   ÚSIGTERMrš   r   r   r   r4   r.   ¯  s   r.   rr   )r)   N);Ú
contextlibrN   ÚloggingrP   ra   rC   rl   rZ   r�   Úcollections.abcr   r   Ú	importlibr   Úmultiprocessingr   Úmultiprocessing.contextr   Úpathlibr   Útimer   Útypingr	   r
   r6   Úfiltersr   Úmainr   r   r   r   r   Ú__all__Ú	getLoggerr-   rS   ÚtupleÚdictÚsetr|   Úfloatr{   r   r   rV   ÚlistrE   r/   r   rF   rU   r   rT   Úcontextmanagerr   rš   r.   r   r   r   r4   Ú<module>   s   
ñÿþ
ýüûúùø	÷
öõôóòñ
ð óÿþ
ýüûúùø	÷
öõôó
òRûÿþ
ýü
û
ú$ 26