B
    ç¹`&;  ã               @   s0  d dl mZ d dlm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mZ ddlmZmZmZmZmZ dd	lmZmZmZ er¼dd
lmZ G dd„ deƒZ G dd„ dƒZ!G dd„ dƒZ"G dd„ deƒZ#ddd„Z$dd„ Z%dd„ Z&deddddddfdd„Z'dS )é    )Údeque)ÚcontextmanagerNé   )ÚWINÚglob)ÚProcessGroup)ÚDefaultLoggerÚSilentLogger)ÚdefaultÚis_stream_interactiveÚis_watchdog_supportedÚis_watchman_supportedÚresolve_spec)ÚWorkerÚget_reloaderÚ	is_active)Úwinapic               @   sF   e Zd ZdZdZddd„Zdd„ Zdd„ Zd	d
„ Zdd„ Z	dd„ Z
dS )ÚFileMonitorProxyz¬
    Wrap an :class:`hupper.interfaces.IFileMonitor` into an object that
    exposes a thread-safe interface back to the reloader to detect
    when it should reload.

    Nc             C   s@   || _ || _tƒ | _dd„ t|p"g ƒD ƒ| _t ¡ | _d| _d S )Nc             S   s   g | ]}t  t |¡¡‘qS © )ÚreÚcompileÚfnmatchÚ	translate)Ú.0Úxr   r   úW/home/kop/projects/devel/pgwui/test_venv/lib/python3.7/site-packages/hupper/reloader.pyú
<listcomp>*   s    z-FileMonitorProxy.__init__.<locals>.<listcomp>F)	ÚcallbackÚloggerÚsetÚchanged_pathsÚignore_filesÚ	threadingÚLockÚlockÚ
is_changed)Úselfr   r   r!   r   r   r   Ú__init__%   s    
zFileMonitorProxy.__init__c                sD   x>t |ddp|gD ](‰ t‡ fdd„| jD ƒƒs| j ˆ ¡ qW d S )NT)Ú	recursivec             3   s   | ]}|  ˆ ¡V  qd S )N)Úmatch)r   r   )Úpr   r   ú	<genexpr>4   s    z,FileMonitorProxy.add_path.<locals>.<genexpr>)r   Úanyr!   ÚmonitorÚadd_path)r&   Úpathr   )r*   r   r.   /   s    zFileMonitorProxy.add_pathc             C   s   | j  ¡  d S )N)r-   Ústart)r&   r   r   r   r0   7   s    zFileMonitorProxy.startc             C   s   | j  ¡  | j  ¡  d S )N)r-   ÚstopÚjoin)r&   r   r   r   r1   :   s    
zFileMonitorProxy.stopc          	   C   sV   | j F || jkrH| j d |¡¡ | j |¡ | jsHd| _|  | j¡ W d Q R X d S )Nz{} changed; reloading ...T)r$   r    r   ÚinfoÚformatÚaddr%   r   )r&   r/   r   r   r   Úfile_changed>   s    
zFileMonitorProxy.file_changedc          	   C   s$   | j  tƒ | _d| _W d Q R X d S )NF)r$   r   r    r%   )r&   r   r   r   Úclear_changesH   s    zFileMonitorProxy.clear_changes)N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r-   r'   r.   r0   r1   r6   r7   r   r   r   r   r      s   


r   c               @   sF   e Zd Zdd„ ZedƒZedƒZedƒZedƒZedƒZedƒZ	[d	S )
ÚControlSignalc             C   s   t | ƒ d¡S )NÚascii)ÚchrÚencode)r   r   r   r   Ú<lambda>O   ó    zControlSignal.<lambda>r   é   é   é   é
   é   N)
r8   r9   r:   ÚbyteÚSIGINTÚSIGHUPÚSIGTERMÚSIGCHLDÚFILE_CHANGEDÚWORKER_COMMANDr   r   r   r   r<   N   s   r<   c               @   s   e Zd ZdZdZdZdS )ÚWorkerResultÚexitÚreloadÚwaitN)r8   r9   r:   ÚEXITÚRELOADÚWAITr   r   r   r   rN   [   s   rN   c               @   sˆ   e Zd ZdZddd„Zdd„ Zdd	„ Zd
d„ Zdd„ Ze	dd„ ƒZ
e	dd„ ƒZdd„ Ze	dd„ ƒZejejejejdœZe	dd„ ƒZdS )ÚReloaderzr
    A wrapper class around a file monitor which will handle changes by
    restarting a new worker process.

    r   Nc	       	      C   sB   || _ || _|| _|| _|| _|| _|| _|| _d | _t	ƒ | _
d S )N)Úworker_pathÚworker_argsÚworker_kwargsr!   Úmonitor_factoryÚreload_intervalÚshutdown_intervalr   r-   r   Úprocess_group)	r&   rV   rY   r   rZ   r[   rW   rX   r!   r   r   r   r'   m   s    zReloader.__init__c          	   C   sz   |   ¡ ^ xV|  ¡ }t ¡ }|tjkr.|  ¡ }|tjkr:P | jt ¡ |  }|dkrt |¡ qW W dQ R X t	 
d¡ dS )z‡
        Execute the reloader forever, blocking the current thread.

        This will invoke ``sys.exit(1)`` if interrupted.

        r   Nr   )Ú_setup_runtimeÚ_run_workerÚtimerN   rT   Ú_wait_for_changesrR   rZ   ÚsleepÚsysrO   )r&   Úresultr0   Údtr   r   r   Úrunƒ   s    


zReloader.runc          	   C   s    |   ¡  |  ¡  W dQ R X dS )zd
        Execute the worker once.

        This method will return after the worker exits.

        N)r]   r^   )r&   r   r   r   Úrun_once—   s    
zReloader.run_oncec             C   s   t | j| j| jd}t| |ƒS )N)ÚargsÚkwargs)r   rV   rW   rX   r^   )r&   Úworkerr   r   r   r^   ¡   s    zReloader._run_workerc             C   s   t td ƒ}t| |tƒ ddS )Nz
.wait_mainr   )r   r[   )r   r8   r^   r	   )r&   ri   r   r   r   r`   §   s    zReloader._wait_for_changesc             c   sF   |   ¡ 4 |  ¡   |  ¡  d V  W d Q R X W d Q R X W d Q R X d S )N)Ú_start_controlÚ_start_monitorÚ_capture_signals)r&   r   r   r   r]   °   s    


