U
    
W[\                     @   s   d Z ddlmZmZ ddlmZ ddlmZ ddlm	Z	 ddl
mZmZmZ ddlmZ ddlmZmZmZmZ d	ZG d
d deZG dd deZG dd deZG dd deZG dd dee	ZdddddgZdS )ap  
Memcache client protocol. Memcached is a caching server, storing data in the
form of pairs key/value, and memcache is the protocol to talk with it.

To connect to a server, create a factory for L{MemCacheProtocol}::

    from twisted.internet import reactor, protocol
    from twisted.protocols.memcache import MemCacheProtocol, DEFAULT_PORT
    d = protocol.ClientCreator(reactor, MemCacheProtocol
        ).connectTCP("localhost", DEFAULT_PORT)
    def doSomething(proto):
        # Here you call the memcache operations
        return proto.set("mykey", "a lot of data")
    d.addCallback(doSomething)
    reactor.run()

All the operations of the memcache protocol are present, but
L{MemCacheProtocol.set} and L{MemCacheProtocol.get} are the more important.

See U{http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt} for
more information about the protocol.
    )absolute_importdivision)deque)LineReceiver)TimeoutMixin)DeferredfailTimeoutError)log)
intToBytes	iteritemsnativeStringnetworkStringi+  c                   @   s   e Zd ZdZdS )NoSuchCommandzA
    Exception raised when a non existent command is called.
    N__name__
__module____qualname____doc__ r   r   </usr/lib/python3/dist-packages/twisted/protocols/memcache.pyr   -   s   r   c                   @   s   e Zd ZdZdS )ClientErrorz1
    Error caused by an invalid client call.
    Nr   r   r   r   r   r   4   s   r   c                   @   s   e Zd ZdZdS )ServerErrorz*
    Problem happening on the server.
    Nr   r   r   r   r   r   ;   s   r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	Commanda8  
    Wrap a client action into an object, that holds the values used in the
    protocol.

    @ivar _deferred: the L{Deferred} object that will be fired when the result
        arrives.
    @type _deferred: L{Deferred}

    @ivar command: name of the command sent to the server.
    @type command: L{bytes}
    c                 K   s0   || _ t | _| D ]\}}t| || qdS )z
        Create a command.

        @param command: the name of the command.
        @type command: L{bytes}

        @param kwargs: this values will be stored as attributes of the object
            for future use
        N)commandr   	_deferreditemssetattr)selfr   kwargskvr   r   r   __init__O   s    
zCommand.__init__c                 C   s   | j | dS )zB
        Shortcut method to fire the underlying deferred.
        N)r   callback)r   valuer   r   r   success_   s    zCommand.successc                 C   s   | j | dS )z5
        Make the underlying deferred fails.
        N)r   Zerrback)r   errorr   r   r   r   f   s    zCommand.failN)r   r   r   r   r"   r%   r   r   r   r   r   r   B   s   r   c                   @   sT  e Zd ZdZdZdZdRd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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)d* Zd+d, ZdSd.d/ZdTd0d1Zd2d3 ZdUd5d6ZdVd7d8ZdWd9d:ZdXd;d<Z d=d> Z!d?d@ Z"dAdB Z#dYdCdDZ$dZdEdFZ%dGdH Z&d[dJdKZ'dLdM Z(dNdO Z)dPdQ Z*dIS )\MemCacheProtocola1  
    MemCache protocol: connect to a memcached server to store/retrieve values.

    @ivar persistentTimeOut: the timeout period used to wait for a response.
    @type persistentTimeOut: L{int}

    @ivar _current: current list of requests waiting for an answer from the
        server.
    @type _current: L{deque} of L{Command}

    @ivar _lenExpected: amount of data expected in raw mode, when reading for
        a value.
    @type _lenExpected: L{int}

    @ivar _getBuffer: current buffer of data, used to store temporary data
        when reading in raw mode.
    @type _getBuffer: L{list}

    @ivar _bufferLength: the total amount of bytes in C{_getBuffer}.
    @type _bufferLength: L{int}

    @ivar _disconnected: indicate if the connectionLost has been called or not.
    @type _disconnected: L{bool}
       F<   c                 C   s*   t  | _d| _d| _d| _| | _| _dS )z
        Create the protocol.

        @param timeOut: the timeout to wait before detecting that the
            connection is dead and close it. It's expressed in seconds.
        @type timeOut: L{int}
        N)r   _current_lenExpected
