o
    bc\                     @   s   d 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
 ddlmZ ddl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ZG dd dee
Zg d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.
    )deque)DeferredTimeoutErrorfail)LineReceiver)TimeoutMixin)log)nativeStringnetworkStringi+  c                   @      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   (       r   c                   @   r   )ClientErrorz1
    Error caused by an invalid client call.
    Nr   r   r   r   r   r   .   r   r   c                   @   r   )ServerErrorz*
    Problem happening on the server.
    Nr   r   r   r   r   r   4   r   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__G   s
   
zCommand.__init__c                 C      | j | dS )zB
        Shortcut method to fire the underlying deferred.
        N)r   callback)r   valuer   r   r   successV      zCommand.successc                 C   r!   )z5
        Make the underlying deferred fails.
        N)r   errback)r   errorr   r   r   r   \   r%   zCommand.failN)r   r   r   r   r    r$   r   r   r   r   r   r   :   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dSd0d1Zd2d3 ZdTd5d6ZdTd7d8ZdTd9d:ZdTd;d<Z d=d> Z!d?d@ Z"dAdB Z#dUdCdDZ$dUdEdFZ%dGdH Z&dVdJdKZ'dLdM Z(dNdO Z)dPdQ Z*dIS )W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   r0   r   r   r   r       s
   zMemCacheProtocol.__init__c                 C   s(   | j r| j  }|| | j sdS 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)r4   r   	transportloseConnectionr   r   r   r   timeoutConnection   s   z"MemCacheProtocol.timeoutConnectionc                 C   s    d| _ | | t| | dS )z9
        Cause any outstanding commands to fail.
        TN)_disconnectedr4   r   connectionLost)r   r2   r   r   r   r:      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   r<      s   zMemCacheProtocol.sendLinec                 C   s   |    | j| |  jt|7  _| j| jd kr`d| j}|d| j }|| jd d }|}d| _d| _d| _| jd }|jrV|j	|j
 \}}|||f|j	|j
< n||_| | dS dS )z)
        Collect data for a get.
               Nr   )resetTimeoutr-   appendr.   lenr,   joinr+   multiplevalues
currentKeyr#   setLineMode)r   databufremvalr3   flagscasr   r   r   rawDataReceived   s$   
z MemCacheProtocol.rawDataReceivedc                 C      | j  d dS )z?
        Manage a success response to a set operation.
        TNr+   r1   r$   r7   r   r   r   
cmd_STORED      zMemCacheProtocol.cmd_STOREDc                 C   rO   )z
        Manage a specific 'not stored' response to a set operation: this is not
        an error, but some condition wasn't met.
        FNrP   r7   r   r   r   cmd_NOT_STORED   s   zMemCacheProtocol.cmd_NOT_STOREDc                 C   s   | j  }|jdkr)|jrdd |j D }|| dS ||j|jf dS |jdkrF|jr9||j dS ||j|j	|jf dS |jdkrS||j dS t
dt|j)zB
        This the end token to a get or a stat operation.
           getc                 S   s    i | ]\}}||d d d qS )Nr>   r   ).0keyrK   r   r   r   
<dictcomp>   s     z,MemCacheProtocol.cmd_END.<locals>.<dictcomp>   gets   statsz%Unexpected END response to {} commandN)r+   r1   r   rD   rE   r   r$   rL   r#   rM   RuntimeErrorformatr	   )r   r3   rE   r   r   r   cmd_END   s"   



zMemCacheProtocol.cmd_ENDc                 C   rO   )z=
        Manage error response for incr/decr/delete.
        FNrP   r7   r   r   r   cmd_NOT_FOUND   rR   zMemCacheProtocol.cmd_NOT_FOUNDc                 C   s   | j d }|jdkr| \}}}d}n| \}}}}t|| _g | _d| _|jr@||jvr3t	d||_
t||g|j|< n|j|krIt	dt||_||_|   dS )z:
        Prepare the reading a value after a get.
        r   rT   r?   zUnexpected commands answer.N)r+   r   splitintr,   r-   r.   rD   keysrZ   rF   rE   rV   rL   rM   
setRawMode)r   r=   r3   rV   rL   lengthrM   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+   r^   rE   )r   r=   r3   rV   rK   r   r   r   cmd_STAT	  s   
zMemCacheProtocol.cmd_STATc                 C   s   | j  | dS )z%
        Read version token.
        NrP   )r   versionDatar   r   r   cmd_VERSION  rR   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+   r1   r   r   )r   r3   r   r   r   	cmd_ERROR  s   

zMemCacheProtocol.cmd_ERRORc                 C   2   t |}td|  | j }|t| dS )z0
        An invalid input as been sent.
        zInvalid input: N)reprr   ri   r+   r1   r   r   r   errTextr3   r   r   r   cmd_CLIENT_ERROR     
z!MemCacheProtocol.cmd_CLIENT_ERRORc                 C   rk   )z4
        An error has happened server-side.
        zServer error: N)rl   r   ri   r+   r1   r   r   rm   r   r   r   cmd_SERVER_ERROR(  rp   z!MemCacheProtocol.cmd_SERVER_ERRORc                 C   rO   )z>
        A delete command has completed successfully.
        TNrP   r7   r   r   r   cmd_DELETED1  rR   zMemCacheProtocol.cmd_DELETEDc                 C   rO   )z6
        The last command has been completed.
        TNrP   r7   r   r   r   cmd_OK7  rR   zMemCacheProtocol.cmd_OKc                 C   rO   )z5
        A C{checkAndSet} update has failed.
        FNrP   r7   r   r   r   
cmd_EXISTS=  rR   zMemCacheProtocol.cmd_EXISTSc                 C   s   |    |ddd }t| dt| d}|dur1|dddd }|r-||d  n*|  n&|dd}t| dt| d}|durI|  n| j }t|}|| | jsa| 	d dS dS )z8
        Receive line commands from the server.
        rd   re   r   cmd_N   _)
r@   r^   getattrr	   replacer+   r1   r_   r$   r;   )r   r=   tokenr3   argsrK   r   r   r   lineReceivedC  s$   

zMemCacheProtocol.lineReceivedre   c                 C      |  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   rV   rK   r   r   r   	increment`  s   zMemCacheProtocol.incrementc                 C   r|   )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   decrr}   r   r   r   r   	decrementq  s   zMemCacheProtocol.decrementc                 C   s   | j r	ttdS t|tsttdt| dS t|| jkr'ttdS d	||dt
|f g}| | t||d}| j| |jS )z1
        Internal wrapper for incr/decr.
        not connectedInvalid type for key: , expecting bytesKey too longrd   s   %drV   )r9   r   rZ   
