a
    mzfG                     @   sr  d 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	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Zdd Zz0ejdd dkrdd	 ZnddlZd
d	 ZW n* ey   ddlZddlZdd	 ZY n0 G dd dZG dd de	jZdZdd Zdd Z ddddZ!dddddej"j#ddfddZ$dd  Z%g Z&d!Z'G d"d# d#e(Z)d&d$d%Z*dS )'a  Extensions to unittest for web frameworks.

Use the :py:meth:`WebCase.getPage` method to request a page
from your HTTP server.

Framework Integration
=====================
If you have control over your server process, you can handle errors
in the server-side of the HTTP conversation a bit better. You must run
both the client (your :py:class:`WebCase` tests) and the server in the
same process (but in separate threads, obviously).
When an error occurs in the framework, call server_error. It will print
the traceback to stdout, and keep any assertions you have from running
(the assumption is that, if the server errors, the page output will not
be of further significance to your tests).
    N)always_iterablec                 C   s   | dkrdS | dkrdS | S )zReturn an IP address for a client connection given the server host.

    If the server is listening on '0.0.0.0' (INADDR_ANY)
    or '::' (IN6ADDR_ANY), this will return the proper localhost.
    z0.0.0.0	127.0.0.1z::z::1 )hostr   r   B/var/www/media/lib/python3.9/site-packages/cheroot/test/webtest.py	interface$   s
    r      javac                   C   s   t jdS Get a key press.   )sysstdinreadr   r   r   r   getchar6   s    r   c                   C   s   t  S )r   )msvcrtZgetchr   r   r   r   r   >   s    c                  C   s\   t j } t| }z0tt j  t jd}W t| tj	| nt| tj	| 0 |S r
   )
r   r   filenotermios	tcgetattrttyZsetrawr   	tcsetattr	TCSADRAIN)fdZold_settingschr   r   r   r   F   s    

$c                   @   s"   e Zd ZdZdd ZdddZdS )NonDataPropertyzNon-data property decorator.c                 C   s*   |dusJ dt |s J d|| _dS )zInitialize a non-data property.Nzfget cannot be nonezfget must be callable)callablefget)selfr   r   r   r   __init__V   s    zNonDataProperty.__init__Nc                 C   s   |du r| S |  |S )zReturn a class property.N)r   )r   objZobjtyper   r   r   __get__\   s    zNonDataProperty.__get__)N)__name__
__module____qualname____doc__r   r    r   r   r   r   r   S   s   r   c                   @   s   e Zd ZdZdZdZejjZ	dZ
dZdZdZdZdZdZdZdZedd	 Zd7ddZd8ddZedd Zejdd Zdd Zd9ddZedd ZdZdd Zedd Zd d! Z d:d"d#Z!d;d$d%Z"d<d&d'Z#d=d(d)Z$d>d*d+Z%d?d,d-Z&d@d.d/Z'dAd0d1Z(dBd2d3Z)dCd5d6Z*dS )DWebCasezHelper web test suite base.r   @  HTTP/1.1httpNutf-8c                 C   s   dj | j d}ttj|S )zuReturn HTTPConnection or HTTPSConnection based on self.scheme.

        * from :py:mod:`python:http.client`.
        z{scheme}Connection)scheme)formatr*   uppergetattrr(   client)r   cls_namer   r   r   _Connw   s    zWebCase._ConnFc                 C   s$   |  |  | j}||_|  |S )z'Return a connection to our HTTP server.)r0   r   PORT	auto_openconnect)r   r2   connr   r   r   get_conn   s    zWebCase.get_connTc              	   C   sB   z| j   W n ttfy$   Y n0 |r6| j|dn| j| _ dS )aY  Make our HTTP_CONN persistent (or not).

        If the 'on' argument is True (the default), then self.HTTP_CONN
        will be set to an instance of HTTP(S)?Connection
        to persist across requests.
        As this class only allows for a single open connection, if
        self already has an open connection, it will be closed.
        )r2   N)	HTTP_CONNclose	TypeErrorAttributeErrorr5   r0   )r   onr2   r   r   r   set_persistent   s    	zWebCase.set_persistentc                 C   s   t | jdS )z+Presence of the persistent HTTP connection.	__class__)hasattrr6   r   r   r   r   