_getBuffer_bufferLengthpersistentTimeOuttimeOut)r   r/   r   r   r   r"      s
    zMemCacheProtocol.__init__c                 C   s    | j r| j  }|| q dS )zW
        Cancel all the outstanding commands, making them fail with C{reason}.
        N)r*   popleftr   )r   reasoncmdr   r   r   _cancelCommands   s    
z MemCacheProtocol._cancelCommandsc                 C   s   |  td | j  dS )z:
        Close the connection in case of timeout.
        zConnection timeoutN)r3   r	   Z	transportZloseConnectionr   r   r   r   timeoutConnection   s    z"MemCacheProtocol.timeoutConnectionc                 C   s    d| _ | | t| | dS )z9
        Cause any outstanding commands to fail.
        TN)_disconnectedr3   r   connectionLost)r   r1   r   r   r   r7      s    
zMemCacheProtocol.connectionLostc                 C   s"   | j s| | j t| | dS )zA
        Override sendLine to add a timeout to response.
        N)r*   
setTimeoutr.   r   sendLine)r   liner   r   r   r9      s    zMemCacheProtocol.sendLinec                 C   s   |    | j| |  jt|7  _| j| jd krd| j}|d| j }|| jd d }|}d| _d| _d| _| jd }|jr|j	|j
 \}}|||f|j	|j
< n||_| | dS )z)
        Collect data for a get.
               Nr   )resetTimeoutr,   appendr-   lenr+   joinr*   multiplevalues
currentKeyr$   ZsetLineMode)r   dataZbufZremvalr2   flagscasr   r   r   rawDataReceived   s"    
z MemCacheProtocol.rawDataReceivedc                 C   s   | j  d dS )z?
        Manage a success response to a set operation.
        TNr*   r0   r%   r4   r   r   r   
cmd_STORED   s    zMemCacheProtocol.cmd_STOREDc                 C   s   | j  d dS )z
        Manage a specific 'not stored' response to a set operation: this is not
        an error, but some condition wasn't met.
        FNrI   r4   r   r   r   cmd_NOT_STORED   s    zMemCacheProtocol.cmd_NOT_STOREDc                 C   s   | j  }|jdkrN|jr:dd t|jD }|| q||j|jf nb|jdkr|jrl||j q||j|j	|jf n,|jdkr||j nt
dt|jf dS )zB
        This the end token to a get or a stat operation.
           getc                 S   s    i | ]\}}||d d d qS )Nr;   r   ).0keyrE   r   r   r   
<dictcomp>   s      z,MemCacheProtocol.cmd_END.<locals>.<dictcomp>   gets   statsz%Unexpected END response to %s commandN)r*   r0   r   rA   r   rB   r%   rF   r$   rG   RuntimeErrorr   )r   r2   rB   r   r   r   cmd_END   s"    




zMemCacheProtocol.cmd_ENDc                 C   s   | j  d dS )z=
        Manage error response for incr/decr/delete.
        FNrI   r4   r   r   r   cmd_NOT_FOUND   s    zMemCacheProtocol.cmd_NOT_FOUNDc                 C   s   | j d }|jdkr(| \}}}d}n| \}}}}t|| _g | _d| _|jr||jkrft	d||_
t||g|j|< n"|j|krt	dt||_||_|   dS )z:
        Prepare the reading a value after a get.
        r   rL   r<   zUnexpected commands answer.N)r*   r   splitintr+   r,   r-   rA   keysrR   rC   rB   rN   rF   rG   Z
setRawMode)r   r:   r2   rN   rF   lengthrG   r   r   r   	cmd_VALUE  s$    





zMemCacheProtocol.cmd_VALUEc                 C   s(   | j d }|dd\}}||j|< dS )z-
        Reception of one stat line.
        r          N)r*   rU   rB   )r   r:   r2   rN   rE   r   r   r   cmd_STAT  s    
zMemCacheProtocol.cmd_STATc                 C   s   | j  | dS )z%
        Read version token.
        NrI   )r   ZversionDatar   r   r   cmd_VERSION%  s    zMemCacheProtocol.cmd_VERSIONc                 C   s$   t d | j }|t  dS )z7
        A non-existent command has been sent.
        zNon-existent command sent.N)r
   errr*   r0   r   r   )r   r2   r   r   r   	cmd_ERROR,  s    

