U
    dG                     @   s  d Z ddlZddlZddlmZmZmZ ddlmZmZ ddl	m
Z
 ddlZddlZedddejdZedddd	ejdZed
ejZedejZedZdd Zddde_dd Zddde_dd Zddde_G dd deZeddddddZG dd deZG dd deZdd Zd d! Zd"d# Z d?d%d&Z!dd'de!_d(d) Z"dd)de"_d*d+ Z#d@d,d-Z$d.dd/e$_dAd0d1Z%d.ddd2e%_G d3d4 d4eZ&d5d6 Z'd7d6ie'_d8d9 Z(d:d; Z)dd<de)_d=d> Z*dd)de*_dS )Ba  
This module provide some helpers for advanced types parsing.

You can define you own parser using the same pattern:

.. code-block:: python

    def my_type(value):
        if not condition:
            raise ValueError('This is not my type')
        return parse(value)

    # Swagger documentation
    my_type.__schema__ = {'type': 'string', 'format': 'my-custom-format'}

The last line allows you to document properly the type in the Swagger documentation.
    N)datetimetime	timedelta)parsedate_tz	mktime_tz)urlparsetzinfo   ;   i?B a  (?:(?P<auth>[^:@]+?(?::[^:@]*?)?)@)?(?:(?P<localhost>localhost)|(?P<ipv4>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:\[?(?P<ipv6>[A-F0-9]*:[A-F0-9:]+)\]?)|(?P<domain>(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)))(?::(?P<port>\d+))?$z9^(?P<local>[^@]*[^@.])@(?P<server>[^@\.]+(?:\.[^@\.]+)*)$z\d{2}:\d{2}c                 C   sL   z"t |  | ddkr | W S W n t jk
r8   Y nX td| dS )zValidate an IPv4 address.   {0} is not a valid ipv4 addressN)socket	inet_atoncounterror
ValueErrorformatvalue r   6/tmp/pip-unpacked-wheel-dt_sn2ih/flask_restx/inputs.pyipv48   s    

r   stringtyper   c                 C   s>   zt t j|  | W S  t jk
r8   td| Y nX dS )zValidate an IPv6 addressr   N)r   	inet_ptonAF_INET6r   r   r   r   r   r   r   ipv6F   s
    r   c                 C   sR   z
t | W S  tk
r   Y nX z
t| W S  tk
rL   td| Y nX dS )z+Validate an IP address (both IPv4 and IPv6)z{0} is not a valid ipN)r   r   r   r   r   r   r   r   ipR   s    

r    c                   @   s8   e Zd ZdZdddZdddZdd	 Zed
d ZdS )URLaP  
    Validate an URL.

    Example::

        parser = reqparse.RequestParser()
        parser.add_argument('url', type=inputs.URL(schemes=['http', 'https']))

    Input to the ``URL`` argument will be rejected
    if it does not match an URL with specified constraints.
    If ``check`` is True it will also be rejected if the domain does not exists.

    :param bool check: Check the domain exists (perform a DNS resolution)
    :param bool ip: Allow IP (both ipv4/ipv6) as domain
    :param bool local: Allow localhost (both string or ip) as domain
    :param bool port: Allow a port to be present
    :param bool auth: Allow authentication to be present
    :param list|tuple schemes: Restrict valid schemes to this list
    :param list|tuple domains: Restrict valid domains to this list
    :param list|tuple exclude: Exclude some domains
    FNc	           	      C   s4   || _ || _|| _|| _|| _|| _|| _|| _d S N)checkr    localportauthschemesdomainsexclude)	selfr#   r    r$   r%   r&   r'   r(   r)   r   r   r   __init__x   s    zURL.__init__c                 C   s(   d}|rd ||f}t||d S )Nz{0} is not a valid URLz. )joinr   r   )r*   r   detailsmsgr   r   r   r      s    z	URL.errorc              
   C   s  t |}t|j}t|j|jfsdt|jpJ|jddd ddd rZ| |d | | |jr| j	r|j| j	kr| |d |s| | |
 }|d s|d rd| js| |d	 nNzt|d p|d  W n4 tk
