o
    .j>                     @  s   U 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
 ddlmZmZ ddlmZmZmZmZmZmZmZmZmZmZmZmZmZ dad	ed
< G dd dZG dd dZG dd dZ ddddZ!dS )    )annotationsdeepcopy)JSONDecodeErrordumpsloads)IterableIterator   )AttributeBagCaseInsensitiveDict)escape_double_quoteextract_commentsheader_content_splitheader_name_to_classis_content_json_objectis_legal_header_namenormalize_listnormalize_strprettify_header_nameunescape_double_quoteunfoldunpack_protected_keywordunquoteFboolOUTPUT_LOCK_TYPEc                      s   e Zd ZU dZded< ded< ded< ded< ded< ded< ded	< ded
< ded< ded< ded< ded< ded< ded< ded< dxddZedyddZedyddZedyddZ	edyddZ
edydd Zedzd"d#Zd{d'd(Zd{d)d*Zd{d+d,Zd{d-d.Zd|d1d2Zd}d~d7d8Zdd>d?ZddAdBZddDdEZddFdGZddHdIZd fdLdMZddNdOZddPdQZddSdTZddVdWZd{dXdYZdydZd[Zdyd\d]Zdd_d`Z d fdbdcZ!edzdddeZ"edzdfdgZ#ddidjZ$ddldmZ%ddndoZ&ddrdsZ'ddtduZ(ddvdwZ)  Z*S )Headerz3
    Object representation of a single Header.
    strcharsetformatboundaryexpirestimeoutmaxpathsamesitedomainfilenameto	report_to	endpointsmax_agegroupnamecontentc              
   C  s$  t |std| d|| _t| j| _t| j| _|| _|  t| jrzt	| j}W n t
yB } z
td| j d|d}~ww t|trL|| _n>t|trzg | _| D ] \}}|durp| jt| dt|  qX| jt| qXntd| j dt| jd| _t| j| _dS )	z
        :param name: The name of the header, should contain only ASCII characters with no spaces in it.
        :param content: Initial content associated with the header.
        'z5' is not a valid header name. Cannot proceed with it.zHeader 'z ' contain an invalid JSON value.N=z' is malformed.;)r   
ValueError_namer   _normalized_namer   _pretty_name_contentr   r   r   
isinstancelist_membersdictitemsappendr   r   
Attributes_attrs)selfr-   r.   payloadekv rD   Y/home/thesage/.local/lib/python3.10/site-packages/niquests/_vendor/kiss_headers/models.py__init__2   s@   



 zHeader.__init__returnc                 C     | j S )zO
        Output the original header name as it was captured initially.
        )r3   r?   rD   rD   rE   r-   [      zHeader.namec                 C  rH   )z]
        Output header name but normalized, lower case and '-' character become '_'.
        )r4   rI   rD   rD   rE   normalized_nameb   rJ   zHeader.normalized_namec                 C  rH   )zd
        Output a prettified name of the header. The first letter capitalized on each word.
        )r5   rI   rD   rD   rE   pretty_namei   rJ   zHeader.pretty_namec                 C  s   t | jdkrt| jS | jS )z
        Output associated content to the header as it was captured initially.
        >>> header = Header("ETag", '"33a64df551425fcc55e4d42a148795d9f25f89d4"')
        >>> header.content
        '33a64df551425fcc55e4d42a148795d9f25f89d4'
        r
   )lenattrsr   r6   rI   rD   rD   rE   r.   p   s   	
zHeader.contentc                 C  
   t | jS )zxOutput unfolded associated content to the header. Meaning that every LF + n space(s) would be properly
        replaced.)r   r.   rI   rD   rD   rE   unfolded_content~      
zHeader.unfolded_content	list[str]c                 C  rO   )z$Retrieve comments in header content.)r   r.   rI   rD   rD   rE   comments   s   
zHeader.commentsotherobjectr   c                 C  s   t |tst| j|jk S )z
        This method is only implemented to make sorted work with Header.
        The lower than is based on alphabetical order using the header name.
        >>> Header("A", "") < Header("Z", "")
        True
        r7   r   NotImplementedErrorrK   r?   rT   rD   rD   rE   __lt__   s   
zHeader.__lt__c                 C  s   t |tst| j|jkS )z
        >>> Header("A", "") <= Header("Z", "")
        True
        >>> Header("A", "") <= Header("A", "")
        True
        rV   rX   rD   rD   rE   __le__      
zHeader.__le__c                 C  s   t |tst| j|jkS )z
        This method is only implemented to make sorted work with Header.
        The greater than is based on alphabetical order using the header name.
        >>> Header("A", "") > Header("Z", "")
        False
        rV   rX   rD   rD   rE   __gt__   r[   zHeader.__gt__c                 C  s   t |tst| j|jkS )z
        >>> Header("A", "") >= Header("Z", "")
        False
        >>> Header("A", "") >= Header("A", "")
        True
        rV   rX   rD   rD   rE   __ge__   r[   zHeader.__ge__memodictr:   c                 C  s   t t| jt| jS )zaSimply provide a deepcopy of a Header object. Pointer/Reference is free of the initial reference.)r   r   r-   r.   r?   r^   rD   rD   rE   __deepcopy__   s   zHeader.__deepcopy___Header__index	int | str"tuple[str, str | list[str] | None]c                 C  s   t |tr|dkr|n|t| j }| j| \}}nt |tr(|| j| }}n
