a
    mzf                     @   s  d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddlm
Z ddlmZmZ zddlmZ W n ey   ddlmZ Y n0 g dZG dd	 d	eZeZd
d ZG dd deZeZeeeegZG dd deZeeZde_ dd Zdd Z dd Z!dd Z"deddZ#e#Z$dd Z%G dd dZ&e&Z'e'ej(Z)d e)_ G d!d" d"Z*d#d$ Z+d%d& Z,dfd'd(Z-d)d* Z.G d+d, d,Z/e/Z0dgd.d/Z1d0d1 Z2d2d3 Z3d4d5 Z4d6d7 Z5d8d9 Z6d:d; Z7dhd=d>Z8did?d@Z9djdAdBZ:dkdCdDZ;dldEdFZ<dGdH Z=dIdJ Z>dKdL Z?dMdN Z@dOdP ZAdmdQdRZBG dSdT dTZCeCZDG dUdV dVZEeEZFdndWdXZGG dYdZ dZeZHeHZId[d\ ZJd]d^ ZKe)d_ZLdod`daZMG dbdc dcZNeOddkrddlPZPePQ  dS )pz$
General Utilities
(part of web.py)
    N)local   )	iteritems
itervalues)StringIO)1StoragestoragestorifyCountercounteritersrstripslstripsstripssafeunicodesafestr	timelimitMemoizememoize
re_compilere_submgroupuniqiterview
IterBetter
iterbettersafeiter	safewritedictreversedictfinddictfindalldictincrdictaddrequeuerestacklistgetintgetdatestrnumifydenumifycommifydateifynthstrcondCaptureStdoutcapturestdoutProfileprofiletryallThreadedDictthreadeddict
autoassignto36sendmailc                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )r   ao  
    A Storage object is like a dictionary except `obj.foo` can be used
    in addition to `obj['foo']`.

        >>> o = storage(a=1)
        >>> o.a
        1
        >>> o['a']
        1
        >>> o.a = 2
        >>> o['a']
        2
        >>> del o.a
        >>> o.a
        Traceback (most recent call last):
            ...
        AttributeError: 'a'

    c              
   C   s<   z
| | W S  t y6 } zt|W Y d }~n
d }~0 0 d S NKeyErrorAttributeErrorselfkeyk r@   7/var/www/media/lib/python3.9/site-packages/web/utils.py__getattr__f   s    
zStorage.__getattr__c                 C   s   || |< d S r8   r@   r=   r>   valuer@   r@   rA   __setattr__l   s    zStorage.__setattr__c              
   C   s<   z
| |= W n, t y6 } zt|W Y d }~n
d }~0 0 d S r8   r9   r<   r@   r@   rA   __delattr__o   s    
zStorage.__delattr__c                 C   s   dt |  d S )Nz	<Storage >dict__repr__r=   r@   r@   rA   rJ   u   s    zStorage.__repr__N)__name__
__module____qualname____doc__rB   rE   rF   rJ   r@   r@   r@   rA   r   Q   s
   r   c                    s4  | dd t dur&t dr&  fddfddt }|t|   D ]}| | }t|trt||trfdd	|D }n|d
 }t||t	s|}t||trt|ts|g}t