zReloader._setup_runtimec          	   c   sF   t  ¡ \| _| _z
d V  W d t  | j¡ t  | j¡ d  | _| _X d S )N)ÚosÚpipeÚ	control_rÚ	control_wÚclose)r&   r   r   r   rj   ·   s    
zReloader._start_controlc                s   ‡ ‡fdd„S )Nc                 s   t  ˆ jˆ¡S )N)rm   Úwriterp   )rg   )r&   Úsignalr   r   r@   Â   rA   z)Reloader._control_proxy.<locals>.<lambda>r   )r&   rs   r   )r&   rs   r   Ú_control_proxyÁ   s    zReloader._control_proxyc             c   s`   t |  tj¡| j| jƒ}| j|j| j| jd|_	|| _	| j	 
¡  z
d V  W d d | _	| ¡  X d S )N)Úintervalr   )r   rt   r<   rL   r   r!   rY   r6   rZ   r-   r0   r1   )r&   Úproxyr   r   r   rk   Ä   s    


zReloader._start_monitor)rH   rI   rJ   rK   c             c   s¾   g }zšxŽ| j  ¡ D ]€\}}tt|d ƒ}|d krB| j d |¡¡ q|  |¡}trr|dkrrt	 
|¡}| |¡ tj}t ||¡}| ||fdd„¡ qW d V  W d xt|ƒD ]
}|ƒ  qªW X d S )Nzskipping unsupported signal={}rH   c             S   s   t   | |¡S )N)rs   )Úsr*   r   r   r   r@   ñ   rA   z+Reloader._capture_signals.<locals>.<lambda>)Ú_signalsÚitemsÚgetattrrs   r   Údebugr4   rt   r   r   ZAddConsoleCtrlHandlerÚappendÚSIG_IGNÚreversed)r&   Zundo_handlersZsignameÚcontrolÚsignumÚhandlerZundoZpsigr   r   r   rl   ß   s&    



zReloader._capture_signals)r   r   NNN)r8   r9   r:   r;   r'   re   rf   r^   r`   r   r]   rj   rt   rk   r<   rH   rI   rJ   rK   rx   rl   r   r   r   r   rU   f   s&       

	

rU   c       
   
      sf  |d krˆj }|d krˆj}tƒ ‰ ‡ ‡fdd„}ˆj ¡  | |¡ tj}d}| d|j	 ¡ zÂˆj
 |j	¡ xxˆ r*ˆ  ¡ }|d krÄ|jršt d¡ |jr²| d¡ tj}P t ˆjtj¡ qt| d |d ¡¡ |d d	krîtj}P qt|d d
