]>
git.saurik.com Git - redis.git/blob - client-libraries/python/redis.py
e844f812b05e348aa470fbdcad2219446e93308a
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 incr(self
, name
, amount
=1):
158 self
._write
('INCR %s\r\n' % name
)
160 self
._write
('INCRBY %s %s\r\n' % (name
, amount
))
161 return self
._get
_numeric
_response
()
163 def decr(self
, name
, amount
=1):
181 self
._write
('DECR %s\r\n' % name
)
183 self
._write
('DECRBY %s %s\r\n' % (name
, amount
))
184 return self
._get
_numeric
_response
()
186 def exists(self
, name
):
189 >>> r.exists('dsjhfksjdhfkdsjfh')
198 self
._write
('EXISTS %s\r\n' % name
)
199 return self
._get
_numeric
_response
()
201 def delete(self
, name
):
204 >>> r.delete('dsjhfksjdhfkdsjfh')
217 self
._write
('DEL %s\r\n' % name
)
218 return self
._get
_numeric
_response
()
220 def key_type(self
, name
):
225 self
._write
('TYPE %s\r\n' % name
)
226 return self
._get
_simple
_response
()
228 def keys(self
, pattern
):
243 >>> r.keys('sjdfhskjh*')
248 self
._write
('KEYS %s\r\n' % pattern
)
249 return self
._get
_value
().split()
256 >>> isinstance(r.randomkey(), str)
260 #raise NotImplementedError("Implemented but buggy, do not use.")
262 self
._write
('RANDOMKEY\r\n')
263 data
= self
._read
().strip()
264 self
._check
_for
_error
(data
)
267 def rename(self
, src
, dst
, preserve
=False):
271 ... r.rename('a', 'a')
272 ... except ResponseError, e:
274 src and dest key are the same
275 >>> r.rename('a', 'b')
278 ... r.rename('a', 'b')
279 ... except ResponseError, e:
284 >>> r.rename('b', 'a', preserve=True)
290 self
._write
('RENAMENX %s %s\r\n' % (src
, dst
))
291 return self
._get
_numeric
_response
()
293 self
._write
('RENAME %s %s\r\n' % (src
, dst
))
294 return self
._get
_simple
_response
().strip()
296 def push(self
, name
, value
, tail
=False):
307 ... except ResponseError, e:
309 Operation against a key holding the wrong kind of value
313 # same considerations on unicode as in set() apply here
315 value
= value
if isinstance(value
, basestring
) else str(value
)
316 self
._write
('%s %s %s\r\n%s\r\n' % (
317 'LPUSH' if tail
else 'RPUSH', name
, len(value
), value
319 except UnicodeEncodeError, e
:
320 raise InvalidData("Error encoding unicode value for element in list '%s': %s." % (name
, e
))
321 return self
._get
_simple
_response
()
323 def llen(self
, name
):
339 self
._write
('LLEN %s\r\n' % name
)
340 return self
._get
_numeric
_response
()
342 def lrange(self
, name
, start
, end
):
347 >>> r.lrange('l', 0, 1)
349 >>> r.push('l', 'aaa')
351 >>> r.lrange('l', 0, 1)
353 >>> r.push('l', 'bbb')
355 >>> r.lrange('l', 0, 0)
357 >>> r.lrange('l', 0, 1)
359 >>> r.lrange('l', -1, 0)
361 >>> r.lrange('l', -1, -1)
366 self
._write
('LRANGE %s %s %s\r\n' % (name
, start
, end
))
367 return self
._get
_multi
_response
()
369 def ltrim(self
, name
, start
, end
):
375 ... r.ltrim('l', 0, 1)
376 ... except ResponseError, e:
379 >>> r.push('l', 'aaa')
381 >>> r.push('l', 'bbb')
383 >>> r.push('l', 'ccc')
385 >>> r.ltrim('l', 0, 1)
389 >>> r.ltrim('l', 99, 95)
396 self
._write
('LTRIM %s %s %s\r\n' % (name
, start
, end
))
397 return self
._get
_simple
_response
()
399 def lindex(self
, name
, index
):
402 >>> res = r.delete('l')
404 >>> r.push('l', 'aaa')
409 >>> r.push('l', 'ccc')
413 >>> r.lindex('l', -1)
418 self
._write
('LINDEX %s %s\r\n' % (name
, index
))
419 return self
._get
_value
()
421 def pop(self
, name
, tail
=False):
427 >>> r.push('l', 'aaa')
429 >>> r.push('l', 'bbb')
436 >>> r.push('l', 'aaa')
438 >>> r.push('l', 'bbb')
440 >>> r.pop('l', tail=True)
448 self
._write
('%s %s\r\n' % ('RPOP' if tail
else 'LPOP', name
))
449 return self
._get
_value
()
451 def lset(self
, name
, index
, value
):
457 ... r.lset('l', 0, 'a')
458 ... except ResponseError, e:
461 >>> r.push('l', 'aaa')
464 ... r.lset('l', 1, 'a')
465 ... except ResponseError, e:
468 >>> r.lset('l', 0, 'bbb')
470 >>> r.lrange('l', 0, 1)
476 value
= value
if isinstance(value
, basestring
) else str(value
)
477 self
._write
('LSET %s %s %s\r\n%s\r\n' % (
478 name
, index
, len(value
), value
480 except UnicodeEncodeError, e
:
481 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
482 return self
._get
_simple
_response
()
484 def lrem(self
, name
, value
, num
=0):
489 >>> r.push('l', 'aaa')
491 >>> r.push('l', 'bbb')
493 >>> r.push('l', 'aaa')
495 >>> r.lrem('l', 'aaa')
497 >>> r.lrange('l', 0, 10)
499 >>> r.push('l', 'aaa')
501 >>> r.push('l', 'aaa')
503 >>> r.lrem('l', 'aaa', 1)
505 >>> r.lrem('l', 'aaa', 1)
507 >>> r.lrem('l', 'aaa', 1)
513 value
= value
if isinstance(value
, basestring
) else str(value
)
514 self
._write
('LREM %s %s %s\r\n%s\r\n' % (
515 name
, num
, len(value
), value
517 except UnicodeEncodeError, e
:
518 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
519 return self
._get
_numeric
_response
()
521 def sort(self
, name
, by
=None, get
=None, start
=None, num
=None, desc
=False, alpha
=False):
526 >>> r.push('l', 'ccc')
528 >>> r.push('l', 'aaa')
530 >>> r.push('l', 'ddd')
532 >>> r.push('l', 'bbb')
534 >>> r.sort('l', alpha=True)
535 ['aaa', 'bbb', 'ccc', 'ddd']
538 >>> for i in range(1, 5):
539 ... res = r.push('l', 1.0 / i)
541 ['0.25', '0.333333333333', '0.5', '1.0']
542 >>> r.sort('l', desc=True)
543 ['1.0', '0.5', '0.333333333333', '0.25']
544 >>> r.sort('l', desc=True, start=2, num=1)
546 >>> r.set('weight_0.5', 10)
548 >>> r.sort('l', desc=True, by='weight_*')
549 ['0.5', '1.0', '0.333333333333', '0.25']
550 >>> for i in r.sort('l', desc=True):
551 ... res = r.set('test_%s' % i, 100 - float(i))
552 >>> r.sort('l', desc=True, get='test_*')
553 ['99.0', '99.5', '99.6666666667', '99.75']
554 >>> r.sort('l', desc=True, by='weight_*', get='test_*')
555 ['99.5', '99.0', '99.6666666667', '99.75']
556 >>> r.sort('l', desc=True, by='weight_*', get='missing_*')
557 [None, None, None, None]
560 stmt
= ['SORT', name
]
562 stmt
.append("BY %s" % by
)
564 stmt
.append("LIMIT %s %s" % (start
, num
))
567 elif isinstance(get
, basestring
):
568 stmt
.append("GET %s" % get
)
569 elif isinstance(get
, list) or isinstance(get
, tuple):
571 stmt
.append("GET %s" % g
)
573 raise RedisError("Invalid parameter 'get' for Redis sort")
579 self
._write
(' '.join(stmt
+ ["\r\n"]))
580 return self
._get
_multi
_response
()
582 def sadd(self
, name
, value
):
585 >>> res = r.delete('s')
593 # same considerations on unicode as in set() apply here
595 value
= value
if isinstance(value
, basestring
) else str(value
)
596 self
._write
('SADD %s %s\r\n%s\r\n' % (
597 name
, len(value
), value
599 except UnicodeEncodeError, e
:
600 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
601 return self
._get
_numeric
_response
()
603 def srem(self
, name
, value
):
608 >>> r.srem('s', 'aaa')
614 >>> r.sismember('s', 'b')
619 # same considerations on unicode as in set() apply here
621 value
= value
if isinstance(value
, basestring
) else str(value
)
622 self
._write
('SREM %s %s\r\n%s\r\n' % (
623 name
, len(value
), value
625 except UnicodeEncodeError, e
:
626 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
627 return self
._get
_numeric
_response
()
629 def sismember(self
, name
, value
):
634 >>> r.sismember('s', 'b')
638 >>> r.sismember('s', 'b')
640 >>> r.sismember('s', 'a')
645 # same considerations on unicode as in set() apply here
647 value
= value
if isinstance(value
, basestring
) else str(value
)
648 self
._write
('SISMEMBER %s %s\r\n%s\r\n' % (
649 name
, len(value
), value
651 except UnicodeEncodeError, e
:
652 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
653 return self
._get
_numeric
_response
()
655 def sinter(self
, *args
):
658 >>> res = r.delete('s1')
659 >>> res = r.delete('s2')
660 >>> res = r.delete('s3')
661 >>> r.sadd('s1', 'a')
663 >>> r.sadd('s2', 'a')
665 >>> r.sadd('s3', 'b')
669 ... except ResponseError, e:
671 wrong number of arguments
674 ... except ResponseError, e:
676 Operation against a key holding the wrong kind of value
677 >>> r.sinter('s1', 's2', 's3')
679 >>> r.sinter('s1', 's2')
684 self
._write
('SINTER %s\r\n' % ' '.join(args
))
685 return set(self
._get
_multi
_response
())
687 def sinterstore(self
, dest
, *args
):
690 >>> res = r.delete('s1')
691 >>> res = r.delete('s2')
692 >>> res = r.delete('s3')
693 >>> r.sadd('s1', 'a')
695 >>> r.sadd('s2', 'a')
697 >>> r.sadd('s3', 'b')
699 >>> r.sinterstore('s_s', 's1', 's2', 's3')
701 >>> r.sinterstore('s_s', 's1', 's2')
703 >>> r.smembers('s_s')
708 self
._write
('SINTERSTORE %s %s\r\n' % (dest
, ' '.join(args
)))
709 return self
._get
_simple
_response
()
711 def smembers(self
, name
):
722 ... except ResponseError, e:
724 Operation against a key holding the wrong kind of value
730 self
._write
('SMEMBERS %s\r\n' % name
)
731 return set(self
._get
_multi
_response
())
733 def select(self
, db
):
748 self
._write
('SELECT %s\r\n' % db
)
749 return self
._get
_simple
_response
()
751 def move(self
, name
, db
):
779 self
._write
('MOVE %s %s\r\n' % (name
, db
))
780 return self
._get
_numeric
_response
()
782 def save(self
, background
=False):
788 ... resp = r.save(background=True)
789 ... except ResponseError, e:
790 ... assert str(e) == 'background save already in progress', str(e)
792 ... assert resp == 'OK'
797 self
._write
('BGSAVE\r\n')
799 self
._write
('SAVE\r\n')
800 return self
._get
_simple
_response
()
806 >>> t = int(time.time())
809 >>> r.lastsave() >= t
814 self
._write
('LASTSAVE\r\n')
815 return self
._get
_numeric
_response
()
817 def flush(self
, all_dbs
=False):
822 >>> r.flush(all_dbs=True)
827 self
._write
('%s\r\n' % ('FLUSHALL' if all_dbs
else 'FLUSHDB'))
828 return self
._get
_simple
_response
()
830 def _get_value(self
, negative_as_nil
=False):
831 data
= self
._read
().strip()
832 if data
== 'nil' or (negative_as_nil
and data
== '-1'):
835 self
._check
_for
_error
(data
)
838 except (TypeError, ValueError):
839 raise ResponseError("Cannot parse response '%s' as data length." % data
)
845 # we got the ending crlf
849 # the data has a trailing crlf embedded, let's restore it
850 buf
.append(self
._read
())
853 def _get_simple_response(self
):
854 data
= self
._read
().strip()
857 self
._check
_for
_error
(data
)
858 raise InvalidResponse("Cannot parse first line '%s' for a simple response." % data
, data
)
860 def _get_numeric_response(self
, allow_negative
=True):
861 data
= self
._read
().strip()
864 except (TypeError, ValueError), e
:
867 if not allow_negative
and value
< 0:
868 self
._check
_for
_error
(data
)
870 self
._check
_for
_error
(data
)
871 raise InvalidResponse("Cannot parse first line '%s' for a numeric response: %s." % (data
, e
), data
)
873 def _get_multi_response(self
):
876 num
= self
._get
_numeric
_response
(allow_negative
=False)
877 except InvalidResponse
, e
:
878 if e
.args
[1] == 'nil':
882 results
.append(self
._get
_value
(negative_as_nil
=True))
886 def _check_for_error(self
, data
):
887 if not data
or data
[0] != '-':
889 if data
.startswith('-ERR'):
890 raise ResponseError(data
[4:].strip())
892 error_len
= int(data
[1:])
893 except (TypeError, ValueError):
894 raise ResponseError("Unknown error format '%s'." % data
)
895 error_message
= self
._read
().strip()[5:]
896 raise ResponseError(error_message
)
898 def disconnect(self
):
899 if isinstance(self
._sock
, socket
.socket
):
911 >>> isinstance(r._sock, socket.socket)
915 if isinstance(self
._sock
, socket
.socket
):
918 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
919 sock
.connect((self
.host
, self
.port
))
920 except socket
.error
, e
:
921 raise ConnectionError("Error %s connecting to %s:%s. %s." % (e
.args
[0], self
.host
, self
.port
, e
.args
[1]))
924 self
._fp
= self
._sock
.makefile('r')
927 if __name__
== '__main__':