||| qVt|D ]H\}}|}t||r|| }|dkr"t|ts"|f}t
||| q|S )a  
    Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
    d doesn't have all of the keys in `requireds` and using the default
    values for keys found in `defaults`.

    For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
    `storage({'a':1, 'b':2, 'c':3})`.

    If a `storify` value is a list (e.g. multiple values in a form submission),
    `storify` returns the last element of the list, unless the key appears in
    `defaults` as a list. Thus:

        >>> storify({'a':[1, 2]}).a
        2
        >>> storify({'a':[1, 2]}, a=[]).a
        [1, 2]
        >>> storify({'a':1}, a=[]).a
        [1]
        >>> storify({}, a=[]).a
        []

    Similarly, if the value has a `value` attribute, `storify will return _its_
    value, unless the key appears in `defaults` as a dictionary.

        >>> storify({'a':storage(value=1)}).a
        1
        >>> storify({'a':storage(value=1)}, a={}).a
        <Storage {'value': 1}>
        >>> storify({}, a={}).a
        {}

    _unicodeF__call__c                    s    rt | tr| S | S d S r8   )
isinstancestr)s)rP   
to_unicoder@   rA   	unicodify   s    zstorify.<locals>.unicodifyc                    s:   t | drt | dr| jS t | dr. | jS  | S d S )NfilerD   )hasattrrD   x)rV   r@   rA   getvalue   s
    

zstorify.<locals>.getvaluec                    s   g | ]} |qS r@   r@   ).0rZ   )r[   r@   rA   
<listcomp>       zstorify.<locals>.<listcomp>r@   )popr   rX   r   tuplekeysrR   listgetrI   setattrr   )mappingZ	requiredsdefaultsZstorr>   rD   resultr@   )rP   r[   rU   rV   rA   r	   |   s4    !
r	   c                   @   sP   e Zd 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d ZdS )r
   a  Keeps count of how many times something is added.

    >>> c = counter()
    >>> c.add('x')
    >>> c.add('x')
    >>> c.add('x')
    >>> c.add('x')
    >>> c.add('x')
    >>> c.add('y')
    >>> c['y']
    1
    >>> c['x']
    5
    >>> c.most()
    ['x']
    c                 C   s    |  |d | |  d7  < d S )Nr   r   
setdefault)r=   nr@   r@   rA   add   s    zCounter.addc                    s"   t t|   fddt| D S )z$Returns the keys with maximum count.c                    s   g | ]\}}| kr|qS r@   r@   r\   r?   vmr@   rA   r]      r^   z Counter.most.<locals>.<listcomp>)maxr   r   rK   r@   ro   rA   most   s    zCounter.mostc                    s"   t |    fddt| D S )z$Returns the keys with minimum count.c                    s   g | ]\}}| kr|qS r@   r@   rm   ro   r@   rA   r]      r^   z!Counter.least.<locals>.<listcomp>)minr   r   rK   r@   ro   rA   least   s    zCounter.leastc                 C   s   t | | t|   S )a  Returns what percentage a certain key is of all entries.

        >>> c = counter()
        >>> c.add('x')
        >>> c.add('x')
        >>> c.add('x')
        >>> c.add('y')
        >>> c.percent('x')
        0.75
        >>> c.percent('y')
        0.25
        )floatsumvaluesr=   r>   r@   r@   rA   percent   s    zCounter.percentc                    s   t    fddddS )zReturns keys sorted by value.

        >>> c = counter()
        >>> c.add('x')
        >>> c.add('x')
        >>> c.add('y')
        >>> c.sorted_keys()
        ['x', 'y']
        c                    s    |  S r8   r@   )r?   rK   r@   rA   <lambda>  r^   z%Counter.sorted_keys.<locals>.<lambda>T)r>   reverse)sortedrb   rK   r@   rK   rA   sorted_keys   s    
zCounter.sorted_keysc                    s    fdd   D S )zReturns values sorted by value.

        >>> c = counter()
        >>> c.add('x')
        >>> c.add('x')
        >>> c.add('y')
        >>> c.sorted_values()
        [2, 1]
        c                    s   g | ]} | qS r@   r@   r\   r?   rK   r@   rA   r]     r^   z)Counter.sorted_values.<locals>.<listcomp>r}   rK   r@   rK   rA   sorted_values  s    
zCounter.sorted_valuesc                    s    fdd   D S )zReturns items sorted by value.

        >>> c = counter()
        >>> c.add('x')
        >>> c.add('x')
        >>> c.add('y')
        >>> c.sorted_items()
        [('x', 2), ('y', 1)]
        c                    s   g | ]}| | fqS r@   r@   r~   rK   r@   rA   r]     r^   z(Counter.sorted_items.<locals>.<listcomp>r   rK   r@   rK   rA   sorted_items  s    
zCounter.sorted_itemsc                 C   s   dt |  d S )Nz	<Counter rG   rH   rK   r@   r@   rA   rJ     s    zCounter.__repr__N)rL   rM   rN   rO   rl   rr   rt   ry   r}   r   r   rJ   r@   r@   r@   rA   r
      s   r
   c                   @   s   e Zd ZdS )_hackN)rL   rM   rN   r@   r@   r@   rA   r   (  s   r   z
A list of iterable items (like lists, but not strings). Includes whichever
of lists, tuples, sets, and Sets are available in this version of Python.
c                 C   sz   t |tr$|D ]}t| ||}q|S | dkrH||rv|t|d  S n.| dkrn||rv|d t|  S ntd|S )NlrzDirection needs to be r or l.)rR   r   _strips
startswithlenendswith
ValueError)	directiontextremoveZsubrr@   r@   rA   r   3  s    


r   c                 C   s   t d| |S )zs
    removes the string `remove` from the right of `text`

        >>> rstrips("foobar", "bar")
        'foo'

    r   r   r   r   r@   r@   rA   r   D  s    r   c                 C   s   t d| |S )aF  
    removes the string `remove` from the left of `text`

        >>> lstrips("foobar", "foo")
        'bar'
        >>> lstrips('http://foo.org/', ['http://', 'https://'])
        'foo.org/'
        >>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])
        'BAZ'
        >>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])
        'BARBAZ'

    r   r   r   r@   r@   rA   r   O  s    r   c                 C   s   t t| ||S )zz
    removes the string `remove` from the both sides of `text`

        >>> strips("foobarfoo", "foo")
        'bar'

    )r   r   r   r@   r@   rA   r   `  s    r   utf-8c                 C   s(   | rt | drdd | D S t| S dS )z
    Converts any given object to utf-8 encoded string.

        >>> safestr('hello')
        'hello'
        >>> safestr(2)
        '2'
    __next__c                 S   s   g | ]}t |qS r@   r   r\   ir@   r@   rA   r]   v  r^   zsafestr.<locals>.<listcomp>N)rX   rS   )objencodingr@   r@   rA   r   k  s    
r   c                    s    fdd}|S )a  
    A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
    if it takes longer.

        >>> import time
        >>> def meaningoflife():
        ...     time.sleep(.2)
        ...     return 42
        >>>
        >>> timelimit(.1)(meaningoflife)()
        Traceback (most recent call last):
            ...
        RuntimeError: took too long
        >>> timelimit(1)(meaningoflife)()
        42

    _Caveat:_ The function isn't stopped after `timeout` seconds but continues
    executing in a separate thread. (There seems to be no way to kill a thread.)

    inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
    c                    s    fdd}|S )Nc                     sP   G  fdddt j}| }| | r:td|jrJ|jd |jS )Nc                       s$   e Zd Zdd Z fddZdS )z3timelimit.<locals>._1.<locals>._2.<locals>.Dispatchc                 S   s.   t j|  d | _d | _| d |   d S )NT)	threadingThread__init__rh   error	setDaemonstartrK   r@   r@   rA   r     s
    