tdt| d| j|t |tr=|nd t| j| _||fS )a
  Permit to pop an element from a Header with a given index.
        >>> header = Header("X", "a; b=k; h; h; z=0; y=000")
        >>> header.pop(1)
        ('b', 'k')
        >>> header.pop()
        ('y', '000')
        >>> header.pop('z')
        ('z', '0')
        r   z"Cannot pop from Header using type .N)	r7   intrM   r>   r   r2   typeremover6   )r?   rb   keyvaluerD   rD   rE   pop   s   

z
Header.poprf   _Header__members_Header__attributes
str | NoneNonec                 O  s   |dkr|n|t | jpd }|D ]}| j|d| |d7 }q| D ]\}}| j||| |d7 }q$t| j| _t| jd| _dS )a  
        This method allows you to properly insert attributes into a Header instance. Insert before provided index.
        >>> header = Header("Content-Type", "application/json; format=flowed")
        >>> repr(header)
        'Content-Type: application/json; format=flowed'
        >>> header.insert(1, charset="UTF-8")
        >>> repr(header)
        'Content-Type: application/json; charset="UTF-8"; format="flowed"'
        r   r
   Nr1   )rM   r>   insertr;   r   r6   r   r9   )r?   rb   rl   rm   memberri   rj   rD   rD   rE   rp      s   

zHeader.insertstr | Headerc                 C  s`   t |tstdt| d| j|d |  j| j dkr$d| n|7  _| j	| | S )aC  
        Allow you to assign-add any string to a Header instance. The string will be a new member of your header.
        >>> header = Header("X-Hello-World", "")
        >>> repr(header)
        'X-Hello-World: '
        >>> header += "preload"
        >>> repr(header)
        'X-Hello-World: preload'
        >>> header += "inclSubDomain"
        >>> repr(header)
        'X-Hello-World: preload; inclSubDomain'
        >>> header += 1
        Traceback (most recent call last):
          ...
        TypeError: Cannot assign-add with type <class 'int'> to an Header.
        zCannot assign-add with type  to an Header.N ; )
r7   r   	TypeErrorrg   r>   rp   r6   lstripr9   r<   rX   rD   rD   rE   __iadd__   s   
$zHeader.__iadd__Header | Headersc                 C  s`   t |tst |tstdt| dt |tr&t }|| 7 }||7 }|S t| }||7 }|S )a  
        This implementation permits to add either a string or a Header to your Header instance.
        When you add a string to your Header instance, it will create another instance with a new
        member in it using the string; see iadd doc about it. But when its another Header the result is a Headers
        object containing both Header object.
        >>> headers = Header("X-Hello-World", "1") + Header("Content-Type", "happiness=True")
        >>> len(headers)
        2
        >>> headers.keys()
        ['X-Hello-World', 'Content-Type']
        >>> Header("Content-Type", "happiness=True") + 1
        Traceback (most recent call last):
          ...
        TypeError: Cannot make addition with type <class 'int'> to an Header.
        zCannot make addition with type rs   )r7   r   r   rv   rg   Headersr   )r?   rT   headersheader_rD   rD   rE   __add__	  s   
zHeader.__add__c                 C  sp   t |tstdtt| d|| vr!td| d| j d| jj|dd t| j| _t	| jd| _
| S )	_
        This method should allow you to remove attributes or members from the header.
        zYou cannot subtract rs   zYou cannot subtract 'z' from 'z' Header because its not there.F
with_valuer1   )r7   r   rv   rg   r2   rL   r>   rh   r6   r   r9   rX   rD   rD   rE   __isub__*  s   
zHeader.__isub__c                 C     t | }||8 }|S )r~   r   )r?   rT   r|   rD   rD   rE   __sub__=     zHeader.__sub__ri   rj   c                   s*   |dv rt  ||S t|}|| |< dS )zF
        Set attribute on header using the property notation.
        >   r3   r>   r6   r9   	__class__r5   r4   N)super__setattr__r   r?   ri   rj   r   rD   rE   r   F  s   	zHeader.__setattr__c                 C  sX   || v r| |= t |tst|}| j| | j|| t| j| _t| jd| _dS )z
        Set an attribute bracket syntax like. This will erase the previously set attribute named after the key.
        Any values that are not str are cast to str.
        r1   N)r7   r   r>   rh   rp   r6   r   r9   r   rD   rD   rE   __setitem__[  s   
zHeader.__setitem__c                 C  sX   t |t| jvrtd| d| j d| jj|dd t| j| _t	| jd| _
dS )ax  
        Remove any attribute named after the key in the header using the bracket syntax.
        >>> headers = Header("Content-Type", "text/html; charset=UTF-8") + Header("Allow", "POST")
        >>> str(headers.content_type)
        'text/html; charset=UTF-8'
        >>> del headers.content_type['charset']
        >>> str(headers.content_type)
        'text/html'
        r/   I' attribute is not defined or have at least one value associated within '	' header.Tr   r1   N)r   r   valued_attrsKeyErrorr-   r>   rh   r   r6   r   r9   )r?   ri   rD   rD   rE   __delitem__l  s   zHeader.__delitem__itemc                 C  s8   t |}|t| jvrtd| d| j d| |= dS )a  
        Remove any attribute named after the key in the header using the property notation.
        >>> headers = Header("Content-Type", "text/html; charset=UTF-8") + Header("Vary", "Content-Type")
        >>> repr(headers.content_type)
        'Content-Type: text/html; charset=UTF-8'
        >>> del headers.content_type.charset
        >>> repr(headers.content_type)
        'Content-Type: text/html'
        r/   r   r   N)r   r   r   AttributeErrorr-   r?   r   rD   rD   rE   __delattr__  s   

