B
    `zL                 @   s   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	 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ZG dd dejZdS )    N)loads)forms)utils)print_stderr)PY3)urlparse)to_bytes)string_types)binary_type)	text_type)BeautifulSoupc               @   s6  e Zd ZdZdZdZdZedd Zedd Z	edd	 Z
ed
ejejB Zdd Zdd Zdd Zdd Zd8ddZd9ddZdd Zd:ddZeedZedd ZedZed d! Zd"d# Zd$d% Zd&d' Zd(d) Z d*d+ Z!ed,d- Z"ed.d/ Z#ed0d1 Z$ed2d3 Z%ed4d5 Z&d6d7 Z'dS );TestResponsez\
    Instances of this class are returned by
    :class:`~webtest.app.TestApp` methods.
    Nzhtml.parserc             C   s   | j dkr|   | j S )a  
        Returns a dictionary containing all the forms in the pages as
        :class:`~webtest.forms.Form` objects. Indexes are both in
        order (from zero) and by form id (if the form is given an id).

        See :doc:`forms` for more info on form objects.
        N)_forms_indexed_parse_forms)self r   X/home/kop/projects/devel/pgwui/test_venv/lib/python3.7/site-packages/webtest/response.pyr      s    	
zTestResponse.formsc             C   s*   | j }|stdd|kr"td|d S )z
        If there is only one form on the page, return it as a
        :class:`~webtest.forms.Form` object; raise a TypeError is
        there are no form or multiple forms.
        z*You used response.form, but no forms exist   z5You used response.form, but more than one form existsr   )r   	TypeError)r   forms_r   r   r   form,   s    zTestResponse.formc             C   sD   |    | jr6y| jS  tk
r4   | j| jdS X | jddS )Nreplaceascii)decode_contentcharsettextUnicodeDecodeErrorbodydecode)r   r   r   r   testbody=   s    zTestResponse.testbodyz<(/?)([:a-z0-9_\-]*)(.*?)>c             C   s`   i  }| _ dd | dD }x<t|D ]0\}}t| || j}|||< |jr(|||j< q(W d S )Nc             S   s   g | ]}t |qS r   )r   ).0fr   r   r   
<listcomp>K   s    z-TestResponse._parse_forms.<locals>.<listcomp>r   )r   html	enumerater   ZFormparser_featuresid)r   r   Z
form_textsir   r   r   r   r   r   I   s    
zTestResponse._parse_formsc             K   s*   | j d }t| jj|}| jj|f|S )Nlocation)headersr   urljoinrequesturltest_appget)r   kwr(   Zabslocationr   r   r   _followR   s    
zTestResponse._followc             K   s2   d| j   krdk s&n td| j | jf |S )a  
        If this response is a redirect, follow that redirect.  It is an
        error if it is not a redirect response. Any keyword
        arguments are passed to :class:`webtest.app.TestApp.get`. Returns
        another :class:`TestResponse` object.
        i,  i  z/You can only follow redirect responses (not %s))
status_intAssertionErrorstatusr0   )r   r/   r   r   r   followX   s    zTestResponse.followc             K   sT   d}| }x6d|j   kr dk r>n n|r>|jf |}|d8 }q
W |dkrPtd|S )z
        Follow all redirects. If this response is not a redirect, do nothing.
        Any keyword arguments are passed to :class:`webtest.app.TestApp.get`.
        Returns another :class:`TestResponse` object.
        d   i,  i  r   r   zredirects chain looks infinite)r1   r0   r2   )r   r/   Zremaining_redirectsresponser   r   r   maybe_followe   s     zTestResponse.maybe_followFc       
   
   C   sR   | j ddd|||||d\}}}	|p&i }|dt| jj | jt|	d |dS )a  
        Click the link as described.  Each of ``description``,
        ``linkid``, and ``url`` are *patterns*, meaning that they are
        either strings (regular expressions), compiled regular
        expressions (objects with a ``search`` method), or callables
        returning true or false.

        All the given patterns are ANDed together:

        * ``description`` is a pattern that matches the contents of the
          anchor (HTML and all -- everything between ``<a...>`` and
          ``</a>``)

        * ``linkid`` is a pattern that matches the ``id`` attribute of
          the anchor.  It will receive the empty string if no id is
          given.

        * ``href`` is a pattern that matches the ``href`` of the anchor;
          the literal content of that attribute, not the fully qualified
          attribute.

        If more than one link matches, then the ``index`` link is
        followed.  If ``index`` is not given and more than one link
        matches, or if no link matches, then ``IndexError`` will be
        raised.

        If you give ``verbose`` then messages will be printed about
        each link, and why it does or doesn't match.  If you use
        ``app.click(verbose=True)`` you'll see a list of all the
        links.

        You can use multiple criteria to essentially assert multiple
        aspects about the link, e.g., where the link's destination is.
        ahrefN)tag	href_attrhref_extractcontentr&   href_patternindexverboseHTTP_REFERERuri)extra_environ)_find_element
