U
    ڲg\2                     @  s  U d 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m	Z	m
Z
mZmZ ddlmZ ddlmZ ddlmZmZmZmZ dd	lmZmZ dd
lmZ ejZejZejZ ej!Z"ejZ#ejZ$ejZ%e&e'Z(d-dddddddZ)d.ddddddZ*d/dddddddZ+d0ddddddZ,d1dddddddZ-e.edreej/ej0B ej1B eej/ej2B ej1B e ej/ej0ej2B ej1B B iZ3ni Z3d2dddddddZ4e.edreej5eej6e ej5ej6B iZ7ni Z7d3dddddd d!Z8d"d#d$d%Z9erej:Z:d&e;d'< d(ej<krRej<d( Z=e=>d)r8e=e? krFe@d*e=e? e= ZAnPerrejBd+krre9 sre:ZAn0ejeCed,dkre-ZAne.edre8ZAne)ZAdS )4z
Code concerned with waiting in different contexts (blocking, async, etc).

These functions are designed to consume the generators returned by the
`generators` module function and to return their final value.

    )annotationsN)get_event_loopwait_forEventTimeoutError)DefaultSelector   )errors)RVPQGen	PQGenConnWaitFunc)WaitReady)_psycopgz	PQGen[RV]intzfloat | Noner
   )genfilenointervalreturnc           	   
   C  s   ztt | }t \}||| |j|d}|s:| t q|| |d d }| |}||| qW 5 Q R X W n> tk
r } z |jr|jd nd}| W Y S d}~X Y nX dS )a8  
    Wait for a generator using the best strategy available.

    :param gen: a generator performing database operations and yielding
        `Ready` values when it would block.
    :param fileno: the file descriptor to wait on.
    :param interval: interval (in seconds) to check for other interrupt, e.g.
        to allow Ctrl-C. If zero or None, wait indefinitely.
    :return: whatever `!gen` returns on completion.

    Consume `!gen`, scheduling `fileno` for completion when it is reported to
    block. Once ready again send the ready state back to `!gen`.
    timeoutr   r   N	nextr   registerselectsend
READY_NONE
unregisterStopIterationargs)	r   r   r   sselrlistreadyexrv r'   3/tmp/pip-unpacked-wheel-b_ea6rx_/psycopg/waiting.pywait_selector%   s    