zHeader.__delattr__,Iterator[tuple[str, str | list[str] | None]]c                 c  s(    t dt| jD ]}| j| V  q	dS )zProvide a way to iter over a Header object. This will yield a Tuple of key, value.
        The value would be None if the key is a member without associated value.r   N)rangerM   r>   )r?   irD   rD   rE   __iter__  s   zHeader.__iter__c                 C  sl   t |tr| j|kp|| jv S t |tr,| j|jkr*t| jt|jkr*| j|jkS dS tdt| d)z
        Verify equality between a Header object and str or another Header object.
        If testing against str, the first thing is to match it to raw content, if not equal verify if not in members.
        FCannot compare type ! to an Header. Use str or Header.)	r7   r   r.   r>   r   rK   rM   rW   rg   rX   rD   rD   rE   __eq__  s   

zHeader.__eq__c                 C  rH   )z`
        Allow casting a single header to a string. Only content would be exposed here.
        )r6   rI   rD   rD   rE   __str__  s   zHeader.__str__c                 C  s   | j  d| j S )z@
        Unambiguous representation of a single header.
        z: )r3   r6   rI   rD   rD   rE   __repr__  s   zHeader.__repr__bytesc                 C     t | jdddS )z
        Provide a bytes repr of header. Warning, this output does not have an RC at the end. Any error encountered
        in encoder would be treated by 'surrogateescape' clause.
        utf-8surrogateescapeerrorsreprencoderI   rD   rD   rE   	__bytes__  s   zHeader.__bytes__Iterable[str]c                   s   t t  t| j  S )z
        Provide a better auto-completion when using a Python interpreter. We are feeding __dir__ so Python can be aware
        of what properties are callable. In other words, more precise auto-completion when not using IDE.
        )r8   r   __dir__r   r>   keysrI   r   rD   rE   r     s   zHeader.__dir__c                 C  sH   g }t | jdkr|S tdt | jD ]}| j| \}}|| q|S )z
        List of members or attributes found in provided content. This list is ordered and normalized.
        eg. Content-Type: application/json; charset=utf-8; format=origin
        Would output : ['application/json', 'charset', 'format']
        r   rM   r>   r   r<   r?   rN   r   attrrj   rD   rD   rE   rN     s   zHeader.attrsc                 C  sX   g }t | jdkr|S tdt | jD ]}| j| \}}|dur)||vr)|| q|S )a  
        List of distinct attributes that have at least one value associated with them. This list is ordered and normalized.
        This property could have been written under the keys() method, but implementing it would interfere with dict()
        cast and the __iter__() method.
        eg. Content-Type: application/json; charset=utf-8; format=origin
        Would output : ['charset', 'format']
        r   Nr   r   rD   rD   rE   r     s   	
zHeader.valued_attrsr   c                 C     || v S )zY
        Safely check if the current header has an attribute or adjective in it.
        rD   r?   r   rD   rD   rE   has     z
Header.hasstr | list[str] | Nonec                 C  s    t |t| jvrdS | j| S )aJ  
        Retrieve the associated value of an attribute.
        >>> header = Header("Content-Type", "application/json; charset=UTF-8; format=flowed")
        >>> header.charset
        'UTF-8'
        >>> header.ChArSeT
        'UTF-8'
        >>> header.FORMAT
        'flowed'
        >>> header.format
        'flowed'
        N)r   r   r   r>   r   rD   rD   rE   get  s   
z
Header.getc                 C  *   || vrdS | | }t |tot|dkS )a"  
        Determine if an attribute name has multiple entries in Header. Detect OneToMany entries.
        >>> header = Header("A", "charset=UTF-8; charset=ASCII; format=flowed")
        >>> header.has_many("charset")
        True
        >>> header.has_many("format")
        False
        Fr
   r7   r8   rM   r?   r-   rrD   rD   rE   has_many     	zHeader.has_many	str | intstr | list[str]c                 C  s   t |trts| j| S | j| gS t|t| jv r!| j| }ntd| d| j	 dtr7t |t
r7|g}t |t
rBtt|S dd |D S )z{
        This method will allow you to retrieve attribute value using the bracket syntax, list-like, or dict-like.
        r/   zK' attribute is not defined or does not have at least one value within the 'r   c                 S  s   g | ]}t t|qS rD   )r   r   ).0rC   rD   rD   rE   
<listcomp>*  s    z&Header.__getitem__.<locals>.<listcomp>)r7   rf   r   r9   r   r   r   r>   r   r-   r   r   r   )r?   r   rj   rD   rD   rE   __getitem__  s"   

zHeader.__getitem__c                 C  s:   t |}t|t| jvrtd| d| j d| | S )z
        All the magic happens here, this method should be invoked when trying to call (not declared) properties.
        For instance, calling self.charset should end up here and be replaced by self['charset'].
        r/   z>' attribute is not defined or have at least one value within 'r   )r   r   r   r   r   r-   r   rD   rD   rE   __getattr__-  s   zHeader.__getattr__c                 C  sJ   || j v rdS t|}| j D ]}t|}||ks|t|dv r" dS qdS )zW
        Verify if a string matches a member or an attribute-name of a Header.
        T F)rN   r   r   )r?   r   r   targetrD   rD   rE   __contains__;  s   

zHeader.__contains__)r-   r   r.   r   rG   r   rG   rR   rT   rU   rG   r   )r^   r:   rG   r   ra   )rb   rc   rG   rd   )rb   rf   rl   r   rm   rn   rG   ro   )rT   rr   rG   r   )rT   rr   rG   ry   )rT   r   rG   r   ri   r   rj   r   rG   ro   ri   r   rG   ro   r   r   rG   ro   )rG   r   rG   r   rG   r   )r   r   rG   r   )r   r   rG   r   r-   r   rG   r   )r   r   rG   r   )r   r   rG   r   )r   r   rG   r   )+__name__
__module____qualname____doc____annotations__rF   propertyr-   rK   rL   r.   rP   rS   rY   rZ   r\   r]   r`   rk   rp   rx   r}   r   r   r   r   r   r   r   r   r   r   r   r   rN   r   r   r   r   r   r   r   __classcell__rD   rD   r   rE   r      sz   
 
)