z<timelimit.<locals>._1.<locals>._2.<locals>.Dispatch.__init__c                    s0   z i | _ W n   t | _Y n0 d S r8   )rh   sysexc_infor   rK   argsfunctionkwr@   rA   run  s    z7timelimit.<locals>._1.<locals>._2.<locals>.Dispatch.runN)rL   rM   rN   r   r   r@   r   r@   rA   Dispatch  s   r   ztook too longr   )r   r   joinis_aliveRuntimeErrorr   rh   )r   r   r   c)r   timeout)r   r   rA   _2  s    

z!timelimit.<locals>._1.<locals>._2r@   )r   r   r   )r   rA   _1  s    ztimelimit.<locals>._1r@   )r   r   r@   r   rA   r     s    r   c                   @   s"   e Zd ZdZdddZdd ZdS )	r   a  
    'Memoizes' a function, caching its return values for each input.
    If `expires` is specified, values are recalculated after `expires` seconds.
    If `background` is specified, values are recalculated in a separate thread.

        >>> calls = 0
        >>> def howmanytimeshaveibeencalled():
        ...     global calls
        ...     calls += 1
        ...     return calls
        >>> fastcalls = memoize(howmanytimeshaveibeencalled)
        >>> howmanytimeshaveibeencalled()
        1
        >>> howmanytimeshaveibeencalled()
        2
        >>> fastcalls()
        3
        >>> fastcalls()
        3
        >>> import time
        >>> fastcalls = memoize(howmanytimeshaveibeencalled, .1, background=False)
        >>> fastcalls()
        4
        >>> fastcalls()
        4
        >>> time.sleep(.2)
        >>> fastcalls()
        5
        >>> def slowfunc():
        ...     time.sleep(.1)
        ...     return howmanytimeshaveibeencalled()
        >>> fastcalls = memoize(slowfunc, .2, background=True)
        >>> fastcalls()
        6
        >>> timelimit(.05)(fastcalls)()
        6
        >>> time.sleep(.2)
        >>> timelimit(.05)(fastcalls)()
        6
        >>> timelimit(.05)(fastcalls)()
        6
        >>> time.sleep(.2)
        >>> timelimit(.05)(fastcalls)()
        7
        >>> fastcalls = memoize(slowfunc, None, background=True)
        >>> threading.Thread(target=fastcalls).start()
        >>> time.sleep(.01)
        >>> fastcalls()
        9
    NTc                 C   s,   || _ i | _|| _|| _i | _t | _d S r8   )funccacheexpires
backgroundrunningr   Lockrunning_lock)r=   r   r   r   r@   r@   rA   r     s    zMemoize.__init__c                    s    t  fj* js2t j< W d    n1 sF0    Y  d	 fdd	}jvrz|dd n@jrt		 j d  jkrj
rtj|d  n|  j d S )
NFc                    sV   j  | rRz0j i t fj< W j    nj    0 d S r8   )r   acquirer   timer   releaseblockr   r>   keywordsr=   r@   rA   update  s     z Memoize.__call__.<locals>.updateTr   r   )targetr   )F)ra   itemsr   r   rd   r   r   r   r   r   r   r   r   )r=   r   r   r   r@   r   rA   rQ     s    ,
"zMemoize.__call__)NTrL   rM   rN   rO   r   rQ   r@   r@   r@   rA   r     s   3
r   z#
A memoized version of re.compile.
c                   @   s   e Zd Zdd Zdd ZdS )_re_subm_proxyc                 C   s
   d | _ d S r8   matchrK   r@   r@   rA   r     s    z_re_subm_proxy.__init__c                 C   s
   || _ dS )N r   )r=   r   r@   r@   rA   rQ     s    z_re_subm_proxy.__call__N)rL   rM   rN   r   rQ   r@   r@   r@   rA   r     s   r   c                 C   s.   t | }t }||j| ||||jfS )z
    Like re.sub, but returns the replacement _and_ the match object.

        >>> t, m = re_subm('g(oo+)fball', r'f\1lish', 'goooooofball')
        >>> t
        'foooooolish'
        >>> m.groups()
        ('oooooo',)
    )r   r   subrQ   r   )patreplstringZcompiled_patproxyr@   r@   rA   r     s    
r   c                    s     fddt dt D S )z
    Returns an iterator over a series of lists of length size from iterable.

        >>> list(group([1,2,3,4], 2))
        [[1, 2], [3, 4]]
        >>> list(group([1,2,3,4,5], 2))
        [[1, 2], [3, 4], [5]]
    c                 3   s   | ]} ||  V  qd S r8   r@   r   seqsizer@   rA   	<genexpr>1  r^   zgroup.<locals>.<genexpr>r   )ranger   r   r@   r   rA   r   (  s    	r   c                 C   sJ   |p
dd }t  }g }| D ]*}||}||v r0q|| || q|S )at  
    Removes duplicate elements from a list while preserving the order of the rest.

        >>> uniq([9,0,2,1,0])
        [9, 0, 2, 1]

    The value of the optional `key` parameter should be a function that
    takes a single argument and returns a key to test the uniqueness.

        >>> uniq(["Foo", "foo", "bar"], key=lambda s: s.lower())
        ['Foo', 'bar']
    c                 S   s   | S r8   r@   rY   r@   r@   rA   rz   A  r^   zuniq.<locals>.<lambda>)setrl   append)r   r>   seenrh   rn   r?   r@   r@   rA   r   4  s    