isinstancebytesr   typerB   MAX_KEY_LENGTHrC   r_   r<   r   r+   rA   r   )r   r3   rV   rK   fullcmdcmdObjr   r   r   r~     s   

zMemCacheProtocol._incrdecrr   c                 C      |  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   rV   rK   rL   
expireTimer   r   r   rx        zMemCacheProtocol.replacec                 C   r   )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  r   zMemCacheProtocol.addc                 C   r   )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  r   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   rV   rK   rM   rL   r   r   r   r   checkAndSet  s   zMemCacheProtocol.checkAndSetc           
   	   C   s   | j r	ttdS t|tsttdt| dS t|| jkr'ttdS t|ts8ttdt| dS |r>d| }t|}d	||t
d|||f g| }| | | | t||||d}	| j|	 |	jS )	z6
        Internal wrapper for setting values.
        r   r   r   r   zInvalid type for value: rd   z%d %d %d)rV   rL   rb   )r9   r   rZ   r   r   r   r   rB   r   rC   r
   r<   r   r+   rA   r   )
r   r3   rV   rK   rL   r   rM   rb   r   r   r   r   r   r     s4   



zMemCacheProtocol._setc                 C      |  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   r   r   r   r   rA        zMemCacheProtocol.appendc                 C   r   )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   r   r   r   r   prepend'  r   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   rV   withIdentifierr   r   r   get9  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   r`   r   r   r   r   getMultipleQ  s   zMemCacheProtocol.getMultiplec           	      C   s   t |}| jrttdS |D ]$}t|ts$ttdt| d  S t|| j	kr3ttd  S q|r9d}nd}d
|g| }| | |rZd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}.
        r   r   r   r   rX   rT   rd   c                 S   s   i | ]}|d qS ))r   r?   Nr   )rU   rV   r   r   r   rW     s    z)MemCacheProtocol._get.<locals>.<dictcomp>T)r`   rE   rD   r   Nr?   F)rV   r#   rL   rM   rD   )listr9   r   rZ   r   r   r   r   rB   r   rC   r<   r   r+   rA   r   )	r   r`   r   rD   rV   r3   r   rE   r   r   r   r   r   k  s0   

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 rY   r   )rE   r9   r   rZ   r<   r   r+   rA   r   )r   argr3   r   r   r   r   stats  s   

zMemCacheProtocol.statsc                 C   6   | 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}
        r   s   versionr   r   r   r   r   r   version     
zMemCacheProtocol.versionc                 C   s`   | j r	ttdS t|tsttdt| d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}
        r   r   r   s   delete s   deleter   )r9   r   rZ   r   r   r   r   r<   r   r+   rA   r   )r   rV   r   r   r   r   delete  s   
zMemCacheProtocol.deletec                 C   r   )z
        Flush all cached values.

        @return: a deferred that will be called back with C{True} when the
            operation has succeeded.
        @rtype: L{Deferred}
        r   s	   flush_allr   r   r   r   r   flushAll  r   zMemCacheProtocol.flushAll)r*   )re   )r   r   )F)N)+r   r   r   r   r   r9   r    r4   r8   r:   r<   rN   rQ   rS   r\   r]   rc   rf   rh   rj   ro   rq   rr   rs   rt   r{   r   r   r~   rx   r   r   r   r   rA   r   r   r   r   r   r   r   r   r   r   r   r   r(   c   sR    
		








r(   )r(   DEFAULT_PORTr   r   r   N)r   collectionsr   twisted.internet.deferr   r   r   twisted.protocols.basicr   twisted.protocols.policiesr   twisted.pythonr   twisted.python.compatr	   r
   r   	Exceptionr   r   r   r   r(   __all__r   r   r   r   <module>   s$   )    x