B
    `_E                 @   sB  d Z ddlmZ ddlZ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 ed	Zed
ZdZereefpefZedZdd ZG dd deZd-ddZG dd deZG dd deZG dd deZG dd deZdd Zdd Zdd  Zd!d" Zd#d$ Z d%d& Z!d'd( Z"d)d* Z#d+d, Z$dgZ%dS ).a  
Middleware to check for obedience to the WSGI specification.

Some of the things this checks:

* Signature of the application and start_response (including that
  keyword arguments are not used).

* Environment checks:

  - Environment is a dictionary (and not a subclass).

  - That all the required keys are in the environment: REQUEST_METHOD,
    SERVER_NAME, SERVER_PORT, wsgi.version, wsgi.input, wsgi.errors,
    wsgi.multithread, wsgi.multiprocess, wsgi.run_once

  - That HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH are not in the
    environment (these headers should appear as CONTENT_LENGTH and
    CONTENT_TYPE).

  - Warns if QUERY_STRING is missing, as the cgi module acts
    unpredictably in that case.

  - That CGI-style variables (that don't contain a .) have
    (non-unicode) string values

  - That wsgi.version is a tuple

  - That wsgi.url_scheme is 'http' or 'https' (@@: is this too
    restrictive?)

  - Warns if the REQUEST_METHOD is not known (@@: probably too
    restrictive).

  - That SCRIPT_NAME and PATH_INFO are empty or start with /

  - That at least one of SCRIPT_NAME or PATH_INFO are set.

  - That CONTENT_LENGTH is a positive integer.

  - That SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should
    be '/').

  - That wsgi.input has the methods read, readline, readlines, and
    __iter__

  - That wsgi.errors has the methods flush, write, writelines

* The status is a string, contains a space, starts with an integer,
  and that integer is in range (> 100).

* That the headers is a list (not a subclass, not another kind of
  sequence).

* That the items of the headers are tuples of 'native' strings (i.e.
  bytestrings in Python2, and unicode strings in Python3).

* That there is no 'status' header (that is used in CGI, but not in
  WSGI).

* That the headers don't contain newlines or colons, end in _ or -, or
  contain characters codes below 037.

* That Content-Type is given if there is content (CGI often has a
  default content type, but WSGI does not).

* That no Content-Type is given when there is no content (@@: is this
  too restrictive?)

* That the exc_info argument to start_response is a tuple or None.

* That all calls to the writer are with strings, and no other methods
  on the writer are accessed.

* That wsgi.input is used properly:

  - .read() is called with zero or one argument

  - That it returns a string

  - That readline, readlines, and __iter__ return strings

  - That .close() is not called

  - No other methods are provided

* That wsgi.errors is used properly:

  - .write() and .writelines() is called with a string, except
    with python3

  - That .close() is not called, and no other methods are provided.

* The response iterator:

  - That it is not a string (it should be a list of a single string; a
    string will work, but perform horribly).

  - That .next() returns a string

  - That the iterator is not iterated over until start_response has
    been called (that can signal either a server or application
    error).

  - That .close() is called (doesn't raise exception, only prints to
    sys.stderr, because we only know it isn't called when the object
    is garbage collected).

    )unicode_literalsN)PY3)binary_type)string_types)	text_type)Iterablez^[a-zA-Z][a-zA-Z0-9\-_]*$z[\000-\037])GETHEADPOSTOPTIONSPUTDELETETRACEPATCH/c             C   s   t | ts| dS | S d S )Nlatin1)
isinstancer   decode)value r   T/home/kop/projects/devel/pgwui/test_venv/lib/python3.7/site-packages/webtest/lint.py	to_string   s    

r   c               @   s   e Zd ZdZdS )WSGIWarningz:
    Raised in response to WSGI-spec-related warnings
    N)__name__
__module____qualname____doc__r   r   r   r   r      s   r   c                s    fdd}|S )a  
    When applied between a WSGI server and a WSGI application, this
    middleware will check for WSGI compliancy on a number of levels.
    This middleware does not modify the request or response in any
    way, but will throw an AssertionError if anything seems off
    (except for a failure to close the application iterator, which
    will be printed to stderr -- there's no way to throw an exception
    at that point).
    c                 s   t | dkstd|r td| \} t| g  fdd}t|d |d< t|d |d< ||}t|ts~tdt| t|S )	N   zTwo arguments requiredzNo keyword arguments allowedc                 s   t | dks$t | dks$td|  |r0td| d }| d }t | dkrV| d }nd }t| t| t|| t| d  t |  S )Nr      zInvalid number of arguments: %szNo keyword arguments allowedr      )lenAssertionErrorcheck_statuscheck_headerscheck_content_typecheck_exc_infoappendWriteWrapper)argskwstatusheadersexc_info)start_responsestart_response_startedr   r   start_response_wrapper   s    



z<middleware.<locals>.lint_app.<locals>.start_response_wrapperz
wsgi.inputzwsgi.errorsz>The application must return an iterator, if only an empty list)	r    r!   check_environInputWrapperErrorWrapperr   r   check_iteratorIteratorWrapper)r(   r)   environr/   iterator)application)r-   r.   r   lint_app   s    

zmiddleware.<locals>.lint_appr   )r7   Zglobal_confr8   r   )r7   r   
middleware   s    +r9   c               @   sD   e Zd Zdd Zdd Zdd Zdd Zd	d
 Zdd Zdd Z	dS )r1   c             C   s
   || _ d S )N)input)self
wsgi_inputr   r   r   __init__   s    zInputWrapper.__init__c             G   s0   t |dkst| jj| }t|tks,t|S )Nr   )r    r!   r:   readtyper   )r;   r(   vr   r   r   r>      s    zInputWrapper.readc             G   s    | j j| }t|tkst|S )N)r:   readliner?   r   r!   )r;   r(   r@   r   r   r   rA      s    zInputWrapper.readlinec             G   sL   t |dkst| jj| }t|ts*tx|D ]}t|tks0tq0W |S )Nr   )r    r!   r:   	readlinesr   listr?   r   )r;   r(   linesliner   r   r   rB      s    
zInputWrapper.readlinesc             c   s    x|   }|sd S |V  qW d S )N)rA   )r;   rE   r   r   r   __iter__   s
    zInputWrapper.__iter__c             C   s   t dd S )Nz input.close() must not be called)r!   )r;   r   r   r   close   s    zInputWrapper.closec             O   s   | j j||S )N)r:   seek)r;   ar)   r   r   r   rH      s    zInputWrapper.seekN)
r   r   r   r=   r>   rA   rB   rF   rG   rH   r   r   r   r   r1      s   r1   c               @   s4   e Zd Zdd Zdd Zdd Zdd Zd	d
 ZdS )r2   c             C   s
   || _ d S )N)errors)r;   wsgi_errorsr   r   r   r=      s    zErrorWrapper.__init__c             C   s$   t st|tkst| j| d S )N)r   r?   r   r!   rJ   write)r;   sr   r   r   rL     s    zErrorWrapper.writec             C   s   | j   d S )N)rJ   flush)r;   r   r   r   rN     s    zErrorWrapper.flushc             C   s   x|D ]}|  | qW d S )N)rL   )r;   seqrE   r   r   r   
writelines	  s    
zErrorWrapper.writelinesc             C   s   t dd S )Nz!errors.close() must not be called)r!   )r;   r   r   r   rG     s    zErrorWrapper.closeN)r   r   r   r=   rL   rN   rP   rG   r   r   r   r   r2      s
   r2   c               @   s   e Zd Zdd Zdd ZdS )r'   c             C   s
   || _ d S )N)writer)r;   Zwsgi_writerr   r   r   r=     s    zWriteWrapper.__init__c             C   s   t |tkst| | d S )N)r?   r   r!   rQ   )r;   rM   r   r   r   __call__  s    zWriteWrapper.__call__N)r   r   r   r=   rR   r   r   r   r   r'     s   r'   c               @   s8   e Zd Zdd Zdd Zdd ZeZdd Zd	d
 ZdS )r4   c             C   s    || _ t|| _d| _|| _d S )NF)original_iteratoriterr6   closedcheck_start_response)r;   Zwsgi_iteratorrV   r   r   r   r=     s    
zIteratorWrapper.__init__c             C   s   | S )Nr   )r;   r   r   r   rF   #  s    zIteratorWrapper.__iter__c             C   sX   | j rtdt| j}| jd k	r6| js0tdd | _t|tsTtd| jt|f |S )NzIterator read after closedzjThe application returns and we started iterating over its body, but start_response has not yet been calledz(Iterator %r returned a non-%r object: %r)rU   r!   nextr6   rV   r   r   )r;   r@   r   r   r   rW   &  s    

zIteratorWrapper.nextc             C   s    d| _ t| jdr| j  d S )NTrG   )rU   hasattrrS   rG   )r;   r   r   r   rG   6  s    zIteratorWrapper.closec             C   s   | j stdd S )Nz/Iterator garbage collected without being closed)rU   r!   )r;   r   r   r   __del__;  s    zIteratorWrapper.__del__N)	r   r   r   r=   rF   rW   __next__rG   rY   r   r   r   r   r4     s   r4   c             C   s  t | tk	r tdt | | f x dD ]}|| kr&td| q&W x,dD ]$}|| krHtd||dd  f qHW d| krtdt xD| D ]<}d	|krqt | | tkrtd
|t | | | | f qW t | d tk	rtd| d  | d dkrtd| d  t| d  t	| d  | d t
krBtd| d  t | drn| d tsntd| d  | dr| d tstd| d  | drt| d dk rtd| d  | dsd| krtd| dtkrtdd S )Nz:Environment is not of the right type: %r (environment: %r))	REQUEST_METHODZSERVER_NAMEZSERVER_PORTzwsgi.versionz
wsgi.inputzwsgi.errorszwsgi.multithreadzwsgi.multiprocesszwsgi.run_oncez$Environment missing required key: %r)ZHTTP_CONTENT_TYPEZHTTP_CONTENT_LENGTHz8Environment should not have the key: %s (use %s instead)   QUERY_STRINGzQUERY_STRING is not in the WSGI environment; the cgi module will use sys.argv when this variable is missing, so application errors are more likely.z9Environmental variable %s is not a string: %r (value: %r)zwsgi.versionz#wsgi.version should be a tuple (%r)zwsgi.url_scheme)httphttpszwsgi.url_scheme unknown: %rz
wsgi.inputzwsgi.errorsr[   zUnknown REQUEST_METHOD: %rZSCRIPT_NAMEz$SCRIPT_NAME doesn't start with /: %rZ	PATH_INFOz"PATH_INFO doesn't start with /: %rCONTENT_LENGTHr   zInvalid CONTENT_LENGTH: %rzgOne of SCRIPT_NAME or PATH_INFO are required (PATH_INFO should at least be '/' if SCRIPT_NAME is empty)zOSCRIPT_NAME cannot be '/'; it should instead be '', and PATH_INFO should be '/')r?   dictr!   warningswarnr   METADATA_TYPEtuplecheck_inputcheck_errorsvalid_methodsget
startswithSLASHint)r5   keyr   r   r   r0   @  sh    




r0   c             C   s,   x&dD ]}t | |std| |f qW d S )N)r>   rA   rB   rF   z-wsgi.input (%r) doesn't have the attribute %s)rX   r!   )r<   attrr   r   r   rg     s
    

rg   c             C   s,   x&dD ]}t | |std| |f qW d S )N)rN   rL   rP   z.wsgi.errors (%r) doesn't have the attribute %s)rX   r!   )rK   ro   r   r   r   rh     s
    

rh   c             C   s   t | tkrtdt| f t| } t| dkr<td|  | d d  sXtd|  t| d d }|dk r|td| | d dkrtd	|  d S )
NzStatus must be a %s (not %r)r\   zjThe status string (%r) should be a three-digit integer followed by a single space and a status explanationr   z5The status string (%r) should start with three digitsd   z:The status code must be greater or equal than 100 (got %d) zeThe status string (%r) should start with threedigits and a space (4th characters is not a space here))r?   re   r!   r   r    isdigitrm   )r*   Z
status_intr   r   r   r"     s$    

r"   c             C   sP   t | tk	rt|t | tkrLy| d W n tk
rJ   t|Y nX | S )Nr   )r?   strr!   r   encodeUnicodeEncodeError)stringmessager   r   r   _assert_latin1_str  s    rx   c             C   s  t | tk	r td| t | f x| D ]}t |tk	rJtd|t |f t|dksZt|\}}t|d|f  | dkrtd| d|ksd|krtd	| t|std
| |	ds|	drtd| t|d|f  t
|r&td|t
|df q&W d S )Nz%Headers (%r) must be of type list: %rz1Individual headers (%r) must be of type tuple: %rr   zgHeader names must be latin1 string (not Py2 unicode or Py3 bytes type). %r is not a valid latin1 stringr*   zyThe Status header cannot be used; it conflicts with CGI script, and HTTP status is not given through headers (value: %r).
:z,Header names may not contain ':' or '\n': %rzBad header name: %r-_z#Names may not end in '-' or '_': %rzgHeader values must be latin1 string (not Py2 unicode or Py3 bytes type).%r is not a valid latin1 stringz#Bad header value: %r (bad char: %r)r   )r?   rC   r!   rf   r    rx   lower	header_researchendswithbad_header_value_regroup)r+   itemnamer   r   r   r   r#     sB    





r#   c             C   s   t | d dd }d}d}d }x.|D ]&\}}| dkr&| r&t |}P q&W xL|D ]D\}}| dkrV||krvd S |std| t d S td| qVW ||kr|d k	r|dkrtd	| d S )
Nr   r   )      i0  )r   i0  zcontent-lengthzcontent-typezEContent-Type header found in a %s response, which not return content.zJContent-Type header found in a %s response, which must not return content.z,No Content-Type header found in headers (%s))rm   splitr}   rr   rc   rd   r   r!   )r*   r+   codeZNO_MESSAGE_BODYZNO_MESSAGE_TYPElengthr   r   r   r   r   r$   
  s.    r$   c             C   s,   | d k	r(t | tk	r(td| t | f d S )Nz exc_info (%r) is not a tuple: %r)r?   rf   r!   )r,   r   r   r   r%   *  s    r%   c             C   s"   t rtp
t}t| |rtdd S )NzuYou should not return a bytes as your application iterator, instead return a single-item list containing that string.)r   bytesrs   r   r!   )r6   Z
valid_typer   r   r   r3   2  s    
r3   )N)&r   
__future__r   rerc   sixr   r   r   r   Zwebtest.compatr   compiler~   r   ri   rs   re   rl   r   Warningr   r9   objectr1   r2   r'   r4   r0   rg   rh   r"   rx   r#   r$   r%   r3   __all__r   r   r   r   <module>t   s<   


:&
%W		6 