r   c                 #   s   d dd dd dd  fdd	}t   }t| }t| D ]&\}}tjd
||||  |V  qFtjd
|||d | d  dS )z
    Takes an iterable `x` and returns an iterator over it
    which prints its progress to stderr as it iterates through.
    F   c                 S   s$   dt | | d tt|| |f S )Nz%5.1f%% (%*d/%d)d   )ru   r   rS   )rk   lenxr@   r@   rA   plainformatT  s    ziterview.<locals>.plainformatc                 S   sN   t t||  | d }| | r:dd| |  dd   }nd}dd| |f S )Ng      ?rG    r   r   z[%s%s]=)intru   )r   rk   r   valspacingr@   r@   rA   barsW  s
    ziterview.<locals>.barsc                 S   s\   |dkrdS ||krt | }nt | | ||  }t|d\}}t|d\}}d|||f S )Nr   z--:--:--<   z%02d:%02d:%02d)r   divmod)elapsedrk   r   ZsecsZminsZhrsr@   r@   rA   eta_  s    
ziterview.<locals>.etac                    sd   ||d }||krd}nd}|t   |  ||7 }| t| t| ||7 }||7 }|S )Nr   z     z ETA )r   r   )	starttimerk   r   outendZWIDTHr   r   r   r@   rA   formatk  s     ziterview.<locals>.formatr   
N)r   r   	enumerater   stderrwrite)rZ   r   r   r   rk   yr@   r   rA   r   M  s    r   c                   @   s>   e Zd ZdZdd ZdddZdd Zd	d
 Zdd ZeZ	dS )r   a  
    Returns an object that can be used as an iterator
    but can also be used via __getitem__ (although it
    cannot go backwards -- that is, you cannot request
    `iterbetter[0]` after requesting `iterbetter[1]`).

        >>> import itertools
        >>> c = iterbetter(itertools.count())
        >>> c[1]
        1
        >>> c[5]
        5
        >>> c[3]
        Traceback (most recent call last):
            ...
        IndexError: already passed 3

    It is also possible to get the first value of the iterator or None.

        >>> c = iterbetter(iter([3, 4, 5]))
        >>> print(c.first())
        3
        >>> c = iterbetter(iter([]))
        >>> print(c.first())
        None

    For boolean test, IterBetter peeps at first value in the itertor without effecting the iteration.

        >>> c = iterbetter(iter(range(5)))
        >>> bool(c)
        True
        >>> list(c)
        [0, 1, 2, 3, 4]
        >>> c = iterbetter(iter([]))
        >>> bool(c)
        False
        >>> list(c)
        []
    c                 C   s   |d | _ | _d S )Nr   )r   r   )r=   iteratorr@   r@   rA   r     s    zIterBetter.__init__Nc                 C   s*   zt t| W S  ty$   | Y S 0 dS )zReturns the first element of the iterator or None when there are no
        elements.

        If the optional argument default is specified, that is returned instead
        of None when there are no elements.
        N)nextiterStopIteration)r=   defaultr@   r@   rA   first  s    zIterBetter.firstc                 c   sL   t | dr| jV  zt| jV  W n ty6   Y d S 0 |  jd7  _qd S )N_headr   )rX   r   r   r   r   r   rK   r@   r@   rA   __iter__  s    
zIterBetter.__iter__c                 C   s|   || j k rtdt| z>|| j kr@t| j |  j d7  _ q|  j d7  _ t| jW S  tyv   tt|Y n0 d S )Nzalready passed r   )r   
IndexErrorrS   r   r   r   )r=   r   r@   r@   rA   __getitem__  s    


zIterBetter.__getitem__c                 C   sR   t | dr|  dkS t | dr$dS zt| j| _W n tyH   Y dS 0 dS d S )N__len__r   r   TF)rX   r   r   r   r   r   rK   r@   r@   rA   __nonzero__  s    

zIterBetter.__nonzero__)N)
rL   rM   rN   rO   r   r   r   r   r   __bool__r@   r@   r@   rA   r   ~  s   (
r   Tc                 #   s$    fddt    V  qdS )zPMakes an iterator safe by ignoring the exceptions occurred during the iteration.c                      s8   z
 W S  t y    Y q    t  Y q 0 q d S r8   )r   	traceback	print_excr@   itr   r@   rA   r     s    
zsafeiter.<locals>.nextN)r   )r   cleanupignore_errorsr@   r   rA   r     s    	r   c                 C   sJ   t | d d}|| W d   n1 s.0    Y  t|j|  dS )zWrites the content to a temp file and then moves the temp file to
    given filename to avoid overwriting the existing file in case of errors.
    z.tmpwN)openr   shutilmovename)filenamecontentfr@   r@   rA   r     s    (r   c                 C   s   t dd t| D S )z|
    Returns a new dictionary with keys and values swapped.

        >>> dictreverse({1: 2, 3: 4})
        {2: 1, 4: 3}
    c                 S   s   g | ]\}}||fqS r@   r@   )r\   r>   rD   r@   r@   rA   r]     r^   zdictreverse.<locals>.<listcomp>)rI   r   )rf   r@   r@   rA   r     s    r   c                 C   s&   t | D ]\}}||u r|  S qdS )z
    Returns a key whose value in `dictionary` is `element`
    or, if none exists, None.

        >>> d = {1:2, 3:4}
        >>> dictfind(d, 4)
        3
        >>> dictfind(d, 5)
    N)r   )
