]>
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
24 class RedisError(Exception): pass
25 class ConnectionError(RedisError
): pass
26 class ResponseError(RedisError
): pass
27 class InvalidResponse(RedisError
): pass
28 class InvalidData(RedisError
): pass
32 """The main Redis client.
35 def __init__(self
, host
=None, port
=None, timeout
=None):
36 self
.host
= host
or 'localhost'
37 self
.port
= port
or 6379
39 socket
.setdefaulttimeout(timeout
)
50 ... except ConnectionError, e:
52 Error 9 while writing to socket. Bad file descriptor.
58 except socket
.error
, e
:
62 raise ConnectionError("Error %s while writing to socket. %s." % tuple(e
.args
))
66 return self
._fp
.readline()
67 except socket
.error
, e
:
68 if e
.args
and e
.args
[0] == errno
.EAGAIN
:
71 raise ConnectionError("Error %s while reading from socket. %s." % tuple(e
.args
))
74 raise ConnectionError("Socket connection closed when reading.")
85 self
._write
('PING\r\n')
86 return self
._get
_simple
_response
()
88 def set(self
, name
, value
, preserve
=False):
91 >>> r.set('a', 'pippo')
94 ... r.set('a', u'pippo \u3235')
95 ... except InvalidData, e:
97 Error encoding unicode value for key 'a': 'ascii' codec can't encode character u'\u3235' in position 15: ordinal not in range(128).
100 >>> r.set('b', 'xxx', preserve=True)
107 # the following will raise an error for unicode values that can't be encoded to ascii
108 # we could probably add an 'encoding' arg to init, but then what do we do with get()?
109 # convert back to unicode? and what about ints, or pickled values?
111 value
= value
if isinstance(value
, basestring
) else str(value
)
112 self
._write
('%s %s %s\r\n%s\r\n' % (
113 'SETNX' if preserve
else 'SET', name
, len(value
), value
115 except UnicodeEncodeError, e
:
116 raise InvalidData("Error encoding unicode value for key '%s': %s." % (name
, e
))
117 return self
._get
_numeric
_response
() if preserve
else self
._get
_simple
_response
()
122 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n')
123 ('OK', 'OK', 'OK', 'OK')
133 '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'
135 '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'
140 self
._write
('GET %s\r\n' % name
)
141 return self
._get
_value
()
143 def mget(self
, *args
):
146 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n')
147 ('OK', 'OK', 'OK', 'OK')
148 >>> r.mget('a', 'b', 'c', 'd')
149 ['pippo', '15', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n', '\\r\\n']
153 self
._write
('MGET %s\r\n' % ' '.join(args
))
154 return self
._get
_multi
_response
()
156 def incr(self
, name
, amount
=1):
171 self
._write
('INCR %s\r\n' % name
)
173 self
._write
('INCRBY %s %s\r\n' % (name
, amount
))
174 return self
._get
_numeric
_response
()
176 def decr(self
, name
, amount
=1):
194 self
._write
('DECR %s\r\n' % name
)
196 self
._write
('DECRBY %s %s\r\n' % (name
, amount
))
197 return self
._get
_numeric
_response
()
199 def exists(self
, name
):
202 >>> r.exists('dsjhfksjdhfkdsjfh')
211 self
._write
('EXISTS %s\r\n' % name
)
212 return self
._get
_numeric
_response
()
214 def delete(self
, name
):
217 >>> r.delete('dsjhfksjdhfkdsjfh')
230 self
._write
('DEL %s\r\n' % name
)
231 return self
._get
_numeric
_response
()
233 def key_type(self
, name
):
238 self
._write
('TYPE %s\r\n' % name
)
239 return self
._get
_simple
_response
()
241 def keys(self
, pattern
):
256 >>> r.keys('sjdfhskjh*')
261 self
._write
('KEYS %s\r\n' % pattern
)
262 return self
._get
_value
().split()
269 >>> isinstance(r.randomkey(), str)
273 #raise NotImplementedError("Implemented but buggy, do not use.")
275 self
._write
('RANDOMKEY\r\n')
276 data
= self
._read
().strip()
277 self
._check
_for
_error
(data
)
280 def rename(self
, src
, dst
, preserve
=False):
284 ... r.rename('a', 'a')
285 ... except ResponseError, e:
287 src and dest key are the same
288 >>> r.rename('a', 'b')
291 ... r.rename('a', 'b')
292 ... except ResponseError, e:
297 >>> r.rename('b', 'a', preserve=True)
303 self
._write
('RENAMENX %s %s\r\n' % (src
, dst
))
304 return self
._get
_numeric
_response
()
306 self
._write
('RENAME %s %s\r\n' % (src
, dst
))
307 return self
._get
_simple
_response
().strip()
309 def push(self
, name
, value
, tail
=False):
320 ... except ResponseError, e:
322 Operation against a key holding the wrong kind of value
326 # same considerations on unicode as in set() apply here
328 value
= value
if isinstance(value
, basestring
) else str(value
)
329 self
._write
('%s %s %s\r\n%s\r\n' % (
330 'LPUSH' if tail
else 'RPUSH', name
, len(value
), value
332 except UnicodeEncodeError, e
:
333 raise InvalidData("Error encoding unicode value for element in list '%s': %s." % (name
, e
))
334 return self
._get
_simple
_response
()
336 def llen(self
, name
):
352 self
._write
('LLEN %s\r\n' % name
)
353 return self
._get
_numeric
_response
()
355 def lrange(self
, name
, start
, end
):
360 >>> r.lrange('l', 0, 1)
362 >>> r.push('l', 'aaa')
364 >>> r.lrange('l', 0, 1)
366 >>> r.push('l', 'bbb')
368 >>> r.lrange('l', 0, 0)
370 >>> r.lrange('l', 0, 1)
372 >>> r.lrange('l', -1, 0)
374 >>> r.lrange('l', -1, -1)
379 self
._write
('LRANGE %s %s %s\r\n' % (name
, start
, end
))
380 return self
._get
_multi
_response
()
382 def ltrim(self
, name
, start
, end
):
388 ... r.ltrim('l', 0, 1)
389 ... except ResponseError, e:
392 >>> r.push('l', 'aaa')
394 >>> r.push('l', 'bbb')
396 >>> r.push('l', 'ccc')
398 >>> r.ltrim('l', 0, 1)
402 >>> r.ltrim('l', 99, 95)
409 self
._write
('LTRIM %s %s %s\r\n' % (name
, start
, end
))
410 return self
._get
_simple
_response
()
412 def lindex(self
, name
, index
):
415 >>> res = r.delete('l')
417 >>> r.push('l', 'aaa')
422 >>> r.push('l', 'ccc')
426 >>> r.lindex('l', -1)
431 self
._write
('LINDEX %s %s\r\n' % (name
, index
))
432 return self
._get
_value
()
434 def pop(self
, name
, tail
=False):
440 >>> r.push('l', 'aaa')
442 >>> r.push('l', 'bbb')
449 >>> r.push('l', 'aaa')
451 >>> r.push('l', 'bbb')
453 >>> r.pop('l', tail=True)
461 self
._write
('%s %s\r\n' % ('RPOP' if tail
else 'LPOP', name
))
462 return self
._get
_value
()
464 def lset(self
, name
, index
, value
):
470 ... r.lset('l', 0, 'a')
471 ... except ResponseError, e:
474 >>> r.push('l', 'aaa')
477 ... r.lset('l', 1, 'a')
478 ... except ResponseError, e:
481 >>> r.lset('l', 0, 'bbb')
483 >>> r.lrange('l', 0, 1)
489 value
= value
if isinstance(value
, basestring
) else str(value
)
490 self
._write
('LSET %s %s %s\r\n%s\r\n' % (
491 name
, index
, len(value
), value
493 except UnicodeEncodeError, e
:
494 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
495 return self
._get
_simple
_response
()
497 def lrem(self
, name
, value
, num
=0):
502 >>> r.push('l', 'aaa')
504 >>> r.push('l', 'bbb')
506 >>> r.push('l', 'aaa')
508 >>> r.lrem('l', 'aaa')
510 >>> r.lrange('l', 0, 10)
512 >>> r.push('l', 'aaa')
514 >>> r.push('l', 'aaa')
516 >>> r.lrem('l', 'aaa', 1)
518 >>> r.lrem('l', 'aaa', 1)
520 >>> r.lrem('l', 'aaa', 1)
526 value
= value
if isinstance(value
, basestring
) else str(value
)
527 self
._write
('LREM %s %s %s\r\n%s\r\n' % (
528 name
, num
, len(value
), value
530 except UnicodeEncodeError, e
:
531 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
532 return self
._get
_numeric
_response
()
534 def sort(self
, name
, by
=None, get
=None, start
=None, num
=None, desc
=False, alpha
=False):
539 >>> r.push('l', 'ccc')
541 >>> r.push('l', 'aaa')
543 >>> r.push('l', 'ddd')
545 >>> r.push('l', 'bbb')
547 >>> r.sort('l', alpha=True)
548 ['aaa', 'bbb', 'ccc', 'ddd']
551 >>> for i in range(1, 5):
552 ... res = r.push('l', 1.0 / i)
554 ['0.25', '0.333333333333', '0.5', '1.0']
555 >>> r.sort('l', desc=True)
556 ['1.0', '0.5', '0.333333333333', '0.25']
557 >>> r.sort('l', desc=True, start=2, num=1)
559 >>> r.set('weight_0.5', 10)
561 >>> r.sort('l', desc=True, by='weight_*')
562 ['0.5', '1.0', '0.333333333333', '0.25']
563 >>> for i in r.sort('l', desc=True):
564 ... res = r.set('test_%s' % i, 100 - float(i))
565 >>> r.sort('l', desc=True, get='test_*')
566 ['99.0', '99.5', '99.6666666667', '99.75']
567 >>> r.sort('l', desc=True, by='weight_*', get='test_*')
568 ['99.5', '99.0', '99.6666666667', '99.75']
569 >>> r.sort('l', desc=True, by='weight_*', get='missing_*')
570 [None, None, None, None]
573 stmt
= ['SORT', name
]
575 stmt
.append("BY %s" % by
)
577 stmt
.append("LIMIT %s %s" % (start
, num
))
580 elif isinstance(get
, basestring
):
581 stmt
.append("GET %s" % get
)
582 elif isinstance(get
, list) or isinstance(get
, tuple):
584 stmt
.append("GET %s" % g
)
586 raise RedisError("Invalid parameter 'get' for Redis sort")
592 self
._write
(' '.join(stmt
+ ["\r\n"]))
593 return self
._get
_multi
_response
()
595 def sadd(self
, name
, value
):
598 >>> res = r.delete('s')
606 # same considerations on unicode as in set() apply here
608 value
= value
if isinstance(value
, basestring
) else str(value
)
609 self
._write
('SADD %s %s\r\n%s\r\n' % (
610 name
, len(value
), value
612 except UnicodeEncodeError, e
:
613 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
614 return self
._get
_numeric
_response
()
616 def srem(self
, name
, value
):
621 >>> r.srem('s', 'aaa')
627 >>> r.sismember('s', 'b')
632 # same considerations on unicode as in set() apply here
634 value
= value
if isinstance(value
, basestring
) else str(value
)
635 self
._write
('SREM %s %s\r\n%s\r\n' % (
636 name
, len(value
), value
638 except UnicodeEncodeError, e
:
639 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
640 return self
._get
_numeric
_response
()
642 def sismember(self
, name
, value
):
647 >>> r.sismember('s', 'b')
651 >>> r.sismember('s', 'b')
653 >>> r.sismember('s', 'a')
658 # same considerations on unicode as in set() apply here
660 value
= value
if isinstance(value
, basestring
) else str(value
)
661 self
._write
('SISMEMBER %s %s\r\n%s\r\n' % (
662 name
, len(value
), value
664 except UnicodeEncodeError, e
:
665 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
666 return self
._get
_numeric
_response
()
668 def sinter(self
, *args
):
671 >>> res = r.delete('s1')
672 >>> res = r.delete('s2')
673 >>> res = r.delete('s3')
674 >>> r.sadd('s1', 'a')
676 >>> r.sadd('s2', 'a')
678 >>> r.sadd('s3', 'b')
682 ... except ResponseError, e:
684 wrong number of arguments
687 ... except ResponseError, e:
689 Operation against a key holding the wrong kind of value
690 >>> r.sinter('s1', 's2', 's3')
692 >>> r.sinter('s1', 's2')
697 self
._write
('SINTER %s\r\n' % ' '.join(args
))
698 return set(self
._get
_multi
_response
())
700 def sinterstore(self
, dest
, *args
):
703 >>> res = r.delete('s1')
704 >>> res = r.delete('s2')
705 >>> res = r.delete('s3')
706 >>> r.sadd('s1', 'a')
708 >>> r.sadd('s2', 'a')
710 >>> r.sadd('s3', 'b')
712 >>> r.sinterstore('s_s', 's1', 's2', 's3')
714 >>> r.sinterstore('s_s', 's1', 's2')
716 >>> r.smembers('s_s')
721 self
._write
('SINTERSTORE %s %s\r\n' % (dest
, ' '.join(args
)))
722 return self
._get
_simple
_response
()
724 def smembers(self
, name
):
735 ... except ResponseError, e:
737 Operation against a key holding the wrong kind of value
743 self
._write
('SMEMBERS %s\r\n' % name
)
744 return set(self
._get
_multi
_response
())
746 def select(self
, db
):
761 self
._write
('SELECT %s\r\n' % db
)
762 return self
._get
_simple
_response
()
764 def move(self
, name
, db
):
792 self
._write
('MOVE %s %s\r\n' % (name
, db
))
793 return self
._get
_numeric
_response
()
795 def save(self
, background
=False):
801 ... resp = r.save(background=True)
802 ... except ResponseError, e:
803 ... assert str(e) == 'background save already in progress', str(e)
805 ... assert resp == 'OK'
810 self
._write
('BGSAVE\r\n')
812 self
._write
('SAVE\r\n')
813 return self
._get
_simple
_response
()
819 >>> t = int(time.time())
822 >>> r.lastsave() >= t
827 self
._write
('LASTSAVE\r\n')
828 return self
._get
_numeric
_response
()
830 def flush(self
, all_dbs
=False):
835 >>> r.flush(all_dbs=True)
840 self
._write
('%s\r\n' % ('FLUSHALL' if all_dbs
else 'FLUSHDB'))
841 return self
._get
_simple
_response
()
847 >>> info and isinstance(info, dict)
849 >>> isinstance(info.get('connected_clients'), int)
854 self
._write
('INFO\r\n')
856 for l
in self
._get
_value
().split('\r\n'):
859 k
, v
= l
.split(':', 1)
860 info
[k
] = int(v
) if v
.isdigit() else v
863 def _get_value(self
, negative_as_nil
=False):
864 data
= self
._read
().strip()
865 if data
== 'nil' or (negative_as_nil
and data
== '-1'):
868 self
._check
_for
_error
(data
)
871 except (TypeError, ValueError):
872 raise ResponseError("Cannot parse response '%s' as data length." % data
)
878 # we got the ending crlf
882 # the data has a trailing crlf embedded, let's restore it
883 buf
.append(self
._read
())
886 def _get_simple_response(self
):
887 data
= self
._read
().strip()
890 self
._check
_for
_error
(data
)
891 raise InvalidResponse("Cannot parse first line '%s' for a simple response." % data
, data
)
893 def _get_numeric_response(self
, allow_negative
=True):
894 data
= self
._read
().strip()
897 except (TypeError, ValueError), e
:
900 if not allow_negative
and value
< 0:
901 self
._check
_for
_error
(data
)
903 self
._check
_for
_error
(data
)
904 raise InvalidResponse("Cannot parse first line '%s' for a numeric response: %s." % (data
, e
), data
)
906 def _get_multi_response(self
):
909 num
= self
._get
_numeric
_response
(allow_negative
=False)
910 except InvalidResponse
, e
:
911 if e
.args
[1] == 'nil':
915 results
.append(self
._get
_value
(negative_as_nil
=True))
919 def _check_for_error(self
, data
):
920 if not data
or data
[0] != '-':
922 if data
.startswith('-ERR'):
923 raise ResponseError(data
[4:].strip())
925 error_len
= int(data
[1:])
926 except (TypeError, ValueError):
927 raise ResponseError("Unknown error format '%s'." % data
)
928 error_message
= self
._read
().strip()[5:]
929 raise ResponseError(error_message
)
931 def disconnect(self
):
932 if isinstance(self
._sock
, socket
.socket
):
944 >>> isinstance(r._sock, socket.socket)
948 if isinstance(self
._sock
, socket
.socket
):
951 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
952 sock
.connect((self
.host
, self
.port
))
953 except socket
.error
, e
:
954 raise ConnectionError("Error %s connecting to %s:%s. %s." % (e
.args
[0], self
.host
, self
.port
, e
.args
[1]))
957 self
._fp
= self
._sock
.makefile('r')
960 if __name__
== '__main__':