def process_data(self, data, cmd_line, callback): error, response = None, None data = data[:-2] # strip \r\n if data == '$-1': response = None elif data == '*0' or data == '*-1': response = [] else: head, tail = data[0], data[1:] if head == '*': error, response = yield self.consume_multibulk( int(tail), cmd_line) elif head == '$': error, response = yield self.consume_bulk(int(tail) + 2) elif head == '+': response = tail elif head == ':': response = int(tail) elif head == '-': if tail.startswith('ERR'): tail = tail[4:] error = ResponseError(tail, cmd_line) else: error = ResponseError('Unknown response type %s' % head, cmd_line) callback((error, response))
def process_data(self, data, cmd_line, callback): with execution_context(callback) as ctx: data = data.decode('utf-8') data = data[:-2] # strip \r\n if data == '$-1': response = None elif data == '*0' or data == '*-1': response = [] else: if len(data) == 0: raise IOError('Disconnected') head, tail = data[0], data[1:] if head == '*': response = yield self.consume_multibulk( int(tail), cmd_line) elif head == '$': response = yield self.consume_bulk(int(tail) + 2) elif head == '+': response = tail elif head == ':': response = int(tail) elif head == '-': if tail.startswith('ERR'): tail = tail[4:] response = ResponseError(tail, cmd_line) else: raise ResponseError('Unknown response type %s' % head, cmd_line) ctx.ret_call(response)
def consume_bulk(self, length, callback): data = yield async (self.connection.read)(length) error = None if not data: error = ResponseError('EmptyResponse') else: data = data[:-2] callback((error, data))
def format_reply(self, cmd_line, data): if cmd_line.cmd not in self.REPLY_MAP: return data try: res = self.REPLY_MAP[cmd_line.cmd](data, *cmd_line.args, **cmd_line.kwargs) except Exception, e: res = ResponseError('failed to format reply, raw data: %s' % data, cmd_line)
def consume_bulk(self, length, callback): with execution_context(callback) as ctx: data = yield async (self.connection.read)(length) if isinstance(data, Exception): raise data if not data: raise ResponseError('EmptyResponse') else: data = data[:-2] ctx.ret_call(data)
def format_reply(self, cmd_line, data): if cmd_line.cmd not in self.REPLY_MAP: return data try: res = self.REPLY_MAP[cmd_line.cmd](data, *cmd_line.args, **cmd_line.kwargs) except Exception, e: raise ResponseError( 'failed to format reply to %s, raw data: %s; err message: %s' % (cmd_line, data, e), cmd_line)
def consume_multibulk(self, length, cmd_line, callback): with execution_context(callback) as ctx: tokens = [] while len(tokens) < length: data = yield async (self.connection.readline)() if not data: raise ResponseError( 'Not enough data in response to %s, accumulated tokens: %s' % (cmd_line, tokens), cmd_line) token = yield self.process_data(data, cmd_line) #FIXME error tokens.append(token) ctx.ret_call(tokens)
def execute(self, callbacks): with execution_context(callbacks) as ctx: command_stack = self.command_stack self.command_stack = [] if callbacks is None: callbacks = [] elif not hasattr(callbacks, '__iter__'): callbacks = [callbacks] if self.transactional: command_stack = [CmdLine('MULTI') ] + command_stack + [CmdLine('EXEC')] request = format_pipeline_request(command_stack) try: self.connection.write(request) except IOError: self.command_stack = [] self.connection.disconnect() raise ConnectionError("Socket closed on remote end") except Exception, e: self.command_stack = [] self.connection.disconnect() raise e yield self.connection.queue_wait() responses = [] total = len(command_stack) cmds = iter(command_stack) while len(responses) < total: data = yield async (self.connection.readline)() if not data: raise ResponseError('Not enough data after EXEC') try: cmd_line = cmds.next() if self.transactional and cmd_line.cmd != 'EXEC': response = yield self.process_data( data, CmdLine('MULTI_PART')) else: response = yield self.process_data(data, cmd_line) responses.append(response) except Exception, e: responses.append(e)
def execute(self, callbacks): with execution_context(callbacks) as ctx: command_stack = self.command_stack self.command_stack = [] if callbacks is None: callbacks = [] elif not hasattr(callbacks, '__iter__'): callbacks = [callbacks] if self.transactional: command_stack = [CmdLine('MULTI') ] + command_stack + [CmdLine('EXEC')] request = format_pipeline_request(command_stack) try: self.connection.write(request) except IOError: self.command_stack = [] self.connection.disconnect() raise ConnectionError("Socket closed on remote end") except Exception as e: self.command_stack = [] self.connection.disconnect() raise e yield self.connection.queue_wait() responses = [] total = len(command_stack) cmds = iter(command_stack) while len(responses) < total: data = yield async (self.connection.readline)() if not data: raise ResponseError('Not enough data after EXEC') try: cmd_line = cmds.next() if self.transactional and cmd_line.cmd != 'EXEC': response = yield self.process_data( data, CmdLine('MULTI_PART')) else: response = yield self.process_data(data, cmd_line) responses.append(response) except Exception as e: responses.append(e) self.connection.read_done() def format_replies(cmd_lines, responses): results = [] for cmd_line, response in zip(cmd_lines, responses): try: results.append(self.format_reply(cmd_line, response)) except Exception as e: results.append(e) return results if self.transactional: command_stack = command_stack[:-1] responses = responses[-1] # actual data only from EXEC command #FIXME: assert all other responses to be 'QUEUED' log.info('responses %s', responses) results = format_replies(command_stack[1:], responses) log.info('results %s', results) else: results = format_replies(command_stack, responses) ctx.ret_call(results)