dictionaryelementr>   rD   r@   r@   rA   r     s    
r   c                 C   s,   g }t | D ]\}}||u r|| q|S )z
    Returns the keys whose values in `dictionary` are `element`
    or, if none exists, [].

        >>> d = {1:4, 3:4}
        >>> dictfindall(d, 4)
        [1, 3]
        >>> dictfindall(d, 5)
        []
    )r   r   )r  r  resr>   rD   r@   r@   rA   r      s
    r    c                 C   s$   |  |d | |  d7  < | | S )z
    Increments `element` in `dictionary`,
    setting it to one if it doesn't exist.

        >>> d = {1:2, 3:4}
        >>> dictincr(d, 1)
        3
        >>> d[1]
        3
        >>> dictincr(d, 5)
        1
        >>> d[5]
        1
    r   r   ri   )r  r  r@   r@   rA   r!   '  s    r!   c                  G   s   i }| D ]}| | q|S )z
    Returns a dictionary consisting of the keys in the argument dictionaries.
    If they share a key, the value from the last argument is used.

        >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
        {1: 0, 2: 1, 3: 1}
    )r   )Zdictsrh   dctr@   r@   rA   r"   ;  s    r"   r_   c                 C   s   |  |}| d| |S )zReturns the element at index after moving it to the beginning of the queue.

    >>> x = [1, 2, 3, 4]
    >>> requeue(x)
    4
    >>> x
    [4, 1, 2, 3]
    r   )r`   insert)queueindexrZ   r@   r@   rA   r#   I  s    	
r#   c                 C   s   |  |}| | |S )zReturns the element at index after moving it to the top of stack.

    >>> x = [1, 2, 3, 4]
    >>> restack(x)
    1
    >>> x
    [2, 3, 4, 1]
    )r`   r   )stackr	  rZ   r@   r@   rA   r$   W  s    	

r$   c                 C   s   t | d |k r|S | | S )z
    Returns `lst[ind]` if it exists, `default` otherwise.

        >>> listget(['a'], 0)
        'a'
        >>> listget(['a'], 1)
        >>> listget(['a'], 1, 'b')
        'b'
    r   )r   )lstindr   r@   r@   rA   r%   e  s    
r%   c              	   C   s*   z
t | W S  ttfy$   | Y S 0 dS )z
    Returns `integer` as an int or `default` if it can't.

        >>> intget('3')
        3
        >>> intget('3a')
        >>> intget('3a', 0)
        0
    N)r   	TypeErrorr   )integerr   r@   r@   rA   r&   t  s    

r&   c           	      C   s  ddd}d}| sdS |s$t j  }t|jdkr>t j |}t| jdkrZt j | } n"t| jdkr|t  | j| j| j} ||  }t|j	| |j
 |jd  }t|| }|d	k r|d
9 }|rt|dk r||dS | ddd}| j|jks|d	k r|d| j 7 }|S t|r^t|dkr:||ddS t|dkrT||ddS ||dS |j}|j	rzt|jd }t|dkr||ddS ||dS )ab  
    Converts a (UTC) datetime object to a nice string representation.

        >>> from datetime import datetime, timedelta
        >>> d = datetime(1970, 5, 1)
        >>> datestr(d, now=d)
        '0 microseconds ago'
        >>> for t, v in iteritems({
        ...   timedelta(microseconds=1): '1 microsecond ago',
        ...   timedelta(microseconds=2): '2 microseconds ago',
        ...   -timedelta(microseconds=1): '1 microsecond from now',
        ...   -timedelta(microseconds=2): '2 microseconds from now',
        ...   timedelta(microseconds=2000): '2 milliseconds ago',
        ...   timedelta(seconds=2): '2 seconds ago',
        ...   timedelta(seconds=2*60): '2 minutes ago',
        ...   timedelta(seconds=2*60*60): '2 hours ago',
        ...   timedelta(days=2): '2 days ago',
        ... }):
        ...     assert datestr(d, now=d+t) == v
        >>> datestr(datetime(1970, 1, 1), now=d)
        'January  1'
        >>> datestr(datetime(1969, 1, 1), now=d)
        'January  1, 1969'
        >>> datestr(datetime(1970, 6, 1), now=d)
        'June  1, 1970'
        >>> datestr(None)
        ''
    Nc                 S   sZ   |r| | } t t| d | }t| dkr4|d7 }|d7 }| dk rN|d7 }n|d7 }|S )Nr   r   rT   r   zfrom nowZago)rS   abs)rk   whatZdivisorr   r@   r@   rA   agohence  s    
zdatestr.<locals>.agohenceiQ r   DateTimedategư>r   r_      dayz%B %dz 0  z, %si  hourr   minutesecondg    .Ai  Zmillisecondmicrosecond)N)datetimeutcnowtyperL   fromtimestampyearmonthr  r   dayssecondsmicrosecondsr  strftimereplace)	Zthennowr  ZonedaydeltaZdeltasecondsZ	deltadaysr   Zdeltamicrosecondsr@   r@   rA   r'     sH    




r'   c                 C   s   d dd t| D S )z
    Removes all non-digit characters from `string`.

        >>> numify('800-555-1212')
        '8005551212'
        >>> numify('800.555.1212')
        '8005551212'

    r   c                 S   s   g | ]}|  r|qS r@   )isdigit)r\   r   r@   r@   rA   r]     r^   znumify.<locals>.<listcomp>)r   rS   )r   r@   r@   rA   r(     s    
r(   c                 C   sF   g }|D ]2}|dkr0| | d  | dd } q| | qd|S )z
    Formats `string` according to `pattern`, where the letter X gets replaced
    by characters from `string`.

        >>> denumify("8005551212", "(XXX) XXX-XXXX")
        '(800) 555-1212'

    Xr   r   Nr   )r   r   )r   patternr   r   r@   r@   rA   r)     s    	r)   c                 C   s   | du rdS t |  } | dr8d}| dd  } nd}d| v rT| d\}}n
