X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/796d05f855f00e0fda05129dd62c39e8670283e7..ca37e9cde814dd093cf2cf5d36f178854d768b3e:/client-libraries/python/redis.py?ds=sidebyside diff --git a/client-libraries/python/redis.py b/client-libraries/python/redis.py index 954c2769..881dfd1d 100644 --- a/client-libraries/python/redis.py +++ b/client-libraries/python/redis.py @@ -32,17 +32,18 @@ class Redis(object): """The main Redis client. """ - def __init__(self, host=None, port=None, timeout=None): + def __init__(self, host=None, port=None, timeout=None, db=None): self.host = host or 'localhost' self.port = port or 6379 if timeout: socket.setdefaulttimeout(timeout) self._sock = None self._fp = None + self.db = db def _write(self, s): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.connect() >>> r._sock.close() >>> try: @@ -76,18 +77,18 @@ class Redis(object): def ping(self): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.ping() 'PONG' >>> """ self.connect() self._write('PING\r\n') - return self._get_simple_response() + return self.get_response() def set(self, name, value, preserve=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.set('a', 'pippo') 'OK' >>> try: @@ -114,12 +115,12 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for key '%s': %s." % (name, e)) - return self._get_numeric_response() if preserve else self._get_simple_response() + return self.get_response() def get(self, name): """ - >>> r = Redis() - >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n') + >>> r = Redis(db=9) + >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '), r.set('d', '\\r\\n') ('OK', 'OK', 'OK', 'OK') >>> r.get('a') 'pippo' @@ -130,19 +131,19 @@ class Redis(object): >>> r.get('b') '15' >>> r.get('c') - '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n' + ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n ' >>> r.get('c') - '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n' + ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n ' >>> r.get('ajhsd') >>> """ self.connect() self._write('GET %s\r\n' % name) - return self._get_value() + return self.get_response() def mget(self, *args): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n') ('OK', 'OK', 'OK', 'OK') >>> r.mget('a', 'b', 'c', 'd') @@ -151,11 +152,11 @@ class Redis(object): """ self.connect() self._write('MGET %s\r\n' % ' '.join(args)) - return self._get_multi_response() + return self.get_response() def incr(self, name, amount=1): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('a') 1 >>> r.incr('a') @@ -171,11 +172,11 @@ class Redis(object): self._write('INCR %s\r\n' % name) else: self._write('INCRBY %s %s\r\n' % (name, amount)) - return self._get_numeric_response() + return self.get_response() def decr(self, name, amount=1): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> if r.get('a'): ... r.delete('a') ... else: @@ -194,11 +195,11 @@ class Redis(object): self._write('DECR %s\r\n' % name) else: self._write('DECRBY %s %s\r\n' % (name, amount)) - return self._get_numeric_response() + return self.get_response() def exists(self, name): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.exists('dsjhfksjdhfkdsjfh') 0 >>> r.set('a', 'a') @@ -209,11 +210,11 @@ class Redis(object): """ self.connect() self._write('EXISTS %s\r\n' % name) - return self._get_numeric_response() + return self.get_response() def delete(self, name): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('dsjhfksjdhfkdsjfh') 0 >>> r.set('a', 'a') @@ -228,19 +229,26 @@ class Redis(object): """ self.connect() self._write('DEL %s\r\n' % name) - return self._get_numeric_response() + return self.get_response() - def key_type(self, name): + def get_type(self, name): """ - Not yet implemented. + >>> r = Redis(db=9) + >>> r.set('a', 3) + 'OK' + >>> r.get_type('a') + 'string' + >>> r.get_type('zzz') + >>> """ self.connect() self._write('TYPE %s\r\n' % name) - return self._get_simple_response() + res = self.get_response() + return None if res == 'none' else res def keys(self, pattern): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.flush() 'OK' >>> r.set('a', 'a') @@ -259,11 +267,11 @@ class Redis(object): """ self.connect() self._write('KEYS %s\r\n' % pattern) - return self._get_value().split() + return self.get_response().split() def randomkey(self): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.set('a', 'a') 'OK' >>> isinstance(r.randomkey(), str) @@ -273,18 +281,16 @@ class Redis(object): #raise NotImplementedError("Implemented but buggy, do not use.") self.connect() self._write('RANDOMKEY\r\n') - data = self._read().strip() - self._check_for_error(data) - return data + return self.get_response() def rename(self, src, dst, preserve=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> try: ... r.rename('a', 'a') ... except ResponseError, e: ... print e - src and dest key are the same + source and destination objects are the same >>> r.rename('a', 'b') 'OK' >>> try: @@ -301,14 +307,29 @@ class Redis(object): self.connect() if preserve: self._write('RENAMENX %s %s\r\n' % (src, dst)) - return self._get_numeric_response() + return self.get_response() else: self._write('RENAME %s %s\r\n' % (src, dst)) - return self._get_simple_response().strip() + return self.get_response() #.strip() + + def expire(self, name, time): + """ + >>> r = Redis(db=9) + >>> r.set('a', 1) + 'OK' + >>> r.expire('a', 1) + 1 + >>> r.expire('zzzzz', 1) + 0 + >>> + """ + self.connect() + self._write('EXPIRE %s %s\r\n' % (name, time)) + return self.get_response() def push(self, name, value, tail=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.push('l', 'a') @@ -331,11 +352,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element in list '%s': %s." % (name, e)) - return self._get_simple_response() + return self.get_response() def llen(self, name): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.push('l', 'a') @@ -350,11 +371,11 @@ class Redis(object): """ self.connect() self._write('LLEN %s\r\n' % name) - return self._get_numeric_response() + return self.get_response() def lrange(self, name, start, end): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.lrange('l', 0, 1) @@ -377,11 +398,11 @@ class Redis(object): """ self.connect() self._write('LRANGE %s %s %s\r\n' % (name, start, end)) - return self._get_multi_response() + return self.get_response() def ltrim(self, name, start, end): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> try: @@ -407,11 +428,11 @@ class Redis(object): """ self.connect() self._write('LTRIM %s %s %s\r\n' % (name, start, end)) - return self._get_simple_response() + return self.get_response() def lindex(self, name, index): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> res = r.delete('l') >>> r.lindex('l', 0) >>> r.push('l', 'aaa') @@ -429,11 +450,11 @@ class Redis(object): """ self.connect() self._write('LINDEX %s %s\r\n' % (name, index)) - return self._get_value() + return self.get_response() def pop(self, name, tail=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.pop('l') @@ -459,11 +480,11 @@ class Redis(object): """ self.connect() self._write('%s %s\r\n' % ('RPOP' if tail else 'LPOP', name)) - return self._get_value() + return self.get_response() def lset(self, name, index, value): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> try: @@ -492,11 +513,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index, name, e)) - return self._get_simple_response() + return self.get_response() def lrem(self, name, value, num=0): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.push('l', 'aaa') @@ -529,11 +550,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index, name, e)) - return self._get_numeric_response() + return self.get_response() def sort(self, name, by=None, get=None, start=None, num=None, desc=False, alpha=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('l') 1 >>> r.push('l', 'ccc') @@ -590,11 +611,11 @@ class Redis(object): stmt.append("ALPHA") self.connect() self._write(' '.join(stmt + ["\r\n"])) - return self._get_multi_response() + return self.get_response() def sadd(self, name, value): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> res = r.delete('s') >>> r.sadd('s', 'a') 1 @@ -611,11 +632,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name, e)) - return self._get_numeric_response() + return self.get_response() def srem(self, name, value): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('s') 1 >>> r.srem('s', 'aaa') @@ -637,11 +658,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name, e)) - return self._get_numeric_response() + return self.get_response() def sismember(self, name, value): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('s') 1 >>> r.sismember('s', 'b') @@ -663,11 +684,11 @@ class Redis(object): )) except UnicodeEncodeError, e: raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name, e)) - return self._get_numeric_response() + return self.get_response() def sinter(self, *args): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> res = r.delete('s1') >>> res = r.delete('s2') >>> res = r.delete('s3') @@ -695,11 +716,11 @@ class Redis(object): """ self.connect() self._write('SINTER %s\r\n' % ' '.join(args)) - return set(self._get_multi_response()) + return set(self.get_response()) def sinterstore(self, dest, *args): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> res = r.delete('s1') >>> res = r.delete('s2') >>> res = r.delete('s3') @@ -719,11 +740,11 @@ class Redis(object): """ self.connect() self._write('SINTERSTORE %s %s\r\n' % (dest, ' '.join(args))) - return self._get_simple_response() + return self.get_response() def smembers(self, name): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('s') 1 >>> r.sadd('s', 'a') @@ -741,60 +762,58 @@ class Redis(object): """ self.connect() self._write('SMEMBERS %s\r\n' % name) - return set(self._get_multi_response()) + return set(self.get_response()) def select(self, db): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.delete('a') 1 - >>> r.select(1) + >>> r.select(10) 'OK' >>> r.set('a', 1) 'OK' - >>> r.select(0) + >>> r.select(9) 'OK' >>> r.get('a') >>> """ self.connect() self._write('SELECT %s\r\n' % db) - return self._get_simple_response() + return self.get_response() def move(self, name, db): """ - >>> r = Redis() - >>> r.select(0) - 'OK' + >>> r = Redis(db=9) >>> r.set('a', 'a') 'OK' - >>> r.select(1) + >>> r.select(10) 'OK' >>> if r.get('a'): ... r.delete('a') ... else: ... print 1 1 - >>> r.select(0) + >>> r.select(9) 'OK' - >>> r.move('a', 1) + >>> r.move('a', 10) 1 >>> r.get('a') - >>> r.select(1) + >>> r.select(10) 'OK' >>> r.get('a') 'a' - >>> r.select(0) + >>> r.select(9) 'OK' >>> """ self.connect() self._write('MOVE %s %s\r\n' % (name, db)) - return self._get_numeric_response() + return self.get_response() def save(self, background=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.save() 'OK' >>> try: @@ -810,12 +829,12 @@ class Redis(object): self._write('BGSAVE\r\n') else: self._write('SAVE\r\n') - return self._get_simple_response() + return self.get_response() def lastsave(self): """ >>> import time - >>> r = Redis() + >>> r = Redis(db=9) >>> t = int(time.time()) >>> r.save() 'OK' @@ -825,24 +844,23 @@ class Redis(object): """ self.connect() self._write('LASTSAVE\r\n') - return self._get_numeric_response() + return self.get_response() def flush(self, all_dbs=False): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.flush() 'OK' - >>> r.flush(all_dbs=True) - 'OK' + >>> # r.flush(all_dbs=True) >>> """ self.connect() self._write('%s\r\n' % ('FLUSHALL' if all_dbs else 'FLUSHDB')) - return self._get_simple_response() + return self.get_response() def info(self): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> info = r.info() >>> info and isinstance(info, dict) True @@ -853,81 +871,52 @@ class Redis(object): self.connect() self._write('INFO\r\n') info = dict() - for l in self._get_value().split('\r\n'): + for l in self.get_response().split('\r\n'): if not l: continue k, v = l.split(':', 1) info[k] = int(v) if v.isdigit() else v return info - def _get_value(self, negative_as_nil=False): + def get_response(self): data = self._read().strip() - if data == 'nil' or (negative_as_nil and data == '-1'): - return - elif data[0] == '-': - self._check_for_error(data) + c = data[0] + if c == '-': + raise ResponseError(data[5:] if data[:5] == '-ERR ' else data[1:]) + if c == '+': + return data[1:] + if c == '*': + try: + num = int(data[1:]) + except (TypeError, ValueError): + raise InvalidResponse("Cannot convert multi-response header '%s' to integer" % data) + result = list() + for i in range(num): + result.append(self._get_value()) + return result + return self._get_value(data) + + def _get_value(self, data=None): + data = data or self._read().strip() + if data == '$-1': + return None try: - l = int(data) - except (TypeError, ValueError): - raise ResponseError("Cannot parse response '%s' as data length." % data) + c, i = data[0], (int(data[1:]) if data.find('.') == -1 else float(data[1:])) + except ValueError: + raise InvalidResponse("Cannot convert data '%s' to integer" % data) + if c == ':': + return i + if c != '$': + raise InvalidResponse("Unkown response prefix for '%s'" % data) buf = [] - while l > 0: + while True: data = self._read() - l -= len(data) - if len(data) > l: - # we got the ending crlf - data = data.rstrip() + i -= len(data) buf.append(data) - if l == 0: - # the data has a trailing crlf embedded, let's restore it - buf.append(self._read()) - return ''.join(buf) + if i < 0: + break + return ''.join(buf)[:-2] - def _get_simple_response(self): - data = self._read().strip() - if data[0] == '+': - return data[1:] - self._check_for_error(data) - raise InvalidResponse("Cannot parse first line '%s' for a simple response." % data, data) - - def _get_numeric_response(self, allow_negative=True): - data = self._read().strip() - try: - value = int(data) - except (TypeError, ValueError), e: - pass - else: - if not allow_negative and value < 0: - self._check_for_error(data) - return value - self._check_for_error(data) - raise InvalidResponse("Cannot parse first line '%s' for a numeric response: %s." % (data, e), data) - - def _get_multi_response(self): - results = list() - try: - num = self._get_numeric_response(allow_negative=False) - except InvalidResponse, e: - if e.args[1] == 'nil': - return results - raise - while num: - results.append(self._get_value(negative_as_nil=True)) - num -= 1 - return results - - def _check_for_error(self, data): - if not data or data[0] != '-': - return - if data.startswith('-ERR'): - raise ResponseError(data[4:].strip()) - try: - error_len = int(data[1:]) - except (TypeError, ValueError): - raise ResponseError("Unknown error format '%s'." % data) - error_message = self._read().strip()[5:] - raise ResponseError(error_message) - def disconnect(self): if isinstance(self._sock, socket.socket): try: @@ -939,10 +928,11 @@ class Redis(object): def connect(self): """ - >>> r = Redis() + >>> r = Redis(db=9) >>> r.connect() >>> isinstance(r._sock, socket.socket) True + >>> r.disconnect() >>> """ if isinstance(self._sock, socket.socket): @@ -955,9 +945,11 @@ class Redis(object): else: self._sock = sock self._fp = self._sock.makefile('r') - + if self.db: + self.select(self.db) + if __name__ == '__main__': import doctest doctest.testmod() - \ No newline at end of file +