setdefaultstrr+   r,   goto)
r   descriptionZlinkidr9   r?   r@   rC   
found_html
found_descfound_attrsr   r   r   clickw   s    %zTestResponse.clickc       
   
   C   sL   | j ddtd|||||d\}}}dt| jji}	| jt|d |	dS )z
        Like :meth:`~webtest.response.TestResponse.click`, except looks
        for link-like buttons.
        This kind of button should look like
        ``<button onclick="...location.href='url'...">``.
        buttonZonclickzlocation\.href='(.*?)')r:   r;   r<   r=   r&   r>   r?   r@   rA   rB   )rC   )rD   recompilerF   r+   r,   rG   )
r   rH   Zbuttonidr9   r?   r@   rI   rJ   rK   rC   r   r   r   clickbutton   s    zTestResponse.clickbuttonc	                s  t |}	t |}
t |} fdd}g }d}x&| j|D ]}t|}| }|} rl|d|  ||s|d|  qB|| }|r||}|s|d qB|d}||d< |	d	r|d
 qB|	dr|d qB|d7 }|	r|	|s|d qB|
r&|
|dds&|d qB|r@||s@|d qB|d |
|||f qBW |sntd| |d krt|dkrtdddd |D  |d }n:y|| }W n, tk
r   tdt|||f Y nX |S )Nc                s    rt |  d S )N)print)s)r@   r   r   printlog   s    z,TestResponse._find_element.<locals>.printlogr   zElement: %rz  Skipped: no %s attributez(  Skipped: doesn't match extract patternr   rB   #z&  Skipped: only internal fragment hrefzjavascript:z$  Skipped: cannot follow javascript:z$  Skipped: doesn't match descriptionr&    z  Skipped: doesn't match idz  Skipped: doesn't match hrefz
  Acceptedz-No matching elements found (from %s possible)zMultiple links match: %sz, c             S   s   g | ]\}}}t |qS r   )repr)r    Zancdattrr   r   r   r"      s    z.TestResponse._find_element.<locals>.<listcomp>z6Only %s (out of %s) links match; index %s out of range)r   Zmake_patternr#   Zfind_allrF   Zdecode_contentsr.   searchgroup
startswithappend
IndexErrorlenjoin)r   r:   r;   r<   r=   r&   r>   r?   r@   Zcontent_patZid_patZhref_patrS   Zfound_linksZtotal_linkselementZel_htmlZ
el_contentattrsZel_hrefmZ
found_linkr   )r@   r   rD      st    