!
	












r   c                      s  e Zd ZU dZded< ded< ded< ded< ded< ded< ded	< ded
< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded < ded!< ded"< ded#< ded$< ded%< ded&< ded'< ded(< ded)< ded*< ded+< ded,< ded-< ded.< ded/< dd2d3Zdd8d9Zdd;d<Zdd>d?ZddAdBZ	ddDdEZ
ddGdHZddJdKZddMdNZddQdRZddTdUZddWdXZddZd[Zd fd\d]Zdd`daZddcddZddedfZddgdhZddjdkZddmdnZddodpZddqdrZddtduZddvdwZddxdyZdd{d|Zdd}d~ZdddZ dddZ!dddZ"ddddZ#ddddZ$dddZ%d fddZ&  Z'S )rz   a   
    Object-oriented representation for Headers. Contains a list of Header with some level of abstraction.
    Combine advantages of dict, CaseInsensibleDict, list, multi-dict, and native objects.
    Headers do not inherit the Mapping type, but it does borrow some concepts from it.
    Header | list[Header]access_control_allow_originwww_authenticateauthorizationproxy_authenticateproxy_authorizationalt_svclocationagecache_controlclear_site_datar!   pragmawarninglast_modifiedetagif_matchif_none_matchif_modified_sinceif_unmodified_sincevary
connection
keep_alivex_cacheviaacceptaccept_charsetaccept_encodingaccept_languageexpectcookie
set_cookiecontent_dispositioncontent_typecontent_rangecontent_encodinghostrefererreferrer_policy
user_agentallowservertransfer_encodingdatefrom_r)   r{   list[Header] | Headerc                 G  s6   t |dkrt|d tr|d | _dS t|| _dS )zG
        :param headers: Initial list of header. Can be empty.
        r
   r   N)rM   r7   r8   _headers)r?   r{   rD   rD   rE   rF     s
   zHeaders.__init__headerr   rG   r   c                 C  r   )z<
        Safely check if header name is in headers.
        rD   r?   r  rD   rD   rE   r     r   zHeaders.hasHeader | list[Header] | Nonec                 C  s   || vrdS | | S )z9
        Retrieve header from headers if exists.
        NrD   r  rD   rD   rE   r     s   zHeaders.getr-   c                 C  r   )a  
        Determine if a header name has multiple entries in Headers. Detect OneToMany entries.
        >>> headers = Header("A", "0") + Header("A", "1") + Header("B", "sad")
        >>> headers.has_many("a")
        True
        >>> headers.has_many("b")
        False
        Fr
   r   r   rD   rD   rE   r     r   zHeaders.has_manyIterator[Header]c                 c  s    | j E dH  dS )ze
        Act like a list by yielding one element at a time. Each element is a Header object.
        N)r   rI   rD   rD   rE   r     s   zHeaders.__iter__rR   c                 C  s*   t  }| D ]}|j|vr||j q|S )z
        Return a list of distinct header name set in headers.
        Be aware that it won't return a typing.KeysView.
        Also this method allows you to create a case sensitive dict.
        )r8   r-   r<   )r?   r   r  rD   rD   rE   r     s   
zHeaders.keysro   c                 C  s   t )z
        I choose not to implement values() on Headers as it would bring more confusion...
        Either we make it the same len as keys() or we don't. Either way don't please me. Hope to hear from the
        community about this.
        )rW   rI   rD   rD   rE   values  s   zHeaders.valueslist[tuple[str, str]]c                 C  s$   g }| D ]}| |j|jf q|S )a  
        Provide a list witch each entry contains a tuple of header name and content.
        This won't return an ItemView as Headers does not inherit from Mapping.
        >>> headers = Header("X-Hello-World", "1") + Header("Content-Type", "happiness=True") + Header("Content-Type", "happiness=False")
        >>> headers.items()
        [('X-Hello-World', '1'), ('Content-Type', 'happiness=True'), ('Content-Type', 'happiness=False')]
        )r<   r-   r.   )r?   r;   r  rD   rD   rE   r;     s   zHeaders.itemsr   c                 C  sL   t  }| D ]}|jdd}||vr|j||< q||  d|j 7  < q|S )a  
        Provide a CaseInsensitiveDict output of current headers. This output type has been borrowed from psf/requests.
        If one header appears multiple times, it would be concatenated into the same value, separated by a comma.
        Be aware that this repr could lead to a mistake. You could also cast a Headers instance to dict() to get a
        case sensitive one. see method keys().
        _-, )r   r-   replacer.   )r?   dict_headersr  header_name_no_underscorerD   rD   rE   to_dict  s   
zHeaders.to_dictr^   r:   c                 C  s   t t| jS )z{
        Just provide a deepcopy of the current Headers object. Pointer/reference is free of the current instance.
        )rz   r   r   r_   rD   rD   rE   r`     s   zHeaders.__deepcopy__ri   c                 C  s\   t |}g }|| vrtd| d| D ]}|j|kr || q|D ]}| j| q#dS )a+  
        Remove all matching header named after called key.
        >>> headers = Header("Content-Type", "text/html") + Header("Allow", "POST")
        >>> headers.has("Content-Type")
        True
        >>> del headers['content-type']
        >>> headers.has("Content-Type")
        False
        r/   #' header is not defined in headers.N)r   r   rK   r<   r   rh   )r?   ri   to_be_removedr  rD   rD   rE   r     s   


