Ejemplo n.º 1
0
 def on_connect(self, connection):
     "Called when the socket connects"
     self._sock = connection._sock
     self._buffer = SocketLineReader(self._sock, self.socket_read_size, name=connection.name)
     if connection.decode_responses:
         self.encoding = connection.encoding
Ejemplo n.º 2
0
class PythonParser(BaseParser):
    "Plain Python parsing class"
    encoding = None

    def __init__(self, socket_read_size):
        self.socket_read_size = socket_read_size
        self._sock = None
        self._buffer = None
               
    def shutdown(self):
        '''
        Free all resources with the assumption that
        this instance will never be used again.
        '''
        if self._buffer is not None:
            self._buffer.close()
            self._buffer.join(JOIN_WAIT_TIME)
            if self._buffer.is_alive():
                raise TimeoutError("Could not stop SocketLineReader in PythonParser instance.")
                
    def __del__(self):
        try:
            self.on_disconnect()
        except Exception:
            pass

    def on_connect(self, connection):
        "Called when the socket connects"
        self._sock = connection._sock
        self._buffer = SocketLineReader(self._sock, self.socket_read_size, name=connection.name)
        if connection.decode_responses:
            self.encoding = connection.encoding

    def on_disconnect(self):
        "Called when the socket disconnects"

        if self._buffer is not None:
            self._buffer.close()
            self._buffer = None

        if self._sock is not None:
            self._sock.close()
            self._sock = None
        self.encoding = None


    def can_read(self):
        return self._buffer and not self._buffer.empty()
    
    def read_int(self):
        '''
        Use if an integer or long is expected in response to a
        command. Provides efficiency for when higher layers know
        what to expect back on this connection.
        
        :return: the expected integer or long
        :rtype: long
        :raise InvalidResponse if a non-integer is received from the Redis server. 
        '''
        response = self._buffer.readline().strip()
        if not response:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)
        if not response.startswith(':'):
            raise InvalidResponse("Expecting integer response but received '%s'" % response)
        try:
            return long(response[1:])
        except ValueError:
            raise InvalidResponse("Expecting integer response but received '%s'" % response)

    def read_subscription_cmd_status_return(self, subscription_command, channel):
        '''
        Given a subscription related command, send the command, parse the status response,
        and return the number of channels caller is subscribed to after
        the command was processed on the server.N
        
        :param subscription_command: the command to which a status response is expected:
            {subsribe | unsubscribe | psubscribe | punsubscribe}
        :type subscription_command: string
        :return: the number of channels caller is subscribed to after 
            the subscription command.
        :rtype: int
        :raises ResponseError: if data read from the server is not 
            of proper format.
        :raises TimeoutError: if server does not respond in time.
        '''
        
        return self._buffer.read_subscription_cmd_status_return(subscription_command, channel)
        

    def read_response(self, socket_buffer=None, encoding=None):
        '''
        Reads one line from the wire, and interprets it.
        Example: the acknowledgment to an unsubscribe
        from channel myChannel on the wire looks like this:
        
             *3\r\n$11\r\nUNSUBSCRIBE\r\n$7\r\nmyChannel\r\n:1\r\n'
             
        *3    # three items to follow
        $11   # string of 11 chars
        UNSUBSCRIBE
        $7    # string of 7 chars
        myChannel
        :1    # one channel subscribed to now
        
        Each line will cause a recursive call to this method
        (see elif byte == '*' below).
        
        Simpler calls will be individual elements, such
        as ':12', which returns the integer 12.
        
        These are the possible prefixes; each item
        is followed by a \r\n, which is stripped
        by SocketLineReader:
        
            +<str>    simple string
            :<int>    integer
            $<n>    string of length <n>
            *<num>    start of array with <num> elements


        :return: response string
        :rtype: string
        '''
        
        if socket_buffer is None:
            socket_buffer = self._buffer
        response = socket_buffer.readline()
        
        if not response:
            raise ConnectionError(SERVER_CLOSED_CONNECTION_ERROR)

        return self.parse_response(response, socket_buffer=self._buffer, encoding=encoding)