zMemCacheProtocol.cmd_ERRORc                 C   s2   t |}td|  | j }|t| dS )z0
        An invalid input as been sent.
        zInvalid input: N)reprr
   r^   r*   r0   r   r   r   ZerrTextr2   r   r   r   cmd_CLIENT_ERROR5  s    
z!MemCacheProtocol.cmd_CLIENT_ERRORc                 C   s2   t |}td|  | j }|t| dS )z4
        An error has happened server-side.
        zServer error: N)r`   r
   r^   r*   r0   r   r   ra   r   r   r   cmd_SERVER_ERROR?  s    
z!MemCacheProtocol.cmd_SERVER_ERRORc                 C   s   | j  d dS )z>
        A delete command has completed successfully.
        TNrI   r4   r   r   r   cmd_DELETEDI  s    zMemCacheProtocol.cmd_DELETEDc                 C   s   | j  d dS )z6
        The last command has been completed.
        TNrI   r4   r   r   r   cmd_OKP  s    zMemCacheProtocol.cmd_OKc                 C   s   | j  d dS )z5
        A C{checkAndSet} update has failed.
        FNrI   r4   r   r   r   
cmd_EXISTSW  s    zMemCacheProtocol.cmd_EXISTSc                 C   s   |    |ddd }t| dt| d}|dk	rb|dddd }|rZ||d  q|  nL|dd}t| dt| d}|dk	r|  n| j }t|}|| | js| 	d dS )z8
        Receive line commands from the server.
        rZ   r[   r   Zcmd_N   _)
r=   rU   getattrr   replacer*   r0   rV   r%   r8   )r   r:   tokenr2   argsrE   r   r   r   lineReceived^  s"    

zMemCacheProtocol.lineReceivedr[   c                 C   s   |  d||S )a  
        Increment the value of C{key} by given value (default to 1).
        C{key} must be consistent with an int. Return the new value.

        @param key: the key to modify.
        @type key: L{bytes}

        @param val: the value to increment.
        @type val: L{int}

        @return: a deferred with will be called back with the new value
            associated with the key (after the increment).
        @rtype: L{Deferred}
        s   incr	_incrdecrr   rN   rE   r   r   r   	increment|  s    zMemCacheProtocol.incrementc                 C   s   |  d||S )a  
        Decrement the value of C{key} by given value (default to 1).
        C{key} must be consistent with an int. Return the new value, coerced to
        0 if negative.

        @param key: the key to modify.
        @type key: L{bytes}

        @param val: the value to decrement.
        @type val: L{int}

        @return: a deferred with will be called back with the new value
            associated with the key (after the decrement).
        @rtype: L{Deferred}
        s   decrrm   ro   r   r   r   	decrement  s    zMemCacheProtocol.decrementc                 C   s   | j rttdS t|ts2ttdt|f S t|| jkrLttdS d	||t
t|g}| | t||d}| j| |jS )z1
        Internal wrapper for incr/decr.
        not connected)Invalid type for key: %s, expecting bytesKey too longrZ   rN   )r6   r   rR   
isinstancebytesr   typer?   MAX_KEY_LENGTHr@   r   rV   r9   r   r*   r>   r   )r   r2   rN   rE   fullcmdcmdObjr   r   r   rn     s    

zMemCacheProtocol._incrdecrr   c                 C   s   |  d||||dS )a  
        Replace the given C{key}. It must already exist in the server.

        @param key: the key to replace.
        @type key: L{bytes}

        @param val: the new value associated with the key.
        @type val: L{bytes}

        @param flags: the flags to store with the key.
        @type flags: L{int}

        @param expireTime: if different from 0, the relative time in seconds
            when the key will be deleted from the store.
        @type expireTime: L{int}

        @return: a deferred that will fire with C{True} if the operation has
            succeeded, and C{False} with the key didn't previously exist.
        @rtype: L{Deferred}
        s   replacer<   _setr   rN   rE   rF   
expireTimer   r   r   ri     s    zMemCacheProtocol.replacec                 C   s   |  d||||dS )a  
        Add the given C{key}. It must not exist in the server.

        @param key: the key to add.
        @type key: L{bytes}

        @param val: the value associated with the key.
        @type val: L{bytes}

        @param flags: the flags to store with the key.
        @type flags: L{int}

        @param expireTime: if different from 0, the relative time in seconds
            when the key will be deleted from the store.
        @type expireTime: L{int}

        @return: a deferred that will fire with C{True} if the operation has
            succeeded, and C{False} with the key already exists.
        @rtype: L{Deferred}
        s   addr<   r|   r~   r   r   r   add  s    zMemCacheProtocol.addc                 C   s   |  d||||dS )a9  
        Set the given C{key}.

        @param key: the key to set.
        @type key: L{bytes}

        @param val: the value associated with the key.
        @type val: L{bytes}

        @param flags: the flags to store with the key.
        @type flags: L{int}

        @param expireTime: if different from 0, the relative time in seconds
            when the key will be deleted from the store.
        @type expireTime: L{int}

        @return: a deferred that will fire with C{True} if the operation has
            succeeded.
        @rtype: L{Deferred}
        s   setr<   r|   r~   r   r   r   set  s    zMemCacheProtocol.setc                 C   s   |  d|||||S )am  
        Change the content of C{key} only if the C{cas} value matches the
        current one associated with the key. Use this to store a value which
        hasn't been modified since last time you fetched it.

        @param key: The key to set.
        @type key: L{bytes}

        @param val: The value associated with the key.
        @type val: L{bytes}

        @param cas: Unique 64-bit value returned by previous call of C{get}.
        @type cas: L{bytes}

        @param flags: The flags to store with the key.
        @type flags: L{int}

        @param expireTime: If different from 0, the relative time in seconds
            when the key will be deleted from the store.
        @type expireTime: L{int}

        @return: A deferred that will fire with C{True} if the operation has
            succeeded, C{False} otherwise.
        @rtype: L{Deferred}
        s   casr|   )r   rN   rE   rG   rF   r   r   r   r   checkAndSet  s    zMemCacheProtocol.checkAndSetc           
   	   C   s   | j rttdS t|ts2ttdt|f S t|| jkrLttdS t|tslttdt|f S |rxd| }t|}d	||t
d|||f g| }| | | | t||||d}	| j|	 |	jS )z6
        Internal wrapper for setting values.
        rr   rs   rt   z+Invalid type for value: %s, expecting bytesrZ   z%d %d %d)rN   rF   rX   )r6   r   rR   rv   rw   r   rx   r?   ry   r@   r   r9   r   r*   r>   r   )
r   r2   rN   rE   rF   r   rG   rX   rz   r{   r   r   r   r}     s:    

 

zMemCacheProtocol._setc                 C   s   |  d||dddS )a  
        Append given data to the value of an existing key.

        @param key: The key to modify.
        @type key: L{bytes}

        @param val: The value to append to the current value associated with
            the key.
        @type val: L{bytes}

        @return: A deferred that will fire with C{True} if the operation has
            succeeded, C{False} otherwise.
        @rtype: L{Deferred}
        s   appendr   r<   r|   ro   r   r   r   r>   4  s    zMemCacheProtocol.appendc                 C   s   |  d||dddS )a  
        Prepend given data to the value of an existing key.

        @param key: The key to modify.
        @type key: L{bytes}

        @param val: The value to prepend to the current value associated with
            the key.
        @type val: L{bytes}

        @return: A deferred that will fire with C{True} if the operation has
            succeeded, C{False} otherwise.
        @rtype: L{Deferred}
        s   prependr   r<   r|   ro   r   r   r   prependG  s    zMemCacheProtocol.prependc                 C   s   |  |g|dS )a  
        Get the given C{key}. It doesn't support multiple keys. If
        C{withIdentifier} is set to C{True}, the command issued is a C{gets},
        that will return the current identifier associated with the value. This
        identifier has to be used when issuing C{checkAndSet} update later,
        using the corresponding method.

        @param key: The key to retrieve.
        @type key: L{bytes}

        @param withIdentifier: If set to C{True}, retrieve the current
            identifier along with the value and the flags.
        @type withIdentifier: L{bool}

        @return: A deferred that will fire with the tuple (flags, value) if
            C{withIdentifier} is C{False}, or (flags, cas identifier, value)
            if C{True}.  If the server indicates there is no value
            associated with C{key}, the returned value will be L{None} and
            the returned flags will be C{0}.
        @rtype: L{Deferred}
        F_get)r   rN   withIdentifierr   r   r   getZ  s    zMemCacheProtocol.getc                 C   s   |  ||dS )a  
        Get the given list of C{keys}.  If C{withIdentifier} is set to C{True},
        the command issued is a C{gets}, that will return the identifiers
        associated with each values. This identifier has to be used when
        issuing C{checkAndSet} update later, using the corresponding method.

        @param keys: The keys to retrieve.
        @type keys: L{list} of L{bytes}

        @param withIdentifier: If set to C{True}, retrieve the identifiers
            along with the values and the flags.
        @type withIdentifier: L{bool}

        @return: A deferred that will fire with a dictionary with the elements
            of C{keys} as keys and the tuples (flags, value) as values if
            C{withIdentifier} is C{False}, or (flags, cas identifier, value) if
            C{True}.  If the server indicates there is no value associated with
            C{key}, the returned values will be L{None} and the returned flags
            will be C{0}.
        @rtype: L{Deferred}

        @since: 9.0
        Tr   )r   rW   r   r   r   r   getMultiples  s    zMemCacheProtocol.getMultiplec           	      C   s   t |}| jrttdS |D ]F}t|tsFttdt|f   S t|| j	krttd  S q|rpd}nd}d
|g| }| | |rtdd |D }t|||d	d
}nt||d ddddd}| j| |jS )z>
        Helper method for C{get} and C{getMultiple}.
        rr   rs   rt   rP   rL   rZ   c                 S   s   g | ]}|d fqS ))r   r<   Nr   )rM   rN   r   r   r   
<listcomp>  s     z)MemCacheProtocol._get.<locals>.<listcomp>T)rW   rB   rA   r   Nr<   F)rN   r$   rF   rG   rA   )listr6   r   rR   rv   rw   r   rx   r?   ry   r@   r9   dictr   r*   r>   r   )	r   rW   r   rA   rN   r2   rz   rB   r{   r   r   r   r     s2    


zMemCacheProtocol._getNc                 C   sL   |rd| }nd}| j r$ttdS | | tdi d}| j| |jS )a  
        Get some stats from the server. It will be available as a dict.

        @param arg: An optional additional string which will be sent along
            with the I{stats} command.  The interpretation of this value by
            the server is left undefined by the memcache protocol
            specification.
        @type arg: L{None} or L{bytes}

        @return: a deferred that will fire with a L{dict} of the available
            statistics.
        @rtype: L{Deferred}
        s   stats rQ   rr   )rB   r6   r   rR   r9   r   r*   r>   r   )r   argr2   r{   r   r   r   stats  s    

zMemCacheProtocol.statsc                 C   s6   | j rttdS | d td}| j| |jS )z
        Get the version of the server.

        @return: a deferred that will fire with the string value of the
            version.
        @rtype: L{Deferred}
        rr   s   versionr   r   r{   r   r   r   version  s    
zMemCacheProtocol.versionc                 C   s^   | j rttdS t|ts2ttdt|f S | d|  td|d}| j	
| |jS )a  
        Delete an existing C{key}.

        @param key: the key to delete.
        @type key: L{bytes}

        @return: a deferred that will be called back with C{True} if the key
            was successfully deleted, or C{False} if not.
        @rtype: L{Deferred}
        rr   rs   s   delete s   deleteru   )r6   r   rR   rv   rw   r   rx   r9   r   r*   r>   r   )r   rN   r{   r   r   r   delete  s    
zMemCacheProtocol.deletec                 C   s6   | j rttdS | d td}| j| |jS )z
        Flush all cached values.

        @return: a deferred that will be called back with C{True} when the
            operation has succeeded.
        @rtype: L{Deferred}
        rr   s	   flush_allr   r   r   r   r   flushAll  s    
zMemCacheProtocol.flushAll)r)   )r[   )r[   )r   r   )r   r   )r   r   )r   r   )F)F)N)+r   r   r   r   ry   r6   r"   r3   r5   r7   r9   rH   rJ   rK   rS   rT   rY   r\   r]   r_   rb   rc   rd   re   rf   rl   rp   rq   rn   ri   r   r   r   r}   r>   r   r   r   r   r   r   r   r   r   r   r   r   r'   n   sP   
					










r'   DEFAULT_PORTN)r   Z
__future__r   r   collectionsr   Ztwisted.protocols.basicr   Ztwisted.protocols.policiesr   Ztwisted.internet.deferr   r   r	   Ztwisted.pythonr
   Ztwisted.python.compatr   r   r   r   r   	Exceptionr   r   r   objectr   r'   __all__r   r   r   r   <module>   s*   ,     