krx*|d D ]}ˆj |¡ qW qttd|ƒ‚qtt ˆjd¡}	|	sR| d¡ tj}P qt|	tjkrv| d¡ tj}d}P qt|	tjkr–| d¡ tj}P qt|	tj kr¶| d¡ tj}P qt|	tj!krÖˆjj"rètj}P qt|	tjkrt|jstP qtW |jr |r |r| d¡ |j#dd | $|¡ W d |jrH| d¡ | #¡  | %¡  n| %¡  | d|j& ¡ X |S )Nc                s   ˆ   | ¡ t ˆjtj¡ d S )N)r|   rm   rr   rp   r<   rM   )Úpacket)Úpacketsr&   r   r   Úhandle_packet  s    
z"_run_worker.<locals>.handle_packetTzStarting monitor for PID %s.r   z3Worker pipe died unexpectedly, triggering a reload.zReceived worker command "{}".r   rP   Zwatch_fileszreceived unknown control signalzControl pipe died unexpectedly.z/Received SIGINT, waiting for server to exit ...Fz%Received SIGHUP, triggering a reload.z(Received SIGTERM, triggering a shutdown.zGracefully killing the server.)Zsoftz(Server did not exit, forcefully killing.zServer exited with code %d.)'r   r[   r   r-   r7   r0   rN   rT   r3   Úpidr\   Z	add_childÚpopleftÚis_aliver_   ra   rS   rm   rr   rp   r<   rK   r{   r4   r.   ÚRuntimeErrorÚreadro   ÚerrorrR   rH   rI   rJ   rL   r%   ÚkillrQ   r2   Úexitcode)
r&   ri   r   r[   r„   rc   Z	soft_killÚcmdr/   rs   r   )rƒ   r&   r   r^   ù   s    












r^   c              C   sX   y>t ƒ } ttjƒr$tdƒ |  ¡  ntdƒ xt d¡ q.W W n t	k
rR   Y nX d S )Nz(Press ENTER or change a file to reload.
z+Waiting for a file to change before reload.rE   )
r   r   rb   ÚstdinÚinputZtrigger_reloadÚprintr_   ra   ÚKeyboardInterrupt)Úreloaderr   r   r   Ú	wait_mainv  s    

r“   c             C   s|   t  d¡}|r&t|ƒ}|  d| ¡ nRtƒ rDddlm} |  d¡ n4tƒ rbddlm	} |  d¡ nddl
m} |  d	¡ |S )
NZHUPPER_DEFAULT_MONITORzFile monitor backend: r   )ÚWatchmanFileMonitorzFile monitor backend: watchman)ÚWatchdogFileMonitorzFile monitor backend: watchdog)ÚPollingFileMonitorzFile monitor backend: polling)rm   Úgetenvr   r{   r   Zwatchmanr”   r   Zwatchdogr•   Úpollingr–   )r   ÚspecrY   r   r   r   Úfind_default_monitor_factory…  s    

rš   c	       
   
   C   sX   t ƒ rtƒ S |dkrt|ƒ}|dkr,t|ƒ}|tkr8|}t| |||||||d}	|	 ¡ S )a¥  
    Start a monitor and then fork a worker process which starts by executing
    the importable function at ``worker_path``.

    If this function is called from a worker process that is already being
    monitored then it will return a reference to the current
    :class:`hupper.interfaces.IReloaderProxy` which can be used to
    communicate with the monitor.

    ``worker_path`` must be a dotted string pointing to a globally importable
    function that will be executed to start the worker. An example could be
    ``myapp.cli.main``. In most cases it will point at the same function that
    is invoking ``start_reloader`` in the first place.

    ``reload_interval`` is a value in seconds and will be used to throttle
    restarts. Default is ``1``.

    ``shutdown_interval`` is a value in seconds and will be used to trigger
    a graceful shutdown of the server. Set to ``None`` to disable the graceful
    shutdown. Default is the same as ``reload_interval``.

    ``verbose`` controls the output. Set to ``0`` to turn off any logging
    of activity and turn up to ``2`` for extra output. Default is ``1``.

    ``logger``, if supplied, supersedes ``verbose`` and should be an object
    implementing :class:`hupper.interfaces.ILogger`.

    ``monitor_factory`` is an instance of
    :class:`hupper.interfaces.IFileMonitorFactory`. If left unspecified, this
    will try to create a :class:`hupper.watchdog.WatchdogFileMonitor` if
    `watchdog <https://pypi.org/project/watchdog/>`_ is installed and will
    fallback to the less efficient
    :class:`hupper.polling.PollingFileMonitor` otherwise.

    If ``monitor_factory`` is ``None`` it can be overridden by the
    ``HUPPER_DEFAULT_MONITOR`` environment variable. It should be a dotted
    python path pointing at an object implementing
    :class:`hupper.interfaces.IFileMonitorFactory`.

    ``ignore_files`` if provided must be an iterable of shell-style patterns
    to ignore.
    N)rV   rW   rX   rZ   r[   rY   r   r!   )r   r   r   rš   r
   rU   re   )
rV   rZ   r[   Úverboser   rY   rW   rX   r!   r’   r   r   r   Ústart_reloaderž  s$    5rœ   )NN)(Úcollectionsr   Ú
contextlibr   r   rm   r   rs   rb   r"   r_   Úcompatr   r   Zipcr   r   r   r	   Úutilsr
   r   r   r   r   ri   r   r   r   Ú r   Úobjectr   r<   rN   rU   r^   r“   rš   rœ   r   r   r   r   Ú<module>   s>   3 
}