]>
git.saurik.com Git - redis.git/blob - client-libraries/python/redis.py
3 """ redis.py - A client for the Redis daemon.
7 __author__
= "Ludovico Magnocavallo <ludo\x40qix\x2eit>"
8 __copyright__
= "Copyright 2009, Ludovico Magnocavallo"
11 __revision__
= "$LastChangedRevision: 175 $"[22:-2]
12 __date__
= "$LastChangedDate: 2009-03-17 16:15:55 +0100 (Mar, 17 Mar 2009) $"[18:-2]
15 # TODO: Redis._get_multi_response
25 class RedisError(Exception): pass
26 class ConnectionError(RedisError
): pass
27 class ResponseError(RedisError
): pass
28 class InvalidResponse(RedisError
): pass
29 class InvalidData(RedisError
): pass
33 """The main Redis client.
36 def __init__(self
, host
=None, port
=None, timeout
=None, db
=None, nodelay
=None, charset
='utf8', errors
='strict'):
37 self
.host
= host
or 'localhost'
38 self
.port
= port
or 6379
40 socket
.setdefaulttimeout(timeout
)
41 self
.nodelay
= nodelay
42 self
.charset
= charset
49 if isinstance(s
, str):
51 if isinstance(s
, unicode):
53 return s
.encode(self
.charset
, self
.errors
)
54 except UnicodeEncodeError, e
:
55 raise InvalidData("Error encoding unicode value '%s': %s" % (value
.encode(self
.charset
, 'replace'), e
))
65 ... except ConnectionError, e:
67 Error 9 while writing to socket. Bad file descriptor.
73 except socket
.error
, e
:
77 raise ConnectionError("Error %s while writing to socket. %s." % tuple(e
.args
))
81 return self
._fp
.readline()
82 except socket
.error
, e
:
83 if e
.args
and e
.args
[0] == errno
.EAGAIN
:
86 raise ConnectionError("Error %s while reading from socket. %s." % tuple(e
.args
))
89 raise ConnectionError("Socket connection closed when reading.")
100 self
._write
('PING\r\n')
101 return self
.get_response()
103 def set(self
, name
, value
, preserve
=False, getset
=False):
106 >>> r.set('a', 'pippo')
108 >>> r.set('a', u'pippo \u3235')
112 >>> r.set('b', 105.2)
114 >>> r.set('b', 'xxx', preserve=True)
121 # the following will raise an error for unicode values that can't be encoded to ascii
122 # we could probably add an 'encoding' arg to init, but then what do we do with get()?
123 # convert back to unicode? and what about ints, or pickled values?
124 if getset
: command
= 'GETSET'
125 elif preserve
: command
= 'SETNX'
126 else: command
= 'SET'
127 value
= self
._encode
(value
)
128 self
._write
('%s %s %s\r\n%s\r\n' % (
129 command
, name
, len(value
), value
131 return self
.get_response()
136 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '), r.set('d', '\\r\\n')
137 ('OK', 'OK', 'OK', 'OK')
147 u' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
149 u' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
154 self
._write
('GET %s\r\n' % name
)
155 return self
.get_response()
157 def getset(self
, name
, value
):
160 >>> r.set('a', 'pippo')
166 return self
.set(name
, value
, getset
=True)
168 def mget(self
, *args
):
171 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n')
172 ('OK', 'OK', 'OK', 'OK')
173 >>> r.mget('a', 'b', 'c', 'd')
174 [u'pippo', 15, u'\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n', u'\\r\\n']
178 self
._write
('MGET %s\r\n' % ' '.join(args
))
179 return self
.get_response()
181 def incr(self
, name
, amount
=1):
196 self
._write
('INCR %s\r\n' % name
)
198 self
._write
('INCRBY %s %s\r\n' % (name
, amount
))
199 return self
.get_response()
201 def decr(self
, name
, amount
=1):
219 self
._write
('DECR %s\r\n' % name
)
221 self
._write
('DECRBY %s %s\r\n' % (name
, amount
))
222 return self
.get_response()
224 def exists(self
, name
):
227 >>> r.exists('dsjhfksjdhfkdsjfh')
236 self
._write
('EXISTS %s\r\n' % name
)
237 return self
.get_response()
239 def delete(self
, name
):
242 >>> r.delete('dsjhfksjdhfkdsjfh')
255 self
._write
('DEL %s\r\n' % name
)
256 return self
.get_response()
258 def get_type(self
, name
):
265 >>> r.get_type('zzz')
269 self
._write
('TYPE %s\r\n' % name
)
270 res
= self
.get_response()
271 return None if res
== 'none' else res
273 def keys(self
, pattern
):
288 >>> r.keys('sjdfhskjh*')
293 self
._write
('KEYS %s\r\n' % pattern
)
294 return self
.get_response().split()
301 >>> isinstance(r.randomkey(), str)
305 #raise NotImplementedError("Implemented but buggy, do not use.")
307 self
._write
('RANDOMKEY\r\n')
308 return self
.get_response()
310 def rename(self
, src
, dst
, preserve
=False):
314 ... r.rename('a', 'a')
315 ... except ResponseError, e:
317 source and destination objects are the same
318 >>> r.rename('a', 'b')
321 ... r.rename('a', 'b')
322 ... except ResponseError, e:
327 >>> r.rename('b', 'a', preserve=True)
333 self
._write
('RENAMENX %s %s\r\n' % (src
, dst
))
334 return self
.get_response()
336 self
._write
('RENAME %s %s\r\n' % (src
, dst
))
337 return self
.get_response() #.strip()
347 self
._write
('DBSIZE\r\n')
348 return self
.get_response()
355 >>> r.expire('a', 10)
364 self
._write
('TTL %s\r\n' % name
)
365 return self
.get_response()
367 def expire(self
, name
, time
):
374 >>> r.expire('zzzzz', 1)
379 self
._write
('EXPIRE %s %s\r\n' % (name
, time
))
380 return self
.get_response()
382 def push(self
, name
, value
, tail
=False):
393 ... except ResponseError, e:
395 Operation against a key holding the wrong kind of value
399 value
= self
._encode
(value
)
400 self
._write
('%s %s %s\r\n%s\r\n' % (
401 'LPUSH' if tail
else 'RPUSH', name
, len(value
), value
403 return self
.get_response()
405 def llen(self
, name
):
421 self
._write
('LLEN %s\r\n' % name
)
422 return self
.get_response()
424 def lrange(self
, name
, start
, end
):
429 >>> r.lrange('l', 0, 1)
431 >>> r.push('l', 'aaa')
433 >>> r.lrange('l', 0, 1)
435 >>> r.push('l', 'bbb')
437 >>> r.lrange('l', 0, 0)
439 >>> r.lrange('l', 0, 1)
441 >>> r.lrange('l', -1, 0)
443 >>> r.lrange('l', -1, -1)
448 self
._write
('LRANGE %s %s %s\r\n' % (name
, start
, end
))
449 return self
.get_response()
451 def ltrim(self
, name
, start
, end
):
457 ... r.ltrim('l', 0, 1)
458 ... except ResponseError, e:
461 >>> r.push('l', 'aaa')
463 >>> r.push('l', 'bbb')
465 >>> r.push('l', 'ccc')
467 >>> r.ltrim('l', 0, 1)
471 >>> r.ltrim('l', 99, 95)
478 self
._write
('LTRIM %s %s %s\r\n' % (name
, start
, end
))
479 return self
.get_response()
481 def lindex(self
, name
, index
):
484 >>> res = r.delete('l')
486 >>> r.push('l', 'aaa')
491 >>> r.push('l', 'ccc')
495 >>> r.lindex('l', -1)
500 self
._write
('LINDEX %s %s\r\n' % (name
, index
))
501 return self
.get_response()
503 def pop(self
, name
, tail
=False):
509 >>> r.push('l', 'aaa')
511 >>> r.push('l', 'bbb')
518 >>> r.push('l', 'aaa')
520 >>> r.push('l', 'bbb')
522 >>> r.pop('l', tail=True)
530 self
._write
('%s %s\r\n' % ('RPOP' if tail
else 'LPOP', name
))
531 return self
.get_response()
533 def lset(self
, name
, index
, value
):
539 ... r.lset('l', 0, 'a')
540 ... except ResponseError, e:
543 >>> r.push('l', 'aaa')
546 ... r.lset('l', 1, 'a')
547 ... except ResponseError, e:
550 >>> r.lset('l', 0, 'bbb')
552 >>> r.lrange('l', 0, 1)
557 value
= self
._encode
(value
)
558 self
._write
('LSET %s %s %s\r\n%s\r\n' % (
559 name
, index
, len(value
), value
561 return self
.get_response()
563 def lrem(self
, name
, value
, num
=0):
568 >>> r.push('l', 'aaa')
570 >>> r.push('l', 'bbb')
572 >>> r.push('l', 'aaa')
574 >>> r.lrem('l', 'aaa')
576 >>> r.lrange('l', 0, 10)
578 >>> r.push('l', 'aaa')
580 >>> r.push('l', 'aaa')
582 >>> r.lrem('l', 'aaa', 1)
584 >>> r.lrem('l', 'aaa', 1)
586 >>> r.lrem('l', 'aaa', 1)
591 value
= self
._encode
(value
)
592 self
._write
('LREM %s %s %s\r\n%s\r\n' % (
593 name
, num
, len(value
), value
595 return self
.get_response()
597 def sort(self
, name
, by
=None, get
=None, start
=None, num
=None, desc
=False, alpha
=False):
602 >>> r.push('l', 'ccc')
604 >>> r.push('l', 'aaa')
606 >>> r.push('l', 'ddd')
608 >>> r.push('l', 'bbb')
610 >>> r.sort('l', alpha=True)
611 [u'aaa', u'bbb', u'ccc', u'ddd']
614 >>> for i in range(1, 5):
615 ... res = r.push('l', 1.0 / i)
617 [Decimal("0.25"), Decimal("0.333333333333"), Decimal("0.5"), Decimal("1.0")]
618 >>> r.sort('l', desc=True)
619 [Decimal("1.0"), Decimal("0.5"), Decimal("0.333333333333"), Decimal("0.25")]
620 >>> r.sort('l', desc=True, start=2, num=1)
621 [Decimal("0.333333333333")]
622 >>> r.set('weight_0.5', 10)
624 >>> r.sort('l', desc=True, by='weight_*')
625 [Decimal("0.5"), Decimal("1.0"), Decimal("0.333333333333"), Decimal("0.25")]
626 >>> for i in r.sort('l', desc=True):
627 ... res = r.set('test_%s' % i, 100 - float(i))
628 >>> r.sort('l', desc=True, get='test_*')
629 [Decimal("99.0"), Decimal("99.5"), Decimal("99.6666666667"), Decimal("99.75")]
630 >>> r.sort('l', desc=True, by='weight_*', get='test_*')
631 [Decimal("99.5"), Decimal("99.0"), Decimal("99.6666666667"), Decimal("99.75")]
632 >>> r.sort('l', desc=True, by='weight_*', get='missing_*')
633 [None, None, None, None]
636 stmt
= ['SORT', name
]
638 stmt
.append("BY %s" % by
)
640 stmt
.append("LIMIT %s %s" % (start
, num
))
643 elif isinstance(get
, basestring
):
644 stmt
.append("GET %s" % get
)
645 elif isinstance(get
, list) or isinstance(get
, tuple):
647 stmt
.append("GET %s" % g
)
649 raise RedisError("Invalid parameter 'get' for Redis sort")
655 self
._write
(' '.join(stmt
+ ["\r\n"]))
656 return self
.get_response()
658 def sadd(self
, name
, value
):
661 >>> res = r.delete('s')
669 value
= self
._encode
(value
)
670 self
._write
('SADD %s %s\r\n%s\r\n' % (
671 name
, len(value
), value
673 return self
.get_response()
675 def srem(self
, name
, value
):
680 >>> r.srem('s', 'aaa')
686 >>> r.sismember('s', 'b')
691 value
= self
._encode
(value
)
692 self
._write
('SREM %s %s\r\n%s\r\n' % (
693 name
, len(value
), value
695 return self
.get_response()
697 def sismember(self
, name
, value
):
702 >>> r.sismember('s', 'b')
706 >>> r.sismember('s', 'b')
708 >>> r.sismember('s', 'a')
713 value
= self
._encode
(value
)
714 self
._write
('SISMEMBER %s %s\r\n%s\r\n' % (
715 name
, len(value
), value
717 return self
.get_response()
719 def sinter(self
, *args
):
722 >>> res = r.delete('s1')
723 >>> res = r.delete('s2')
724 >>> res = r.delete('s3')
725 >>> r.sadd('s1', 'a')
727 >>> r.sadd('s2', 'a')
729 >>> r.sadd('s3', 'b')
733 ... except ResponseError, e:
735 wrong number of arguments
738 ... except ResponseError, e:
740 Operation against a key holding the wrong kind of value
741 >>> r.sinter('s1', 's2', 's3')
743 >>> r.sinter('s1', 's2')
748 self
._write
('SINTER %s\r\n' % ' '.join(args
))
749 return set(self
.get_response())
751 def sinterstore(self
, dest
, *args
):
754 >>> res = r.delete('s1')
755 >>> res = r.delete('s2')
756 >>> res = r.delete('s3')
757 >>> r.sadd('s1', 'a')
759 >>> r.sadd('s2', 'a')
761 >>> r.sadd('s3', 'b')
763 >>> r.sinterstore('s_s', 's1', 's2', 's3')
765 >>> r.sinterstore('s_s', 's1', 's2')
767 >>> r.smembers('s_s')
772 self
._write
('SINTERSTORE %s %s\r\n' % (dest
, ' '.join(args
)))
773 return self
.get_response()
775 def smembers(self
, name
):
786 ... except ResponseError, e:
788 Operation against a key holding the wrong kind of value
794 self
._write
('SMEMBERS %s\r\n' % name
)
795 return set(self
.get_response())
797 def select(self
, db
):
812 self
._write
('SELECT %s\r\n' % db
)
813 return self
.get_response()
815 def move(self
, name
, db
):
841 self
._write
('MOVE %s %s\r\n' % (name
, db
))
842 return self
.get_response()
844 def save(self
, background
=False):
850 ... resp = r.save(background=True)
851 ... except ResponseError, e:
852 ... assert str(e) == 'background save already in progress', str(e)
854 ... assert resp == 'OK'
859 self
._write
('BGSAVE\r\n')
861 self
._write
('SAVE\r\n')
862 return self
.get_response()
868 >>> t = int(time.time())
871 >>> r.lastsave() >= t
876 self
._write
('LASTSAVE\r\n')
877 return self
.get_response()
879 def flush(self
, all_dbs
=False):
884 >>> # r.flush(all_dbs=True)
888 self
._write
('%s\r\n' % ('FLUSHALL' if all_dbs
else 'FLUSHDB'))
889 return self
.get_response()
895 >>> info and isinstance(info, dict)
897 >>> isinstance(info.get('connected_clients'), int)
902 self
._write
('INFO\r\n')
904 for l
in self
.get_response().split('\r\n'):
907 k
, v
= l
.split(':', 1)
908 info
[k
] = int(v
) if v
.isdigit() else v
911 def auth(self
, passwd
):
913 self
._write
('AUTH %s\r\n' % passwd
)
914 return self
.get_response()
916 def get_response(self
):
917 data
= self
._read
().strip()
920 raise ConnectionError("Socket closed on remote end")
923 raise ResponseError(data
[5:] if data
[:5] == '-ERR ' else data
[1:])
929 except (TypeError, ValueError):
930 raise InvalidResponse("Cannot convert multi-response header '%s' to integer" % data
)
933 result
.append(self
._get
_value
())
935 return self
._get
_value
(data
)
937 def _get_value(self
, data
=None):
938 data
= data
or self
._read
().strip()
942 c
, i
= data
[0], (int(data
[1:]) if data
.find('.') == -1 else float(data
[1:]))
944 raise InvalidResponse("Cannot convert data '%s' to integer" % data
)
948 raise InvalidResponse("Unkown response prefix for '%s'" % data
)
956 data
= ''.join(buf
)[:-2]
958 return int(data
) if data
.find('.') == -1 else decimal
.Decimal(data
)
959 except (ValueError, decimal
.InvalidOperation
):
960 return data
.decode(self
.charset
)
962 def disconnect(self
):
963 if isinstance(self
._sock
, socket
.socket
):
975 >>> isinstance(r._sock, socket.socket)
980 if isinstance(self
._sock
, socket
.socket
):
983 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
984 sock
.connect((self
.host
, self
.port
))
985 except socket
.error
, e
:
986 raise ConnectionError("Error %s connecting to %s:%s. %s." % (e
.args
[0], self
.host
, self
.port
, e
.args
[1]))
989 self
._fp
= self
._sock
.makefile('r')
992 if self
.nodelay
is not None:
993 self
._sock
.setsockopt(socket
.SOL_TCP
, socket
.TCP_NODELAY
, self
.nodelay
)
996 if __name__
== '__main__':