r } z| |t| W 5 d }~X Y nX | js\|d rB|d d
rB| |d n|d dkr\| |d | jrd|d r| js| |d |d r| js| |d |d r| js| |d n2t|d }d|  k rdk sn | |d |d r| jr"|d | jkr"| |d n$| jrF|d | jkrF| |d | jrzt|d d  W n$ tjk
r   | |d Y nX |S )N/   r   ?zDid you mean: http://{0}zProtocol is not allowedr   r   zIP is not allowed127.zLocalhost is not allowed::1r&   zAuthentication is not allowed	localhostr%   zCustom port is not allowedi  zPort is out of rangedomainzDomain is not allowedzDomain does not exists)r   netloc_regexmatchnetlocallschemepathsplitr   r'   	groupdictr    r   strr$   
startswithr#   r&   r%   intr(   r)   r   getaddrinfo)r*   r   parsedZnetloc_matchdataer%   r   r   r   __call__   s^    "

"

zURL.__call__c                 C   s
   dddS )Nr   urlr   r   r*   r   r   r   
__schema__   s    zURL.__schema__)FFFFFNNN)N)	__name__
__module____qualname____doc__r+   r   rE   propertyrH   r   r   r   r   r!   a   s           

4r!   T)httphttpsftpZftps)r    r&   r%   r$   r'   c                   @   s@   e Zd ZdZdddZdddZdd	 Zd
d Zedd Z	dS )emailaN  
    Validate an email.

    Example::

        parser = reqparse.RequestParser()
        parser.add_argument('email', type=inputs.email(dns=True))

    Input to the ``email`` argument will be rejected if it does not match an email
    and if domain does not exists.

    :param bool check: Check the domain exists (perform a DNS resolution)
    :param bool ip: Allow IP (both ipv4/ipv6) as domain
    :param bool local: Allow localhost (both string or ip) as domain
    :param list|tuple domains: Restrict valid domains to this list
    :param list|tuple exclude: Exclude some domains
    FNc                 C   s"   || _ || _|| _|| _|| _d S r"   )r#   r    r$   r(   r)   )r*   r#   r    r$   r(   r)   r   r   r   r+      s
    zemail.__init__c                 C   s   |pd}t ||d S )Nz{0} is not a valid email)r   r   r*   r   r.   r   r   r   r      s    zemail.errorc                 C   s*   zt | W dS  tk
r$   Y dS X d S )NTF)r    r   )r*   r   r   r   r   is_ip   s
    zemail.is_ipc                 C   s   t |}|rd|kr | | |d}| jrbzt|d  W n  tjk
r`   | | Y nX | jr~|| jkr~| |d | jr|| jkr| |d | j	s|dks|
dr| | | |r| js| | |S )Nz..serverz-{0} does not belong to the authorized domainsz!{0} belongs to a forbidden domain)r4   r3   r2   )email_regexr7   r   groupr#   r   rA   r(   r)   r$   r?   rS   r    )r*   r   r7   rT   r   r   r   rE      s,    




zemail.__call__c                 C   s
   dddS )Nr   rQ   r   r   rG   r   r   r   rH     s    zemail.__schema__)FFFNN)N)
rI   rJ   rK   rL   r+   r   rS   rE   rM   rH   r   r   r   r   rQ      s   

rQ   c                   @   s4   e Zd ZdZdd Zdd Zdd Zedd	 Zd
S )regexa`  
    Validate a string based on a regular expression.

    Example::

        parser = reqparse.RequestParser()
        parser.add_argument('example', type=inputs.regex('^[0-9]+$'))

    Input to the ``example`` argument will be rejected if it contains anything
    but numbers.

    :param str pattern: The regular expression the input must match
    c                 C   s   || _ t|| _d S r"   )patternrecompile)r*   rX   r   r   r   r+   )  s    zregex.__init__c                 C   s$   | j |s d| j}t||S )Nz#Value does not match pattern: "{0}")rY   searchr   rX   r   )r*   r   messager   r   r   rE   -  s    zregex.__call__c                 C   s
   t | jS r"   )rW   rX   )r*   memor   r   r   __deepcopy__3  s    zregex.__deepcopy__c                 C   s   d| j dS )Nr   )r   rX   )rX   rG   r   r   r   rH   6  s    zregex.__schema__N)	rI   rJ   rK   rL   r+   rE   r^   rM   rH   r   r   r   r   rW     s   rW   c                 C   sf   t | ts"t| t} t|t}| jdkrFtj| } tj|}n| tj} |tj}| |fS )a  
    Normalize datetime intervals.

    Given a pair of datetime.date or datetime.datetime objects,
    returns a 2-tuple of tz-aware UTC datetimes spanning the same interval.

    For datetime.date objects, the returned interval starts at 00:00:00.0
    on the first date and ends at 00:00:00.0 on the second.

    Naive datetimes are upgraded to UTC.

    Timezone-aware datetimes are normalized to the UTC tzdata.

    Params:
        - start: A date or datetime
        - end: A date or datetime
    N)	