zHeaders.__delitem__rj   c                 C  s   t |tstd| dt| d|| v r| |= t|dkr:t|d}t|dkr:|D ]}| jt	|| q,dS | jt	|| dS )a  
        Set header using the bracket syntax. This operation would remove any existing header named after the key.
        Warning, if your value contain comma separated entries, it will split it into multiple Header instance.
        >>> headers = Headers()
        >>> headers.content_type = "application/json"
        >>> len(headers)
        1
        >>> headers.accept = "text/html, application/json;q=1.0"
        >>> len(headers)
        3
        zCannot assign header 'z' using type z to headers.subject,r
   N)
r7   r   rv   rg   r   r   rM   r   r<   r   )r?   ri   rj   entriesentryrD   rD   rE   r     s   

zHeaders.__setitem__r   c                 C  s"   || vrt d| d| |= dS )a6  
        Remove header using the property notation.
        >>> headers = Header("Content-Type", "text/html; charset=UTF-8") + Header("Vary", "Content-Type")
        >>> headers.has("Content-Type")
        True
        >>> del headers.content_type
        >>> headers.has("Content-Type")
        False
        r/   r  N)r   r   rD   rD   rE   r   1  s   

zHeaders.__delattr__c                   s"   |dkrt  ||S || |< dS )z
        Set header like it is a property/member. This operation would remove any existing header named after the key.
        r   N)r   r   r   r   rD   rE   r   @  s   zHeaders.__setattr__rT   rU   c                 C  sN   t |tstdt| dt|t| krdS | D ]	}||vr$ dS qdS )z
        Essentially compare if one Headers instance equal to another. The order does not matter and instance length matter.
        r   r   FT)r7   rz   rW   rg   rM   )r?   rT   r  rD   rD   rE   r   I  s   
