o
    .j^:                     @  s0  U d dl mZ d dlmZ d dlmZ d dlmZmZm	Z	 d dl
mZmZ h dZded< dLddZdMddZdNddZdOddZdPddZdQddZdRd d!ZdNd"d#ZdSd'd(ZdLd)d*ZdLd+d,ZdTd/d0ZdUd2d3ZdVd5d6ZdWd7d8ZdXd9d:ZdYd>d?ZdXd@dAZ dXdBdCZ!dZdDdEZ"d[dIdJZ#dKS )\    )annotations)decode_header)dumps)findallsearchsub)AnyIterable>   as_in_is_and_def_for_not_elif_from_pass_break_while_yield_assert_except_return_finally_zset[str]RESERVED_KEYWORDstringstrreturnc                 C  s   |   ddS )z
    Normalize a string by applying on it lowercase and replacing '-' to '_'.
    >>> normalize_str("Content-Type")
    'content_type'
    >>> normalize_str("X-content-type")
    'x_content_type'
    -_)lowerreplacer    r$   X/home/thesage/.local/lib/python3.10/site-packages/niquests/_vendor/kiss_headers/utils.pynormalize_str      r&   strings	list[str]c                 C  s   t tt| S )zJNormalize a list of string by applying fn normalize_str over each element.)listmapr&   )r(   r$   r$   r%   normalize_list(   s   r,   namec                 C  sL   t | dk r| S | d dkr| d  r| dd } | tv r$| dd } | S )a;  
    By choice, this project aims to allow developers to access header or attribute in header by using the property
    notation. Some keywords are protected by the language itself. So :
    When starting by a number, prepend an underscore to it. When using a protected keyword, append an underscore to it.
    >>> unpack_protected_keyword("_3to1")
    '3to1'
    >>> unpack_protected_keyword("from_")
    'from'
    >>> unpack_protected_keyword("_from")
    '_from'
    >>> unpack_protected_keyword("3")
    '3'
    >>> unpack_protected_keyword("FroM_")
    'FroM_'
       r   r       N)lenisdigitr   r-   r$   r$   r%   unpack_protected_keyword-   s   r4   type_type
str | Nonec                 C  s   t dt| }|r|d S dS )z5
    Typically extract a class name from a Type.
    z<class '([a-zA-Z0-9._]+)'>r   N)r   r   )r5   rr$   r$   r%   extract_class_nameI   s   r9   	delimiterc           	      C  sN  t |dks
|dvrtdd}d}d}d}dg}t| tdt | D ]u\}}|dkr5| }|r4|s4d}n |dkr>|s>d	}n|d
krG|rGd}n|dkoT| |d | dv }|sr|s`|dkr`d	}n|dkrh|rhd}|rr||krr|srd}||kr|p}|p}|p}|du r|d   |d< |d q#|d  |7  < q#|r|d   |d< |S )ak  
    Take a string and split it according to the passed delimiter.
    It will ignore delimiter if inside between double quote, inside a value, or in parenthesis.
    The input string is considered perfectly formed. This function does not split coma on a day
    when attached, see "RFC 7231, section 7.1.1.2: Date".
    >>> header_content_split("Wed, 15-Apr-2020 21:27:31 GMT, Fri, 01-Jan-2038 00:00:00 GMT", ",")
    ['Wed, 15-Apr-2020 21:27:31 GMT', 'Fri, 01-Jan-2038 00:00:00 GMT']
    >>> header_content_split('quic=":443"; ma=2592000; v="46,43", h3-Q050=":443"; ma=2592000, h3-Q049=":443"; ma=2592000', ",")
    ['quic=":443"; ma=2592000; v="46,43"', 'h3-Q050=":443"; ma=2592000', 'h3-Q049=":443"; ma=2592000']
    >>> header_content_split("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0", ";")
    ['Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0']
    >>> header_content_split("text/html; charset=UTF-8", ";")
    ['text/html', 'charset=UTF-8']
    >>> header_content_split('text/html; charset="UTF-\"8"', ";")
    ['text/html', 'charset="UTF-"8"']
    r/   >    ,;z9Delimiter should be either semi-colon, a coma or a space.F r   "(T)   >   FriMonSatSunThuTueWed=r=   r0   )r1   
ValueErrorziprangelstriprstripappend)	r   r:   in_double_quotein_parenthesisin_valueis_on_a_dayresultletterindexr$   r$   r%   header_content_splitQ   sD   

rX   c                 C  s   t | dr| jdur| jS t| dd dd }|dr&|dd }|dr1|dd }d	}|D ]}| rF|d	krF|d
| 7 }q5||7 }q5|S )a_  
    Take a type and infer its header name.
    >>> from kiss_headers.builder import ContentType, XContentTypeOptions, BasicAuthorization
    >>> class_to_header_name(ContentType)
    'Content-Type'
    >>> class_to_header_name(XContentTypeOptions)
    'X-Content-Type-Options'
    >>> class_to_header_name(BasicAuthorization)
    'Authorization'
    __override__N'.r0   r    r/   r>   r   )hasattrrY   r   splitendswith