| d }}g }tt |ddd D ],\}}|r|d s|dd	 |d| qxd|}|r|d| 7 }|| S )
a  
    Add commas to an integer `n`.

        >>> commify(1)
        '1'
        >>> commify(123)
        '123'
        >>> commify(-123)
        '-123'
        >>> commify(1234)
        '1,234'
        >>> commify(1234567890)
        '1,234,567,890'
        >>> commify(123.0)
        '123.0'
        >>> commify(1234.5)
        '1,234.5'
        >>> commify(1234.56789)
        '1,234.56789'
        >>> commify(' %.2f ' % -1234.5)
        '-1,234.50'
        >>> commify(None)
        >>>

    N-r   r   .r_      r   ,)rS   stripr   splitr   r  r   )rk   prefixZdollarsZcentsr   r   r   r   r@   r@   rA   r*     s&    


r*   c                 C   s
   t | dS )z3
    Formats a numified `datestring` properly.
    zXXXX-XX-XX XX:XX:XX)r)   )Z
datestringr@   r@   rA   r+   :  s    r+   c                 C   s<   | dksJ | d dv r d|  S dddd | d	 d|  S )
a)  
    Formats an ordinal.
    Doesn't handle negative numbers.

        >>> nthstr(1)
        '1st'
        >>> nthstr(0)
        '0th'
        >>> [nthstr(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14, 15]]
        ['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th', '15th']
        >>> [nthstr(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]]
        ['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd']
        >>> [nthstr(x) for x in [111, 112, 113, 114, 115]]
        ['111th', '112th', '113th', '114th', '115th']

    r   r   )         z%sthz%sstz%sndz%srd)r      r-  
   )rd   )rk   r@   r@   rA   r,   A  s    r,   c                 C   s   | r|S |S dS )z
    Function replacement for if-else to use in expressions.

        >>> x = 2
        >>> cond(x % 2 == 0, "even", "odd")
        'even'
        >>> cond(x % 2 == 0, "even", "odd") + '_row'
        'even_row'
    Nr@   )	predicateZconsequencealternativer@   r@   rA   r-   Y  s    
r-   c                   @   s    e Zd ZdZdd Zdd ZdS )r.   z
    Captures everything `func` prints to stdout and returns it instead.

        >>> def idiot():
        ...     print("foo")
        >>> capturestdout(idiot)()
        'foo\n'

    **WARNING:** Not threadsafe!
    c                 C   s
   || _ d S r8   r   r=   r   r@   r@   rA   r   u  s    zCaptureStdout.__init__c                 O   s>   t  }tj}|t_z| j|i | W |t_n|t_0 | S r8   )r   r   stdoutr   r[   )r=   r   r   r   Z	oldstdoutr@   r@   rA   rQ   x  s    zCaptureStdout.__call__Nr   r@   r@   r@   rA   r.   i  s   r.   c                   @   s    e Zd ZdZdd Zdd ZdS )r0   a	  
    Profiles `func` and returns a tuple containing its output
    and a string with human-readable profiling information.

        >>> import time
        >>> out, inf = profile(time.sleep)(.001)
        >>> out
        >>> inf[:10].strip()
        'took 0.0'
    c                 C   s
   || _ d S r8   r9  r:  r@   r@   rA   r     s    zProfile.__init__c                 G   s   dd l }dd l}dd l}dd l}| \}}|| | }t }	|j| j	g|R  }
t |	 }	t
 }|j||d}|  |dd |d |  dt|	 d }|| 7 }z|| W n ty   Y n0 |
|fS )Nr   )streamr   Zcalls(   z

took z	 seconds
)cProfilepstatsostempfilemkstempcloser0   r   Zruncallr   r   ZStatsZ
strip_dirsZ
sort_statsZprint_statsZprint_callersrS   r[   r   IOError)r=   r   r>  r?  r@  rA  r  r   ZprofZstimerh   r   statsrZ   r@   r@   rA   rQ     s.    

zProfile.__call__Nr   r@   r@   r@   rA   r0     s   r0   c                 C   s   |   } i }t| D ]\}}t|ds(q|r8||s8qt|d dd z| }t|| t| W q   td t|d tddt 	d  Y q0 qtd	 td
 t|D ]\}}tdt
|d | qdS )aZ  
    Tries a series of functions and prints their results.
    `context` is a dictionary mapping names to values;
    the value will only be tried if it's callable.

        >>> tryall(dict(j=lambda: True))
        j: True
        ----------------------------------------
        results:
           True: 1

    For example, you might have a file `test/stuff.py`
    with a series of functions testing various things in it.
    At the bottom, have a line:

        if __name__ == "__main__": tryall(globals())

    Then you can run `python test/stuff.py` and get the results of
    all the tests.
    rQ   :r   )r   ERRORz   z
   r   z(----------------------------------------zresults:r  N)copyr   rX   r   printr!   r   r   
format_excr0  rS   )contextr1  resultsr>   rD   r   r@   r@   rA   r2     s(    


$r2   c                   @   s   e Zd ZdZe Zdd Zdd Zdd Zdd	 Z	e
e	Z	d
d Zdd Zdd Zdd ZeZdd Zdd Zd/ddZdd Zdd Zdd Zdd  ZeZd!d" Zd#d$ Zd%d& Zd'd( Zd0d)d*Zd+d, Zd-d. ZeZdS )1r3   a#  
    Thread local storage.

        >>> d = ThreadedDict()
        >>> d.x = 1
        >>> d.x
        1
        >>> import threading
        >>> def f(): d.x = 2
        ...
        >>> t = threading.Thread(target=f)
        >>> t.start()
        >>> t.join()
        >>> d.x
        1
    c                 C   s   t j|  d S r8   )r3   