zHeaders.__eq__rf   c                 C  rO   )zx
        Return number of headers. If one header appears multiple time, it is not reduced to one in this count.
        )rM   r   rI   rD   rD   rE   __len__Z  rQ   zHeaders.__len__c                 C  s   |   S )z>
        Just calling __repr__ of self. see __repr__.
        )r   rI   rD   rD   rE   r   `  r   zHeaders.__str__c              	   C  s   g }|   D ]q}| |}|std| dd}zt r't|t d nd}W n	 ty3   Y nw t|tr^t	|dkr^|r^t
|dr^|jdu r^|dj|d	d
d |D d qt|trp|D ]	}|t| qeq|t| qd|S )z
        Non-ambiguous representation of a Headers instance. Using CRLF as described in rfc2616.
        The repr of Headers will not end with blank(s) line(s). You have to add it yourself, depending on your needs.
        z$This should not happen. Cannot get 'z*' from headers when keys() said its there.Nr   r
   
__squash__Tz{name}: {content}r	  c                 S     g | ]}|j qS rD   )r.   )r   elrD   rD   rE   r         z$Headers.__repr__.<locals>.<listcomp>)r-   r.   z
)r   r   LookupErrorr   __subclasses__r   rv   r7   r8   rM   hasattrr  r<   r   joinr   )r?   resultheader_namer   target_subclassr  rD   rD   rE   r   f  sF   




zHeaders.__repr__r   c                 C  s   t | }||7 }|S )zS
        Add using syntax c = a + b. The result is a newly created object.
        r   r?   rT   r{   rD   rD   rE   r}     r   zHeaders.__add__Header | strc                 C  r   )zX
        Subtract using syntax c = a - b. The result is a newly created object.
        r   r   rD   rD   rE   r     r   zHeaders.__sub__c                 C  s2   t |tr| j| | S tdtt| d)zi
        Inline add, using operator '+'. It is only possible to add to it another Header object.
        zCannot add type "" to Headers.)r7   r   r   r<   rv   r   rg   rX   rD   rD   rE   rx     s   
zHeaders.__iadd__c                 C  s   t |tr(t|}t }| D ]}||jkr|| q|D ]}| j| q| S t |tr9|| v r7| j| | S t	dtt
| d)a  
        Inline subtract, using the operator '-'. If str is subtracted to it,
        would be looking for header named like provided str.
        Would remove any entries named 'Set-Cookies'. eg :
        >>> headers = Header("Set-Cookies", "HELLO=WORLD") + Header("Allow", "POST")
        >>> headers.has("Set-Cookies")
        True
        >>> headers -= 'Set-Cookies'
        >>> headers.has("Set-Cookies")
        False
        zCannot subtract type "r"  )r7   r   r   r8   rK   r<   r   rh   r   rv   rg   )r?   rT   other_normalizedr  r  rD   rD   rE   r     s   



zHeaders.__isub__r   c                 C  sv   t |tr
| j| S t|}|| vrtd| dt }| jD ]}|j|kr,|| q t|dks5t	r7|S |
 S )z
        Extract header using the bracket syntax, dict-like. The result is either a single Header or a list of Header.
        r/   r  r
   )r7   rf   r   r   r   r8   rK   r<   rM   r   rk   )r?   r   r{   r  rD   rD   rE   r     s   




zHeaders.__getitem__c                 C  s(   t |}|| vrtd| d| | S )a  
        Where the magic happens, every header is accessible via the property notation.
        The result is either a single Header or a list of Header.
        eg.
        >>> headers = Header("Content-Type", "text/html; charset=UTF-8") + Header("Allow", "POST") + Header("From", "john-doe@gmail.com")
        >>> headers.content_type
        Content-Type: text/html; charset=UTF-8
        >>> headers.from_
        From: john-doe@gmail.com
        r/   r  )r   r   r   rD   rD   rE   r     s   zHeaders.__getattr__c                 C  s   t |  S )z[
        Provide a JSON representation of Headers. JSON is by definition a string.
        )r   r;   rI   rD   rD   rE   to_json  s   zHeaders.to_jsonr   c                 C  r   )ak  
        Will encode your headers as bytes using utf-8 charset encoding. Any error encountered in encoder would be
        treated by the 'surrogateescape' clause.
        >>> headers = Header("Content-Type", "text/html; charset=UTF-8") + Header("Allow", "POST")
        >>> bytes(headers)
        b'Content-Type: text/html; charset=UTF-8\r\nAllow: POST'
        r   r   r   r   rI   rD   rD   rE   r     s   zHeaders.__bytes__c                 C  s   t | j}|  t|S )zFReturn a new instance of Headers containing headers in reversed order.)r   r   reverserz   )r?   list_of_headersrD   rD   rE   __reversed__  s   
zHeaders.__reversed__c                 C  rO   )z=Return True if Headers does contain at least one entry in it.)r   r   rI   rD   rD   rE   __bool__  s   
zHeaders.__bool__c                 C  sV   t |tr	t|n|}| D ]}t |tr|j|kr dS t |tr(||kr( dS qdS )z
        This method will allow you to test if a header, based on its string name, is present or not in headers.
        You could also use a Header object to verify it's presence.
        TF)r7   r   r   rK   r   )r?   r   r  rD   rD   rE   r     s   zHeaders.__contains___Headers__index_Headers__headerc                 C  s0   t |tstdt| d| j|| dS )z%Insert header before the given index.zCannot insert element of type z in Headers.N)r7   r   rv   rg   r   rp   )r?   r)  r*  rD   rD   rE   rp      s
   
zHeaders.insertr   ra   _Headers__value_Headers__start_Headers__stopc           	      C  s   t |t}|st|nd}t| }|dk r|| n|}|dk r#|| n|}t| j||d  t||d D ]\}}|rD||krD|  S ||jkrM|  S q6td| d)a  
        Search for the first appearance of an header based on its name or instance in Headers.
        Same method signature as list().index().
        Raises IndexError if not found.
        >>> headers = Header("A", "hello") + Header("B", "world") + Header("C", "funny; riddle")
        >>> headers.index("A")
        0
        >>> headers.index("A", 1)
        Traceback (most recent call last):
        ...
        IndexError: Value 'A' is not present within Headers.
        >>> headers.index("A", 0, 1)
        0
        >>> headers.index("C")
        2
        >>> headers.index(headers[0])
        0
        >>> headers.index(headers[1])
        1
        Nr   r
   zValue 'z ' is not present within Headers.)	r7   r   r   rM   zipr   r   rK   
IndexError)	r?   r+  r,  r-  value_is_headernormalized_valueheaders_lenr  indexrD   rD   rE   r3  )  s   

zHeaders.index_Headers__index_or_namec                 C  s   t |tr| j|S t |trA| |}|du rt t |tr-|D ]}| j| q#n| j| t	du r?t |t
r?|gS |S tdt| d)a  
        Pop header instance(s) from headers. By default the last one. Accept index as integer or header name.
        If you pass a header name, it will pop from Headers every entry named likewise.
        >>> headers = Header("A", "hello") + Header("B", "world") + Header("C", "funny; riddle")
        >>> header = headers.pop()
        >>> repr(header)
        'C: funny; riddle'
        >>> headers = Header("A", "hello") + Header("B", "world") + Header("C", "funny; riddle")
        >>> header = headers.pop(1)
        >>> repr(header)
        'B: world'
        >>> header = headers.pop("A")
        >>> repr(header)
        'A: hello'
        >>> headers = Header("A", "hello") + Header("B", "world") + Header("C", "funny; riddle") + Header("B", "ending")
        >>> headers = headers.pop("B")
        >>> len(headers)
        2
        >>> headers[0].name
        'B'
        >>> (str(headers[0]), str(headers[1]))
        ('world', 'ending')
        NTzType z- is not supported by pop() method on Headers.)r7   rf   r   rk   r   r   r/  r8   rh   r   r   rv   rg   )r?   r4  r{   r  rD   rD   rE   rk   S  s"   



zHeaders.poptuple[str, str]c                 C  s   |   }|j|jfS )z=Pop the last header as a tuple (header name, header content).)rk   r-   r.   r  rD   rD   rE   popitem  s   zHeaders.popitemr   c                   s$   t t  t tdd | D  S )z
        Provide a better auto-completion when using python interpreter. We are feeding __dir__ so Python can be aware
        of what properties are callable. In other word, more precise auto-completion when not using IDE.
        c                 S  r  rD   )rK   )r   r  rD   rD   rE   r     r  z#Headers.__dir__.<locals>.<listcomp>)r8   r   r   setrI   r   rD   rE   r     s   zHeaders.__dir__)r{   r   )r  r   rG   r   )r  r   rG   r  r   )rG   r  r   )rG   ro   )rG   r  )rG   r   )r^   r:   rG   rz   r   r   r   r   rG   rf   r   )rT   r   rG   rz   )rT   r!  rG   rz   )r   r   rG   r   )r   r   rG   r   r   )rG   rz   )rG   r   )r   r!  rG   r   )r)  rf   r*  r   rG   ro   )r   ra   )r+  r!  r,  rf   r-  rf   rG   rf   r   )r4  r   rG   r   )rG   r5  r   )(r   r   r   r   r   rF   r   r   r   r   r   r  r;   r  r`   r   r   r   r   r   r  r   r   r}   r   rx   r   r   r   r$  r   r'  r(  r   rp   r3  rk   r6  r   r   rD   rD   r   rE   rz   I  s   
 














	



