"""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:
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:
))
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'
>>> 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')
"""
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')
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:
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')
"""
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')
"""
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')
"""
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)
#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:
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')
))
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')
"""
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)
"""
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:
"""
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')
"""
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')
"""
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:
))
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')
))
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')
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
))
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')
))
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')
))
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')
"""
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')
"""
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')
"""
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:
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'
"""
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
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:
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):
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
+