startswithisupper)r5   class_raw_nameheader_namerV   r$   r$   r%   class_to_header_name   s   


rd   	root_typec              	   C  s   t | dd}| D ]:}t|}|du rqt|dr!|jdus0t |dd |kr0|  S | rFzt| |W   S  tyE   Y qw qtd|  d)	a  
    The opposite of class_to_header_name function. Will raise TypeError if no corresponding entry is found.
    Do it recursively from the root type.
    >>> from kiss_headers.builder import CustomHeader, ContentType, XContentTypeOptions, LastModified, Date
    >>> header_name_to_class("Content-Type", CustomHeader)
    <class 'kiss_headers.builder.ContentType'>
    >>> header_name_to_class("Last-Modified", CustomHeader)
    <class 'kiss_headers.builder.LastModified'>
    r    r>   NrY   r\   r0   z+Cannot find a class matching header named 'z'.)	r&   r"   __subclasses__r9   r]   rY   r^   header_name_to_class	TypeError)r-   re   normalized_namesubclass
class_namer$   r$   r%   rg      s$   
rg   c                 C  s"   d dd | dddD S )a#  
    Take a header name and prettify it.
    >>> prettify_header_name("x-hEllo-wORLD")
    'X-Hello-World'
    >>> prettify_header_name("server")
    'Server'
    >>> prettify_header_name("contEnt-TYPE")
    'Content-Type'
    >>> prettify_header_name("content_type")
    'Content-Type'
    r   c                 S  s   g | ]}|  qS r$   )
capitalize).0elr$   r$   r%   
<listcomp>   s    z(prettify_header_name.<locals>.<listcomp>r    )joinr"   r^   r3   r$   r$   r%   prettify_header_name   s   "rq   itemsIterable[tuple[str, Any]]list[tuple[str, str]]c                 C  sv   t  }| D ]3\}}d}t|D ]!\}}t|tr||7 }t|tr0||j|dur*|nddd7 }q|||f q|S )u  
    This function takes a list of tuples, representing headers by key, value. Where value is bytes or string containing
    (RFC 2047 encoded) partials fragments like the following :
    >>> decode_partials([("Subject", "=?iso-8859-1?q?p=F6stal?=")])
    [('Subject', 'pöstal')]
    r>   Nutf-8ignore)errors)r*   r   
isinstancer   bytesdecoderP   )rr   revised_itemsheadcontentrevised_contentpartialpartial_encodingr$   r$   r%   decode_partials   s   

r   c                 C  sD   t | dkr| dr| ds| dr | dr | dd S | S )z
    Remove simple quote or double quote around a string if any.
    >>> unquote('"hello"')
    'hello'
    >>> unquote('"hello')
    '"hello'
    >>> unquote('"a"')
    'a'
    >>> unquote('""')
    ''
    r.   r?   rZ   r/   r0   )r1   r`   r_   r#   r$   r$   r%   unquote	  s   r   c                 C  s   dt |  d S )z
    Surround string by a double quote char.
    >>> quote("hello")
    '"hello"'
    >>> quote('"hello')
    '""hello"'
    >>> quote('"hello"')
    '"hello"'
    r?   )r   r#   r$   r$   r%   quote  s   
r   r}   intc                 C  s"   |  drt| dd d S dS )a  
    A recursive function that counts trailing white space at the end of the given string.
    >>> count_leftover_space("hello   ")
    3
    >>> count_leftover_space("byebye ")
    1
    >>> count_leftover_space("  hello ")
    1
    >>> count_leftover_space("  hello    ")
    4
    r;   Nr0   r/   r   )r_   count_leftover_spacer}   r$   r$   r%   r   ,  s   
r   elemc                 C  s   d}z|  |}W n ty   |  Y S w |t| }dt| d|  | }z|| |d  d }W n	 ty=   Y nw | ||durM| || d nd ddd} | drc| dd } | drn| dd } | S )aJ  
    Remove a member for a given header content and take care of the unneeded leftover semi-colon.
    >>> header_strip("text/html; charset=UTF-8; format=flowed", "charset=UTF-8")
    'text/html; format=flowed'
    >>> header_strip("text/html; charset=UTF-8;    format=flowed", "charset=UTF-8")
    'text/html; format=flowed'
    Nr;   r=   r>   r/   r0   )	rW   rK   r1   r   r"   rO   rN   r`   r_   )r}   r   next_semi_colon_index
elem_indexelem_end_indexr$   r$   r%   header_strip=  s:   	