.
	
	


!







	*
/rz   c                   @  s   e Zd ZdZd.ddZd/dd	Zd0d
dZd1ddZd2ddZ	d3d4ddZ		d3d5d!d"Z
d6d$d%Zed7d&d'Zd8d)d*Zd9d,d-ZdS ):r=   z
    Dedicated class to handle attributes within a Header. Wrap an AttributeBag and offer methods to manipulate it
    with ease.
    Store advanced info on attributes, members/adjectives, case insensitive on keys and keep attrs ordering.
    membersrR   c                 C  s   t  | _t|tdt|D ]R\}}|dkrqt|tdu r"t|}d|v rXt|jddd\}}|	dt|ksDt|dksDd|v rM| 
t|d  q| 
|tt| q| 
t|d  qd S )Nr   rt   Fr0   r
   )maxsplitr   )r   _bagr.  r   rM   r7   r   tuplesplitcountrp   r   r   )r?   r9  rq   r3  ri   rj   rD   rD   rE   rF     s   &zAttributes.__init__rG   r   c                 C  s   d}t | jdkr|S tdt | D ]+}| | \}}|dur1|dj|t||dkr+dndd7 }q||dkr:d| n|7 }q|S )z2Convert an Attributes instance to its string repr.rt   r   Nz{semi_colon_r}{key}="{value}"ru   )ri   rj   semi_colon_r)rM   r;  r   r   r   )r?   r.   r   ri   rj   rD   rD   rE   r     s   
zAttributes.__str__c                 C  s2   g }| D ]\}}}||vr|dur| | q|S )z\This method return a list of attribute name that have at least one value associated to them.N)r<   )r?   r   r3  ri   rj   rD   rD   rE   r     s   
zAttributes.keysrT   rU   r   c                 C  s   t |tstt| t|krdS t| }t|}g }|D ],\}}}t|}|D ] \}}	}
t|	}	||	krH||
krH|||	f|vrH||||	f q(qt|t|kS )zMVerify if two instance of Attributes are equal. We don't care about ordering.F)r7   r=   rW   rM   r8   r   r<   )r?   rT   list_repr_alist_repr_b
list_checkindex_akey_avalue_aindex_bkey_bvalue_brD   rD   rE   r     s$   

zAttributes.__eq__r   rc   (tuple[str, str | None] | str | list[str]c                 C  s   t |trdd | j| d D }t|dkr|S |d S | jD ]"}|| j| d v rB| j| d |}|| j| d | f  S q t| d)zz
        Extract item from an Attributes instance using its (integer) index or key string name (case insensible).
        c                 S  s   g | ]}|d ur|qS )NrD   )r   rj   rD   rD   rE   r     s    z*Attributes.__getitem__.<locals>.<listcomp>r   r
   z not in defined indexes.)r7   r   r;  rM   r3  r/  )r?   r   r  r   posrD   rD   rE   r     s   