isinstancer   combineSTART_OF_DAYr	   pytzUTCZlocalize
astimezone)startendr   r   r   r   _normalize_interval>  s    

rg   c                 C   s   t | ts| tdd }nd|dd }tdd|}|d}|dkrX| tdd }n&|dkrp| tdd	 }n| tdd
 }|S )Nr0   )daysTz[+-].+ :r   )hours)minutes)seconds)r_   r   r   r<   rY   subr   )re   r   rf   r   Ztime_without_offsetZnum_separatorsr   r   r   _expand_datetime^  s    

rp   c                 C   sf   zt t| W S  tk
r`   zt| dfW  Y S  tk
rZ   t| df Y  Y S X Y nX dS )zg
    Do some nasty try/except voodoo to get some sort of datetime
    object(s) out of the string.
    N)sorted	aniso8601Zparse_intervalr   parse_datetime
parse_dater   r   r   r   _parse_intervalv  s    ru   argumentc                 C   sr   | st dz2t| \}}|dkr,t|| }t||| \}}W n* t k
rh   d}t |j|| dY nX ||fS )aG  
    Parses ISO 8601-formatted datetime intervals into tuples of datetimes.

    Accepts both a single date(time) or a full interval using either start/end
    or start/duration notation, with the following behavior:

    - Intervals are defined as inclusive start, exclusive end
    - Single datetimes are translated into the interval spanning the
      largest resolution not specified in the input value, up to the day.
    - The smallest accepted resolution is 1 second.
    - All timezones are accepted as values; returned datetimes are
      localized to UTC. Naive inputs and date inputs will are assumed UTC.

    Examples::

        "2013-01-01" -> datetime(2013, 1, 1), datetime(2013, 1, 2)
        "2013-01-01T12" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 13)
        "2013-01-01/2013-02-28" -> datetime(2013, 1, 1), datetime(2013, 2, 28)
        "2013-01-01/P3D" -> datetime(2013, 1, 1), datetime(2013, 1, 4)
        "2013-01-01T12:00/PT30M" -> datetime(2013, 1, 1, 12), datetime(2013, 1, 1, 12, 30)
        "2013-01-01T06:00/2013-01-01T12:00" -> datetime(2013, 1, 1, 6), datetime(2013, 1, 1, 12)

    :param str value: The ISO8601 date time as a string
    :return: Two UTC datetimes, the start and the end of the specified interval
    :rtype: A tuple (datetime, datetime)
    :raises ValueError: if the interval is invalid.
    z,Expected a valid ISO8601 date/time interval.NzIInvalid {arg}: {value}. {arg} must be a valid ISO8601 date/time interval.argr   )r   ru   rp   rg   r   )r   rv   re   rf   r.   r   r   r   iso8601interval  s    
ry   ziso8601-intervalc                 C   s   t | d}|S )z3Parse a valid looking date in the format YYYY-mm-ddz%Y-%m-%d)r   strptimer   dater   r   r   r|     s    r|   c              	   C   s6   z
t | W S  ttfk
r0   td| Y nX d S )Nz{0} is not a valid integer)r@   	TypeErrorr   r   r   r   r   r   _get_integer  s    
r~   c                 C   s*   t | } | dk r&d}t|j|| d| S )z:Restrict input type to the natural numbers (0, 1, 2, 3...)r   z<Invalid {arg}: {value}. {arg} must be a non-negative integerrw   r~   r   r   r   rv   r.   r   r   r   natural  s
    r   integer)r   minimumc                 C   s*   t | } | dk r&d}t|j|| d| S )z9Restrict input type to the positive integers (1, 2, 3...)r0   z8Invalid {arg}: {value}. {arg} must be a positive integerrw   r   r   r   r   r   positive  s
    r   )r   r   ZexclusiveMinimumc                   @   s.   e Zd ZdZd
ddZdd Zedd Zd	S )	int_rangez3Restrict input to an integer in a range (inclusive)rv   c                 C   s   || _ || _|| _d S r"   )lowhighrv   )r*   r   r   rv   r   r   r   r+     s    zint_range.__init__c                 C   s@   t |}|| jk s|| jkr<d}t|j| j|| j| jd|S )Nz@Invalid {arg}: {val}. {arg} must be within the range {lo} - {hi})rx   vallohi)r~   r   r   r   r   rv   rR   r   r   r   rE     s    zint_range.__call__c                 C   s   d| j | jdS )Nr   )r   r   maximum)r   r   rG   r   r   r   rH     s    zint_range.__schema__N)rv   )rI   rJ   rK   rL   r+   rE   rM   rH   r   r   r   r   r     s
   
	r   c                 C   s^   t | tr| S | dkr tdn| s(dS t|  } | dkr@dS | dkrLdS td| dS )ap  
    Parse the string ``"true"`` or ``"false"`` as a boolean (case insensitive).

    Also accepts ``"1"`` and ``"0"`` as ``True``/``False`` (respectively).

    If the input is from the request JSON body, the type is already a native python boolean,
    and will be passed through without further parsing.

    :raises ValueError: if the boolean value is invalid
    Nzboolean type must be non-nullF)true1onT)false0z"Invalid literal for boolean(): {0})r_   boolr   r>   lowerr   r   r   r   r   boolean  s    

r   r   c                 C   s   | }t | sd| df} zFt| }t|}|d dkrPt|jtj	dW S t|tj	W S W n" t
k
r   td|Y nX dS )aH  
    Turns an RFC822 formatted date into a datetime object.

    Example::

        inputs.datetime_from_rfc822('Wed, 02 Oct 2002 08:00:00 EST')

    :param str value: The RFC822-complying string to transform
    :return: The parsed datetime
    :rtype: datetime
    :raises ValueError: if value is an invalid date literal

     z00:00:00Nr   Invalid date literal "{0}")
time_regexr[   r,   r   r   r   fromtimestampreplacerb   utc	Exceptionr   r   )r   raw	timetuple	timestampr   r   r   datetime_from_rfc822  s    
r   c                 C   sn   zFzt | W W S  tk
rB   t | }t|j|j|j Y W S X W n" tk
rh   td	| Y nX dS )a>  
    Turns an ISO8601 formatted date into a datetime object.

    Example::

        inputs.datetime_from_iso8601("2012-01-01T23:30:00+02:00")

    :param str value: The ISO8601-complying string to transform
    :return: A datetime
    :rtype: datetime
    :raises ValueError: if value is an invalid date literal

    r   N)
rr   rs   r   rt   r   yearmonthdayr   r   r{   r   r   r   datetime_from_iso86019  s    
r   z	date-timec                 C   s   t |  S )a!  
    Turns an ISO8601 formatted date into a date object.

    Example::

        inputs.date_from_iso8601("2012-01-01")



    :param str value: The ISO8601-complying string to transform
    :return: A date
    :rtype: date
    :raises ValueError: if value is an invalid date literal

    )r   r|   r   r   r   r   date_from_iso8601T  s    r   )rv   )rv   )rv   )+rL   rY   r   r   r   r   email.utilsr   r   urllib.parser   rr   rb   rc   ra   Z
END_OF_DAYrZ   
IGNORECASEr6   rU   r   r   rH   r   r    objectr!   rF   rQ   rW   rg   rp   ru   ry   r|   r~   r   r   r   r   r   r   r   r   r   r   r   <module>   sl   	
	q    C$ 
0
	
	!