r)   zPQGenConn[RV])r   r   r   c           	   
   C  s   zt | \}}|sd}t `}||| |j|d}|sF| t q*|| |d d }| |\}}||| q*W 5 Q R X W n> tk
r } z |jr|jd nd}| W Y S d}~X Y nX dS )a  
    Wait for a connection generator using the best strategy available.

    :param gen: a generator performing database operations and yielding
        (fd, `Ready`) pairs when it would block.
    :param interval: interval (in seconds) to check for other interrupt, e.g.
        to allow Ctrl-C. If zero or None, wait indefinitely.
    :return: whatever `!gen` returns on completion.

    Behave like in `wait()`, but take the fileno to wait from the generator
    itself, which might change during processing.
    Nr   r   r   r   )	r   r   r   r!   r"   r#   r$   r%   r&   r'   r'   r(   	wait_connG   s"    

r*   c           
   
     st  t   t }ddd fdd}zt| }|t@ }|t@ }|sT|sTtd|    d|rr|||t	 |r|
||t zJ|dk	rzt  |I dH  W q tk
r   Y qX n  I dH  W 5 |r|| |r|| X | }q,W nr tk
r. } ztt|W 5 d}~X Y nB tk
rn } z"|jrR|jd nd}	|	 W Y S d}~X Y nX dS )	a  
    Coroutine waiting for a generator to complete.

    :param gen: a generator performing database operations and yielding
        `Ready` values when it would block.
    :param fileno: the file descriptor to wait on.
    :param interval: interval (in seconds) to check for other interrupt, e.g.
        to allow Ctrl-C. If None, wait indefinitely.
    :return: whatever `!gen` returns on completion.

    Behave like in `wait()`, but exposing an `asyncio` interface.
    r   Nonestater   c                   s   | O     d S Nsetr-   evr$   r'   r(   wakeup~   s    zwait_async.<locals>.wakeupbad poll status: r   N)r   r   r   WAIT_RWAIT_WeInternalErrorclear
add_readerREADY_R
add_writerREADY_Wremove_readerremove_writerr   waitr   r   OSErrorZOperationalErrorstrr   r    )
r   r   r   loopr4   r!   readerwriterr%   r&   r'   r2   r(   
wait_asyncj   s@    
rG   c           
   
     sH  t   t }ddd fdd}zt| \}}|t@ }|t@ }|sX|sXtd|    d|rv|||t	 |r|
||t zF|rzt  |I dH  W q tk
r   Y qX n  I dH  W 5 |r|| |r|| X | \}}q0W nB tk
rB } z"|jr&|jd nd}	|	 W Y S d}~X Y nX dS )	a  
    Coroutine waiting for a connection generator to complete.

    :param gen: a generator performing database operations and yielding
        (fd, `Ready`) pairs when it would block.
    :param interval: interval (in seconds) to check for other interrupt, e.g.
        to allow Ctrl-C. If zero or None, wait indefinitely.
    :return: whatever `!gen` returns on completion.

    Behave like in `wait()`, but take the fileno to wait from the generator
    itself, which might change during processing.
    r   r+   r,   c                   s   |     d S r.   r/   r1   r2   r'   r(   r4      s    zwait_conn_async.<locals>.wakeupr5   r   N)r   r   r   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   r   rA   r   r   r   r    )
r   r   rD   r4   r   r!   rE   rF   r%   r&   r'   r2   r(   wait_conn_async   s<    
rH   c              
   C  s   zxt | }d}|f}t|t@ r$|n||t@ r2|n|||\}}}d}	|rNt}	|rZ|	tO }	|	sj| t q| |	}qW n> tk
r }
 z |
j	r|
j	d nd}| W Y S d}
~
X Y nX dS )z
    Wait for a generator using select where supported.

    BUG: on Linux, can't select on FD >= 1024. On Windows it's fine.
    r'   r   N)
r   r   r6   r7   r<   r>   r   r   r   r    )r   r   r   r!   emptyZfnlistZrlZwlZxlr$   r%   r&   r'   r'   r(   wait_select   s,    

rJ   EpollSelectorc              
   C  s   zt | }|dks|dk rd}t }t| }||| ||}|sV| t q<|d d }d}|tj @ rvt	}|tj
 @ r|tO }| |}t| }||| q<W 5 Q R X W n> tk
r }	 z |	jr|	jd nd}
|
 W Y S d}	~	X Y nX dS )a$  
    Wait for a generator using epoll where supported.

    Parameters are like for `wait()`. If it is detected that the best selector
    strategy is `epoll` then this function will be used instead of `wait`.

    See also: https://linux.die.net/man/2/epoll_ctl

    BUG: if the connection FD is closed, `epoll.poll()` hangs. Same for
    EpollSelector. For this reason, wait_poll() is currently preferable.
    To reproduce the bug:

        export PSYCOPG_WAIT_FUNC=wait_epoll
        pytest tests/test_concurrency.py::test_concurrent_close
    Nr   g        r   )r   r   epoll_epoll_evmasksr   pollr   r   EPOLLOUTr<   EPOLLINr>   modifyr   r    )r   r   r   r!   rL   evmaskfileevsr3   r$   r%   r&   r'   r'   r(   
wait_epoll  s.    



rT   PollSelectorc              
   C  s   zt | }|dks|dk r d}nt|d }t }t| }||| ||}|sb| t qH|d d }d}|tj @ rt	}|tj
 @ r|tO }| |}t| }||| qHW n> tk
r }	 z |	jr|	jd nd}
|
 W Y S d}	~	X Y nX dS )za
    Wait for a generator using poll where supported.

    Parameters are like for `wait()`.
    Nr   g     @@r   )r   r   r   rN   _poll_evmasksr   r   r   POLLOUTr<   POLLINr>   rQ   r   r    )r   r   r   r!   rN   rR   rS   r3   r$   r%   r&   r'   r'   r(   	wait_pollH  s0    


rY   bool)r   c               
   C  sX   t jd} | rTz| dr"W dS W n. tk
rR } ztd| W 5 d}~X Y nX dS )z
    Detect if some greenlet library has patched the select library.

    If this is the case, avoid to use the wait_c function as it doesn't behave
    in a collaborative way.

    Currently supported: gevent.
    zgevent.monkeyr   Tz+failed to detect gevent monkey-patching: %sNF)sysmodulesgetZis_module_patched	Exceptionloggerwarning)mr%   r'   r'   r(   _is_select_patchedn  s    


rb   r   rA   ZPSYCOPG_WAIT_FUNCZwait_zHPSYCOPG_WAIT_FUNC should be the name of an available wait function; got win32SelectSelector)N)N)N)N)N)N)N)D__doc__
__future__r   osr[   r   logging	selectorsZasyncior   r   r   r   r    r	   r8   abcr
   r   r   r   Z_enumsr   r   Z_cmoduler   Rr6   Wr7   ZRWZWAIT_RWNONEr   r<   r>   ZREADY_RW	getLogger__name__r_   r)   r*   rG   rH   rJ   hasattrEPOLLONESHOTrP   EPOLLERRrO   rM   rT   rX   rW   rV   rY   rb   Zwait_c__annotations__environfname
startswithglobalsImportErrorrA   platformgetattrr'   r'   r'   r(   <module>   s   

"#=="   -   
&