zAttributes.__getitem__Nri   rj   rn   r3  
int | Nonero   c           
      C  s   |dur|dk r|t |  n|}|dur|nt | }|durK| jD ])}| j| \}}t|tdt |D ]\}}	||krI| j| d |	  d7  < q4q!|| jvr[|g|gf| j|< dS | j| d | | j| d | dS )a  
        Insert an attribute into the Attributes instance. If no value is provided, adding it at the end.
        If an index is specified, insert it just before specified index.
        >>> attributes = Attributes(["text/html", "charset=UTF-8"])
        >>> str(attributes)
        'text/html; charset="UTF-8"'
        >>> attributes.insert("charset")
        >>> str(attributes)
        'text/html; charset="UTF-8"; charset'
        >>> attributes = Attributes(["text/html", "charset"])
        >>> attributes.insert("charset", "UTF-8", 1)
        >>> str(attributes)
        'text/html; charset="UTF-8"; charset'
        >>> attributes.insert("hello", None, -1)
        >>> str(attributes)
        'text/html; charset="UTF-8"; hello; charset'
        Nr   r
   )rM   r;  r.  r   r<   )
r?   ri   rj   r3  to_be_insertedr   r  indexesindex_currD   rD   rE   rp     s    

zAttributes.insertr   bool | Nonec                 C  s  || j vrdS g }|durB|durtd|dkr|n|t|  }| j | d |}| j | d | || j | d | |du r|durt| j | d | j | d D ]\}}|du rk|durk|| qY|du rx|du rx|| qY|D ] }| j | d |}| j | d | | j | d | q{n|| j | d 7 }g g f| j |< t| j | d dkr| j |= | j D ]A}| j | \}	}
t|}t|
tdt|
D ])\}}|d |v r| j | d |  d8  < q||kr| j | d |  d8  < qqdS )a  
        Remove attribute from an Attributes instance. If no index is provided, will remove every entry, member/adjective
        and attribute. When index is not specified, you may want to limit its focus to attribute (aka. with value) or
        the opposite by setting **with_value** to either True or False. Be cautious that setting a specific index cannot
        be used in addition to **with_value**.
        >>> attributes = Attributes(["text/html", "charset=UTF-8", "charset"])
        >>> str(attributes)
        'text/html; charset="UTF-8"; charset'
        >>> attributes.remove("charset", 1)
        >>> str(attributes)
        'text/html; charset'
        >>> attributes = Attributes(["text/html", "charset=UTF-8", "charset"])
        >>> attributes.remove("charset", with_value=False)
        >>> str(attributes)
        'text/html; charset="UTF-8"'
        >>> attributes = Attributes(["text/html", "charset=UTF-8", "charset"])
        >>> attributes.remove("charset")
        >>> str(attributes)
        'text/html'
        Nz:Cannot set both index and with_value in the remove method.r   r
   TF)	r;  r2   rM   r3  rk   r<   r.  r#   r   )r?   ri   r3  r   freed_indexesrJ  rN  value_r   r  rM  max_freed_indexrO  rD   rD   rE   rh     sP   
&

zAttributes.remove str | dict[str, list[str] | str]c                 C  st   t | jdkr	dS t|tr|| jv S | \}}t|}tdt | D ]}| | \}}||kr7||kr7 dS q$dS )aj  Verify if a member/attribute/value is in an Attributes instance. See examples bellow :
        >>> attributes = Attributes(["application/xml", "q=0.9", "q=0.1"])
        >>> "q" in attributes
        True
        >>> {"Q": "0.9"} in attributes
        True
        >>> "z" in attributes
        False
        >>> {"Q": "0.2"} in attributes
        False
        r   FT)rM   r;  r7   r   r6  r   r   )r?   r   
target_keytarget_valuer   ri   rj   rD   rD   rE   r   i  s   

zAttributes.__contains__c                 C  sH   t | jdkr	dS d}| jD ]}| j| \}}t|}||kr!|}q|S )zISimply output the latest index used in attributes. Index start from zero.r   N)rM   r;  r#   )r?   	max_indexri   r  rM  maximum_ind_keyrD   rD   rE   
last_index  s   
zAttributes.last_indexrf   c                 C  s   | j }|dur|d S dS )z_The length of an Attributes instance is equal to the last index plus one. Not by keys() length.Nr
   r   )rY  )r?   rY  rD   rD   rE   r    s   zAttributes.__len__%Iterator[tuple[int, str, str | None]]c                 c  s2    t dt| D ]}| | \}}|||fV  qdS )zProvide an iterator over all attributes with or without associated value.
        For each entry, output a tuple of index, attribute and a optional value.r   N)r   rM   )r?   r   ri   rj   rD   rD   rE   r     s
   zAttributes.__iter__)r9  rR   r   r   r   )r   rc   rG   rI  )NN)ri   r   rj   rn   r3  rK  rG   ro   )ri   r   r3  rK  r   rP  rG   ro   )r   rT  rG   r   )rG   rK  r8  )rG   rZ  )r   r   r   r   rF   r   r   r   r   rp   rh   r   r   rY  r  r   rD   rD   rD   rE   r=     s     





&
J
r=   TlockrG   ro   c                 C  s   | a dS )z{
    This method will restrict type entropy by always returning a List[Header] instead of Union[Header, List[Header]].
    N)r   )r[  rD   rD   rE   lock_output_type  s   r\  N)T)r[  r   rG   ro   )"
__future__r   copyr   jsonr   r   r   typingr   r	   
structuresr   r   utilsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rz   r=   r\  rD   rD   rD   rE   <module>   s*    <    2    L  