persistent   s    zWebCase.persistentc                 C   s   |  | d S )N)r;   )r   r:   r   r   r   r?      s    c                 C   s
   t | jS )zReturn an IP address for a client connection.

        If the server is listening on '0.0.0.0' (INADDR_ANY)
        or '::' (IN6ADDR_ANY), this will return the proper localhost.
        )r   HOSTr>   r   r   r   r      s    zWebCase.interfaceGETr   c           	      C   s   dt _t|tr|d}t|tr.|d}|p4d}|| _d| _t }t||||| j| j	| j
|ph| j|| jd
}t | | _|\| _| _| _dd | jD | _t jrt  |S )a;  Open the url with debugging support.

        Return status, headers, body.

        url should be the identifier passed to the server, typically a
        server-absolute path and query string (sent between method and
        protocol), and should only be an absolute URI if proxy support is
        enabled in the server.

        If the application under test generates absolute URIs, be sure
        to wrap them first with :py:func:`strip_netloc`::

            >>> class MyAppWebCase(WebCase):
            ...     def getPage(url, *args, **kwargs):
            ...         super(MyAppWebCase, self).getPage(
            ...             cheroot.test.webtest.strip_netloc(url),
            ...             *args, **kwargs
            ...         )

        ``raise_subcls`` is passed through to :py:func:`openURL`.
        Fr)   r   N)raise_subclsssl_contextc                 S   s$   g | ]\}}|  d krd|fqS )z
set-cookieCookielower.0kvr   r   r   
<listcomp>   s   z#WebCase.getPage.<locals>.<listcomp>)ServerErrorr:   
isinstancestrencodeurltimeopenURLr@   r1   r6   PROTOCOLrC   statusheadersbodycookies)	r   rP   rU   methodrV   protocolrB   startresultr   r   r   getPage   s.    



zWebCase.getPagec                 C   s4   t jdd}tt| }|r0tdt	 |S )zDetermine whether tests are run in interactive mode.

        Load interactivity setting from environment, where
        the value can be numeric or a string like true or
        False or 1 or 0.
        ZWEBTEST_INTERACTIVETruezhInteractive test failure interceptor support via WEBTEST_INTERACTIVE environment variable is deprecated.)