zTestResponse._find_elementr.   c       	         s  t |\}}}}}d } }}t |||||f}t  jj|}| }|dks`td| tst	 ddr fdd|}d|krfd	d
|d D |d< d|krȇfdd
|d D |d< d|kr|d |d< |dkr j
j}n j
j}||f|S )a;  
        Go to the (potentially relative) link ``href``, using the
        given method (``'get'`` or ``'post'``) and any extra arguments
        you want to pass to the :meth:`webtest.app.TestApp.get` or
        :meth:`webtest.app.TestApp.post` methods.

        All hostnames and schemes will be ignored.
        rU   )r.   postz9Only "get" or "post" are allowed for method (you gave %r)Z_use_unicodeFc                s   t | tr|  jS | S )N)
isinstancer   encoder   )rR   )r   r   r   to_str  s    
z!TestResponse.goto.<locals>.to_strparamsc                s   g | ]}t t |qS r   )tuplemap)r    p)rf   r   r   r"     s   z%TestResponse.goto.<locals>.<listcomp>Zupload_filesc                s   g | ]}t  |qS r   )ri   )r    r!   )rf   r   r   r"     s   content_typer.   )r   urlsplit
urlunsplitr*   r+   r,   lowerr2   r   getattrr-   r.   rc   )	r   r9   methodargsschemehostpathqueryfragmentr   )r   rf   r   rG      s.    	



zTestResponse.gotoz
[ \n\r\t]+c             C   s(   t | dddkr"| jd| j| _| jS )z7
        Return the whitespace-normalized body
        _normal_bodyN    )ro   _normal_body_regexsubr   rw   )r   r   r   r   normal_body,  s    zTestResponse.normal_bodyc             C   s6   | j stdt| dddkr0| jd| j| _| jS )zC
        Return the whitespace-normalized body, as unicode
        zDYou cannot access Response.unicode_normal_body unless charset is set_unicode_normal_bodyN )r   AttributeErrorro   _unicode_normal_body_regexrz   r   r|   )r   r   r   r   unicode_normal_body7  s    z TestResponse.unicode_normal_bodyc             C   sL   | j st|tr|d}t|tr8|| jkp6|| jkS || jkpJ|| jkS )z
        A response 'contains' a string if it is present in the body
        of the response.  Whitespace is normalized when searching
        for a string.
        utf8)	r   rd   r   re   r
   r   r{   r   r   )r   rR   r   r   r   __contains__E  s
    

zTestResponse.__contains__c             O   s   d|kr(|d }|d= t |tr,|g}ng }|r8tdx8|D ]0}|| kr>td|  tt|  td| q>W x8|D ]0}|| krxtd|  tt|  td| qxW dS )aE  mustcontain(*strings, no=[])

        Assert that the response contains all of the strings passed
        in as arguments.

        Equivalent to::

            assert string in res

        Can take a `no` keyword argument that can be a string or a
        list of strings which must not be present in the response.
        noz)The only keyword argument allowed is 'no'zActual response (no %r):zBody does not contain string %rzActual response (has %r)zBody contains bad string %rN)rd   r	   r   r   rF   r]   )r   stringsr/   r   rR   Zno_sr   r   r   mustcontainQ  s*    


zTestResponse.mustcontainc             C   s   t ddd | j D }dd | jD }|  t d| jt ddd |D |f }ts~t|t	r~|
| jpxdd}|S )	N
c             S   s   g | ]}|  r|qS r   )strip)r    lr   r   r   r"   v  s    z(TestResponse.__str__.<locals>.<listcomp>c             S   s(   g | ] \}}|  d kr| |fqS )zcontent-length)rn   title)r    nvr   r   r   r"   x  s   zResponse: %s
%s
%sc             S   s    g | ]\}}t d ||f qS )z%s: %s)rF   )r    r   r   r   r   r   r"   ~  s    r   r   )rF   r_   r   
splitlinesZ
headerlistsortr3   r   rd   r   re   r   )r   Zsimple_bodyr)   outputr   r   r   __str__u  s    
zTestResponse.__str__c             C   s"   t | }tr|S || jpddS )Nr   r   )rF   r   r   r   )r   r   r   r   r   __unicode__  s    zTestResponse.__unicode__c             C   s   | j rd| j  }nd}| jrjt| j}t|dkr`|d d d |dd   }|dt| j 7 }d| }nd	}| jrd
| j }nd}d| j | | | d S )Nz %srU      
   z...z/%sz body=%sz no bodyz location: %s<>)rk   r   rV   r^   r(   r3   )r   ctbrr   r(   r   r   r   __repr__  s    

zTestResponse.__repr__c             C   s*   d| j krtd| j  t| j| j}|S )z
        Returns the response as a `BeautifulSoup
        <https://www.crummy.com/software/BeautifulSoup/bs3/documentation.html>`_
        object.

        Only works with HTML responses; other content-types raise
        AttributeError.
        r#   z,Not an HTML response body (content-type: %s))rk   r~   r   r   r%   )r   Zsoupr   r   r   r#     s    