_instancesrl   rK   r@   r@   rA   r     s    zThreadedDict.__init__c                 C   s   t j|  d S r8   )r3   rM  r   rK   r@   r@   rA   __del__  s    zThreadedDict.__del__c                 C   s   t | S r8   )idrK   r@   r@   rA   __hash__   s    zThreadedDict.__hash__c                  C   s   t tjD ]} |   q
dS )z"Clears all ThreadedDict instances.N)rc   r3   rM  clear)tr@   r@   rA   	clear_all  s    zThreadedDict.clear_allc                 C   s
   | j | S r8   __dict__rx   r@   r@   rA   r     s    zThreadedDict.__getitem__c                 C   s   || j |< d S r8   rT  rC   r@   r@   rA   __setitem__  s    zThreadedDict.__setitem__c                 C   s   | j |= d S r8   rT  rx   r@   r@   rA   __delitem__  s    zThreadedDict.__delitem__c                 C   s
   || j v S r8   rT  rx   r@   r@   rA   __contains__  s    zThreadedDict.__contains__c                 C   s   | j   d S r8   )rU  rQ  rK   r@   r@   rA   rQ    s    zThreadedDict.clearc                 C   s
   | j  S r8   )rU  rH  rK   r@   r@   rA   rH    s    zThreadedDict.copyNc                 C   s   | j ||S r8   )rU  rd   r=   r>   r   r@   r@   rA   rd   !  s    zThreadedDict.getc                 C   s
   | j  S r8   )rU  r   rK   r@   r@   rA   r   $  s    zThreadedDict.itemsc                 C   s
   t | jS r8   )r   rU  rK   r@   r@   rA   r   '  s    zThreadedDict.iteritemsc                 C   s
   | j  S r8   )rU  rb   rK   r@   r@   rA   rb   *  s    zThreadedDict.keysc                 C   s.   zt | jW S  ty(   | j  Y S 0 d S r8   )iterkeysrU  	NameErrorrb   rK   r@   r@   rA   rZ  -  s    zThreadedDict.iterkeysc                 C   s
   | j  S r8   )rU  rw   rK   r@   r@   rA   rw   5  s    zThreadedDict.valuesc                 C   s
   t | jS r8   )r   rU  rK   r@   r@   rA   r   8  s    zThreadedDict.itervaluesc                 G   s   | j j|g|R  S r8   )rU  r`   )r=   r>   r   r@   r@   rA   r`   ;  s    zThreadedDict.popc                 C   s
   | j  S r8   )rU  popitemrK   r@   r@   rA   r\  >  s    zThreadedDict.popitemc                 C   s   | j ||S r8   )rU  rj   rY  r@   r@   rA   rj   A  s    zThreadedDict.setdefaultc                 O   s   | j j|i | d S r8   )rU  r   )r=   r   kwargsr@   r@   rA   r   D  s    zThreadedDict.updatec                 C   s
   d| j  S )Nz<ThreadedDict %r>rT  rK   r@   r@   rA   rJ   G  s    zThreadedDict.__repr__)N)N) rL   rM   rN   rO   r   rM  r   rN  rP  rS  staticmethodr   rV  rW  rX  Zhas_keyrQ  rH  rd   r   r   rb   rZ  r   rw   r   r`   r\  rj   r   rJ   __str__r@   r@   r@   rA   r3     s8   

r3   c                 C   s,   t |D ]\}}|dkrqt| || qdS )a:  
    Automatically assigns local variables to `self`.

        >>> self = storage()
        >>> autoassign(self, dict(a=1, b=2))
        >>> self.a
        1
        >>> self.b
        2

    Generally used in `__init__` methods, as in:

        def __init__(self, foo, bar, baz=1): autoassign(self, locals())
    r=   N)r   re   )r=   localsr>   rD   r@   r@   rA   r5   P  s    r5   c                 C   sN   | dk rt dd}g }| dkr@t| d\} }|d||  qd|pLdS )ax  
    Converts an integer to base 36 (a useful scheme for human-sayable IDs).

        >>> to36(35)
        'z'
        >>> to36(119292)
        '2k1o'
        >>> int(to36(939387374), 36)
        939387374
        >>> to36(0)
        '0'
        >>> to36(-393)
        Traceback (most recent call last):
            ...
        ValueError: must supply a positive integer

    r   zmust supply a positive integerZ$0123456789abcdefghijklmnopqrstuvwxyz$   r   0)r   r   r  r   )qlettersZ	convertedr   r@   r@   rA   r6   e  s    r6   z(?<!\()(http://(\S+))c                 K   s   | dg }t| ||||fi |}|D ]}t|trT||d |d |d q(t|drtj	t
|dd}	t
|dd}
||	| |
 q(t|trt|d	}| }|  tj	|}	||	|d q(td
t| q(|  dS )a  
    Sends the email message `message` with mail and envelope headers
    for from `from_address_` to `to_address` with `subject`.
    Additional email headers can be specified with the dictionary
    `headers.

    Optionally cc, bcc and attachments can be specified as keyword arguments.
    Attachments must be an iterable and each attachment can be either a
    filename or a file object or a dictionary with filename, content and
    optionally content_type keys.

    If `web.config.smtp_server` is set, it will send the message
    to that SMTP server. Otherwise it will look for
    `/usr/sbin/sendmail`, the typical location for the sendmail-style
    binary. To use sendmail from a different path, set `web.config.sendmail_path`.
    attachmentsr   r  content_typereadr   r   NrbzInvalid attachment: %s)r`   _EmailMessagerR   rI   attachrd   rX   r@  pathbasenamegetattrrg  rS   r   rC  r   reprsend)from_address
to_addresssubjectmessageheadersr   re  mailar   rf  r  r  r@   r@   rA   r7     s"    



r7   c                   @   s`   e Zd ZdddZdd Zd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S )ri  Nc                    s   dd }t |}t |}t |}||}||dg }||dg }	|| |	 }
dd l  j|d | _ fdd|
D | _t|d	||d
|pi | _	|rd	|| j	d< | 
 | _| jdd | jdd | jdd | j|d d| _d S )Nc                 S   s&   t | tst| gS dd | D S d S )Nc                 S   s   g | ]}t |qS r@   r   )r\   rv  r@   r@   rA   r]     r^   z;_EmailMessage.__init__.<locals>.listify.<locals>.<listcomp>)rR   rc   r   rY   r@   r@   rA   listify  s    