osenvirongetbooljsonloadsrF   warningswarnDeprecationWarning)r   Zenv_strZis_interactiver   r   r   interactive   s    zWebCase.interactive   c                 C   st  t d t d|  | js$| |d}tj| tj  t  }t	|t
ds`|d}|dvrjq>t |  |dkrt| j D ]P\}}|d | j dkrtjd	 t  }tjd
 |dkr qt | qnx|dkrt| j nb|dkrt | j nL|dkr"t | j n6|dkr0d S |dkrF| |n|dkrXt  tj| tj  q>d S )N z    ERROR: %szO    Show: [B]ody [H]eaders [S]tatus [U]RL; [I]gnore, [R]aise, or sys.e[X]it >> asciiZBHSUIRXBr   r   z<-- More -->z            qHSUIRX)printrg   ZfailureExceptionr   stdoutwriteflushr   r,   rM   typedecode	enumeraterV   
splitlinesconsole_heightrF   pprintrU   rT   rP   exit)r   msgpixlinemr   r   r   _handlewebError   sJ    









zWebCase._handlewebErrorc                 C   s   t | jdd S )zInteger HTTP status code.N   )intrT   r>   r   r   r   status_code*  s    zWebCase.status_codec                 C   s   t |tr| jn| j}||kS )z-Check whether actual status matches expected.)rM   r   r   rT   )r   expectedactualr   r   r   status_matches/  s
    zWebCase.status_matchesc                 C   s@   t t| jt|rdS d}|p0|jf i t }| | dS )zFail if self.status != status.

        status may be integer code, exact string status, or
        iterable of allowed possibilities.
        Nz,Status {self.status} does not match {status})anymapr   r   r+   localsr   )r   rT   r~   tmplr   r   r   assertStatus8  s
    zWebCase.assertStatusc                 C   st   |  }| jD ]0\}}|  |kr|du s6t||kr|  S q|du rf|du rZd| }nd||f }| | dS )z+Fail if (key, [value]) not in self.headers.Nz%r not in headersz%r:%r not in headers)rF   rU   rN   r   )r   keyvaluer~   lowkeyrI   rJ   r   r   r   assertHeaderE  s    

zWebCase.assertHeaderc                    sb   |  }| jD ]2\} |  |kr fdd|D }|r|  S q|du rTdt  }| | dS )z?Fail if header indicated by key doesn't have one of the values.c                    s   g | ]}t | kr|qS r   )rN   )rH   r   rJ   r   r   rK   Y      z*WebCase.assertHeaderIn.<locals>.<listcomp>Nz%(key)r not in %(values)r)rF   rU   varsr   )r   r   valuesr~   r   rI   matchesr   r   r   assertHeaderInT  s    

zWebCase.assertHeaderInc                 C   sN   | j ||d}ttj|d}||v r,|S |du r@d||f }| | dS )z8Fail if the header does not contain the specified value.)r~   ,Nz%r not in %r)r   r   rN   stripsplitr   )r   r   r   r~   Zactual_valueheader_valuesr   r   r   assertHeaderItemValuea  s    zWebCase.assertHeaderItemValuec                    s>   |    fdd| jD }|r:|du r0d| }| | dS )zFail if key in self.headers.c                    s    g | ]\}}|   kr|qS r   rE   rG   r   r   r   rK   o  r   z*WebCase.assertNoHeader.<locals>.<listcomp>Nz%r in headersrF   rU   r   )r   r   r~   r   r   r   r   assertNoHeaderl  s    zWebCase.assertNoHeaderc                    sJ   |   | j} fdd|D }|rF|du r<d||f }| | dS )z0Fail if the header contains the specified value.c                    s(   g | ] \}}|   kr|kr|qS r   rE   rG   r   r   r   r   rK   y  r   z3WebCase.assertNoHeaderItemValue.<locals>.<listcomp>Nz%r:%r in %rr   )r   r   r   r~   hdrsr   r   r   r   assertNoHeaderItemValueu  s    zWebCase.assertNoHeaderItemValuec                 C   sD   t |tr|| j}|| jkr@|du r6d|| jf }| | dS )zFail if value != self.body.Nz"expected body:
%r

actual body:
%rrM   rN   rO   encodingrV   r   r   r   r~   r   r   r   
assertBody  s    

zWebCase.assertBodyc                 C   sD   t |tr|| j}|| jvr@|du r6d|| jf }| | dS )zFail if value not in self.body.Nz%r not in body: %sr   r   r   r   r   assertInBody  s    

zWebCase.assertInBodyc                 C   s>   t |tr|| j}|| jv r:|du r0d| }| | dS )zFail if value in self.body.Nz%r found in bodyr   r   r   r   r   assertNotInBody  s    

zWebCase.assertNotInBodyr   c                 C   sH   t |tr|| j}t|| j|du rD|du r:d| }| | dS )z4Fail if value (a regex pattern) is not in self.body.NzNo match for %r in body)rM   rN   rO   r   researchrV   r   )r   patternr~   flagsr   r   r   assertMatchesBody  s    
zWebCase.assertMatchesBody)F)TF)NrA   NNr   )N)NN)N)N)N)N)N)N)N)Nr   )+r!   r"   r#   r$   r@   r1   r(   r.   HTTPConnectionr6   rS   r*   rP   rC   rT   rU   rV   r   rQ   propertyr0   r5   r;   r?   setterr   r\   r   rg   r{   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r%   c   sT   




	  
9
0
	




	



	
	r%   )POSTPUTPATCHc           	      C   s   | du rg } d}| D ]\}}|  dkrd} q2q|sd|dkrN| d|f n| dd||f f |tv rd}| D ]\}}|  dkrtd} qqt|s| d	 | d
tt|pdf | S )zAReturn request headers, with required headers added (if missing).NFr   TP   Hostz%s:%szcontent-type)zContent-Typez!application/x-www-form-urlencodedzContent-Lengthri   )rF   appendmethods_with_bodiesrN   len)	rU   rX   rV   r   portfoundrI   Z_vrJ   r   r   r   cleanHeaders  s.    r   c                 C   s"   d| j | jf }||  |  fS )z=Return status, headers, body the way we like from a response.z%s %s)rT   reason
getheadersr   )responseZresp_status_liner   r   r   shb  s    r   r   rB   c                    s:   t jtg|R i |} fdd}tj j|d|tjdS )z
    Open a URL, retrying when it fails.

    Specify ``raise_subcls`` (class or tuple of classes) to exclude
    those socket.error subclasses from being suppressed and retried.
    c                     s(   t  d } t|  r| td d S )Nr   g      ?)r   exc_inforM   rQ   sleepexcr   r   r   on_exception  s    
zopenURL.<locals>.on_exception	   )retriescleanupZtrap)	functoolspartial_open_url_oncejaracoZ
retry_callsocketerror)rB   argskwargsopenerr   r   r   r   rR     s    rR   rA   r   r&   r'   c	                 C   s   t |||||}t|dr |}	n&i }
|r0||
d< |t||fi |
}	||	_tddd |D |	_t| trx| 	 } |	j
| | ddd |D ]\}}|	||d q|	  |d	ur|	| |	 }t|\}}}t|ds|	  |||fS )
zBOpen the given HTTP resource and return status, headers, and body.r   contextri   c                 S   s   g | ]}|  r|qS r   )isdigit)rH   r   r   r   r   rK     r   z"_open_url_once.<locals>.<listcomp>T)	skip_hostskip_accept_encodingzLatin-1N)r   r=   r   _http_vsn_strr   join	_http_vsnrM   bytesrx   
putrequestr,   	putheaderrO   
endheaderssendgetresponser   r7   )rP   rU   rX   rV   r   r   	http_connrY   rC   r4   kwr   r   r   shbr   r   r   r     s2    




r   c           	      C   s8   t j| }|\}}}}}}dd|||df}t j|S )a'  Return absolute-URI path from URL.

    Strip the scheme and host from the URL, returning the
    server-absolute portion.

    Useful for wrapping an absolute-URI for which only the
    path is expected (such as in calls to :py:meth:`WebCase.getPage`).

    .. testsetup::

       from cheroot.test.webtest import strip_netloc

    >>> strip_netloc('https://google.com/foo/bar?bing#baz')
    '/foo/bar?bing'

    >>> strip_netloc('//google.com/foo/bar?bing#baz')
    '/foo/bar?bing'

    >>> strip_netloc('/foo/bar?bing#baz')
    '/foo/bar?bing'
    ri   )urllibparseurlparse
urlunparse)	rP   parsedZ_schemeZ_netlocpathparamsqueryZ	_fragmentstrippedr   r   r   strip_netloc  s    r   Fc                   @   s   e Zd ZdZdZdS )rL   z&Exception for signalling server error.FN)r!   r"   r#   r$   r:   r   r   r   r   rL   6  s   rL   c                 C   sN   | du rt  } ts | d tv r$dS dt_td tdtj	|   dS dS )zServer debug hook.

    Return True if exception handled, False if ignored.
    You probably want to wrap this, so you can still handle an error using
    your framework when it's ignored.
    Nr   FTri   )
r   r   
ignore_allignored_exceptionsrL   r:   rs   r   	tracebackformat_exceptionr   r   r   r   server_error<  s    r   )N)+r$   r|   r   r   r   rQ   r   r^   rb   Zunittestrd   r   http.clientr(   urllib.parser   Zmore_itertools.morer   Zjaraco.functoolsr   r   platformr   r   ImportErrorr   r   r   ZTestCaser%   r   r   r   rR   r.   r   r   r   r   r   	ExceptionrL   r   r   r   r   r   <module>   sT   
  E"

&