zTestResponse.htmlc             C   s   d| j krtd| j  yddlm} W nd tk
r   yddl}W nB tk
r   yddlm} W n tk
r   tdY nX Y nX Y nX || jS )z
        Returns the response as an :mod:`ElementTree
        <python:xml.etree.ElementTree>` object.

        Only works with XML responses; other content-types raise
        AttributeError
        xmlz+Not an XML response body (content-type: %s)r   )ElementTreeNzKYou must have ElementTree installed (or use Python 2.5) to use response.xml)rk   r~   Z	xml.etreer   ImportErrorZelementtreeXMLr   )r   r   r   r   r   r     s     	

zTestResponse.xmlc             C   s   d| j kr"d| j kr"td| j  yddlm} W n tk
rN   tdY nX yddlm} W n tk
rz   |j}Y nX | j dkr|| j| j	j
d	S |j| j| j	j
d	S d
S )ah  
        Returns the response as an `lxml object <https://lxml.de/>`_.
        You must have lxml installed to use this.

        If this is an HTML response and you have lxml 2.x installed,
        then an ``lxml.html.HTML`` object will be returned; if you
        have an earlier version of lxml then a ``lxml.HTML`` object
        will be returned.
        r#   r   z3Not an XML or HTML response body (content-type: %s)r   )etreez1You must have lxml installed to use response.lxml)
fromstringz	text/html)base_urlN)rk   r~   lxmlr   r   Z	lxml.htmlr   ZHTMLr   r+   r,   r   )r   r   r   r   r   r   r     s"    



zTestResponse.lxmlc             C   s    | j dstd| j  | jS )zx
        Return the response as a JSON response.
        The content type must be one of json type to use this.
        )z+jsonz/jsonz+Not a JSON response body (content-type: %s))rk   endswithr~   Z	json_body)r   r   r   r   json  s
    
zTestResponse.jsonc             C   s^   d| j kr"d| j kr"td| j  yddlm} W n tk
rN   tdY nX || j}|S )z
        Returns the response as a `PyQuery
        <https://pypi.org/project/pyquery/>`_ object.

        Only works with HTML and XML responses; other content-types raise
        AttributeError.
        r#   r   z3Not an HTML or XML response body (content-type: %s)r   )PyQueryz7You must have PyQuery installed to use response.pyquery)rk   r~   pyqueryr   r   r   )r   r   rW   r   r   r   r     s    	

zTestResponse.pyqueryc             C   s   ddl }ddl}|jddd}|j}|  t|d}trV|| j	| j
pLdd n|| j |  |d d	krd
| }nd| }|| dS )z{
        Show this response in a browser window (for debugging purposes,
        when it's hard to read the HTML).
        r   Nzwebtest-pagez.html)prefixsuffixwr   r   /zfile:///zfile://)
webbrowsertempfileNamedTemporaryFilenamecloseopenr   writer   r   r   Zopen_new)r   r   r   r!   r   r,   r   r   r   showbrowser  s    

zTestResponse.showbrowser)NNNNFN)NNNNF)r.   )(__name__
__module____qualname____doc__r+   r   r%   propertyr   r   r   rN   rO   SIZ_tag_rer   r0   r4   r7   rL   rP   rD   rG   r   ry   r{   r   r   r   r   r   r   r   r#   r   r   r   r   r   r   r   r   r   r      sD   
	  
. 
D
-	
$r   )rN   r   r   Zwebtestr   r   Zwebtest.compatr   r   r   r   sixr	   r
   r   Zbs4r   ZwebobResponser   r   r   r   r   <module>   s   