z'_EmailMessage.__init__.<locals>.listifyccbccr   r   c                    s   g | ]} j |d  qS )r   )utils	parseaddr)r\   r   emailr@   rA   r]     r^   z*_EmailMessage.__init__.<locals>.<listcomp>z, )FromToSubjectCczContent-Transfer-Encoding7bitContent-DispositioninlinezMIME-Versionz1.0r   F)r   rd   email.utilsrz  r{  rp  
recipientsr"   r   rt  new_messagers  
add_headerset_payload	multipart)r=   rp  rq  rr  rs  rt  r   rw  rx  ry  r  r@   r|  rA   r     s.    
z_EmailMessage.__init__c                 C   s   ddl m} | S )Nr   )Message)email.messager  )r=   r  r@   r@   rA   r    s    z_EmailMessage.new_messagec                 C   s   | j s2|  }|dd || j || _d| _ dd l}zddlm} W n   ddlm} Y n0 |px|	|d pxd}|  }|
| |d| |jdd	|d
 |ds|| | j| d S )NzContent-Typezmultipart/mixedTr   )encoders)Encoderszapplication/octet-streamr  
attachment)r   ztext/)r  r  r  rj  rs  	mimetypesr}  r  r  
guess_typer  r   encode_base64)r=   r   r  rf  msgr  r  r@   r@   rA   rj    s.    


z_EmailMessage.attachc                 C   sF   t | jD ]0\}}| dkr,| j| q
| j|| q
i | _d S )Nzcontent-type)r   rt  lowerrs  set_typer  )r=   r?   rn   r@   r@   rA   prepare_message  s
    z_EmailMessage.prepare_messagec                 C   s   |    | j }zddlm} W n ty@   tt d}Y n0 |jdrZ| 	| n&|jddkrv| 
| n
| | d S )Nr   webapiconfigsmtp_serverZemail_engineZaws)r  rs  	as_stringr   r  ImportErrorr   r  rd   send_with_smtpsend_with_awsdefault_email_sender)r=   message_textr  r@   r@   rA   ro    s    
z_EmailMessage.sendc                 C   sn   zddl m} W n ty.   tt d}Y n0 dd l}|jj|jd|jdd}|	|| j
| j d S )Nr   r  r  r   aws_access_key_idaws_secret_access_key)r  r  )r   r  r  r   Zboto.sesZsesZSESConnectionr  rd   Zsend_raw_emailrp  r  )r=   r  r  Zbotor   r@   r@   rA   r    s    

z_EmailMessage.send_with_awsc                 C   s   zddl m} W n ty.   tt d}Y n0 |jd}|jdd}|jd}|jd}|jd	d }|jd
d}dd l}	|	||}
|r|
| |r|
	  |

  |
	  |r|r|
|| |
| j| j| |
  d S )Nr   r  r  r  Z	smtp_portr   Zsmtp_usernameZsmtp_passwordZsmtp_debuglevelZsmtp_starttlsF)r   r  r  r   r  rd   smtplibSMTPset_debuglevelehlostarttlsloginr7   rp  r  quit)r=   r  r  serverportusernamepasswordZdebug_levelr  r  Z
smtpserverr@   r@   rA   r    s,    
z_EmailMessage.send_with_smtpc                 C   s   zddl m} W n ty.   tt d}Y n0 |jdd}| jdrRJ d| jD ]}|drXJ dqX|d| jg| j }t	j
|t	jd	}|j|d
 |j  |  d S )Nr   r  r  Zsendmail_pathz/usr/sbin/sendmailr+  securityz-f)stdinr   )r   r  r  r   r  rd   rp  r   r  
subprocessPopenPIPEr  r   encoderC  wait)r=   r  r  r7   r   cmdpr@   r@   rA   r  ;  s    

z"_EmailMessage.default_email_senderc                 C   s   dS )Nz<EmailMessage>r@   rK   r@   r@   rA   rJ   O  s    z_EmailMessage.__repr__c                 C   s
   | j  S r8   )rs  r  rK   r@   r@   rA   r_  R  s    z_EmailMessage.__str__)N)N)rL   rM   rN   r   r  rj  r  ro  r  r  r  rJ   r_  r@   r@   r@   rA   ri    s   
$
	ri  __main__)r   )N)NT)r_   )r   )N)N)N)N)N)N)RrO   r  r@  rer   r  r   r   r   r   r   ZthreadlocalZ
py3helpersr   r   r   r  io__all__rI   r   r   r	   r
   r   rc   ra   r   	frozensetr   r   r   r   r   r   r   r   r   r   r   compiler   r   r   r   r   r   r   r   r   r   r   r   r    r!   r"   r#   r$   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   Zr_urlr7   ri  rL   doctesttestmodr@   r@   r@   rA   <module>   s   5(PW
5S
	
1a
	





a5
1
,g
' +