r   boolc                 C  s   | dko
t d| du S )a  
    Verify if a provided header name is valid.
    >>> is_legal_header_name(":hello")
    False
    >>> is_legal_header_name("hello")
    True
    >>> is_legal_header_name("Content-Type")
    True
    >>> is_legal_header_name("Hello;")
    False
    >>> is_legal_header_name("Hello\rWorld")
    False
    >>> is_legal_header_name("Hello \tWorld")
    False
    >>> is_legal_header_name('Hello World"')
    False
    >>> is_legal_header_name("Hello-World/")
    True
    >>> is_legal_header_name("")
    False
    r>   z+[^\x21-\x7F]|[:;(),<>=@?\[\]\r\n\t &{}\"\\]N)r   r3   r$   r$   r%   is_legal_header_namem  s   r   c                 C  s
   t d| S )a  
    Extract parts of content that are considered as comments. Between parenthesis.
    >>> extract_comments("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0 (hello) llll (abc)")
    ['Macintosh; Intel Mac OS X 10.9; rv:50.0', 'hello', 'abc']
    z\(([^)]+)\))r   r   r$   r$   r%   extract_comments  s   
r   c                 C  s   t dd| S )aL  Some header content may have folded content (CRLF + n spaces) in it, making your job at reading them a little more difficult.
    This function undoes the folding in the given content.
    >>> unfold("___utmvbtouVBFmB=gZg\r\n    XbNOjalT: Lte; path=/; Max-Age=900")
    '___utmvbtouVBFmB=gZg XbNOjalT: Lte; path=/; Max-Age=900'
    z\r\n[ ]+r;   )r   r   r$   r$   r%   unfold  s   r   payloadry   tuple[str, bytes]c              	   C  s   d}|   }d}t|tdt|D ],\}}|dkr)|d||d d f  S z||dd 7 }W q ty>   Y  nw |d||d d fS )	u  This function's purpose is to extract lines that can be decoded using the UTF-8 decoder.
    >>> extract_encoded_headers("Host: developer.mozilla.org\r\nX-Hello-World: 死の漢字\r\n\r\n".encode("utf-8"))
    ('Host: developer.mozilla.org\r\nX-Hello-World: 死の漢字\r\n', b'')
    >>> extract_encoded_headers("Host: developer.mozilla.org\r\nX-Hello-World: 死の漢字\r\n\r\nThat IS totally random.".encode("utf-8"))
    ('Host: developer.mozilla.org\r\nX-Hello-World: 死の漢字\r\n', b'That IS totally random.')
    r>   r       s   
r/   Nru   z
)
splitlinesrL   rM   r1   rp   rz   UnicodeDecodeError)r   rU   linesrW   liner$   r$   r%   extract_encoded_headers  s   r   c                 C  s   |  ddS )z
    Replace escaped double quote in content by removing the backslash.
    >>> unescape_double_quote(r'UTF"-8')
    'UTF"-8'
    >>> unescape_double_quote(r'UTF"-8')
    'UTF"-8'
    \"r?   )r"   r   r$   r$   r%   unescape_double_quote  s   r   c                 C  s   t | ddS )z
    Replace not escaped double quote in content by adding a backslash beforehand.
    >>> escape_double_quote(r'UTF\"-8')
    'UTF\\"-8'
    >>> escape_double_quote(r'UTF"-8')
    'UTF\\"-8'
    r?   r   )r   r"   r   r$   r$   r%   escape_double_quote  r'   r   c                 C  s0   |   } | dr| dp| do| dS )zj
    Sometime, you may receive a header that hold a JSON list or object.
    This function detect it.
    {}[])stripr`   r_   r   r$   r$   r%   is_content_json_object  s   r   headers)Iterable[tuple[str | bytes, str | bytes]]Iterable[tuple[str, str]]c                 C  s   g }| D ]<\}}|d u rqt |tr|d}t |tr"|d}nt |tdu r9t |ttfr5t|}nt|}|||f q|S )Nutf_8F)rx   ry   rz   r   dictr*   r   rP   )r   decodedkvr$   r$   r%   transform_possible_encoded  s   



r   N)r   r   r   r   )r(   r)   r   r)   )r-   r   r   r   )r5   r6   r   r7   )r   r   r:   r   r   r)   )r5   r6   r   r   )r-   r   re   r6   r   r6   )rr   rs   r   rt   )r}   r   r   r   )r}   r   r   r   r   r   )r-   r   r   r   )r}   r   r   r)   )r}   r   r   r   )r   ry   r   r   )r}   r   r   r   )r   r   r   r   )$
__future__r   email.headerr   jsonr   rer   r   r   typingr   r	   r   __annotations__r&   r,   r4   r9   rX   rd   rg   rq   r   r   r   r   r   r   r   r   r   r   r   r   r   r$   r$   r$   r%   <module>   s6    





J
!
$





0

	
	


