]>
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, db
=None):
36 self
.host
= host
or 'localhost'
37 self
.port
= port
or 6379
39 socket
.setdefaulttimeout(timeout
)
51 ... except ConnectionError, e:
53 Error 9 while writing to socket. Bad file descriptor.
59 except socket
.error
, e
:
63 raise ConnectionError("Error %s while writing to socket. %s." % tuple(e
.args
))
67 return self
._fp
.readline()
68 except socket
.error
, e
:
69 if e
.args
and e
.args
[0] == errno
.EAGAIN
:
72 raise ConnectionError("Error %s while reading from socket. %s." % tuple(e
.args
))
75 raise ConnectionError("Socket connection closed when reading.")
86 self
._write
('PING\r\n')
87 return self
.get_response()
89 def set(self
, name
, value
, preserve
=False):
92 >>> r.set('a', 'pippo')
95 ... r.set('a', u'pippo \u3235')
96 ... except InvalidData, e:
98 Error encoding unicode value for key 'a': 'ascii' codec can't encode character u'\u3235' in position 15: ordinal not in range(128).
101 >>> r.set('b', 'xxx', preserve=True)
108 # the following will raise an error for unicode values that can't be encoded to ascii
109 # we could probably add an 'encoding' arg to init, but then what do we do with get()?
110 # convert back to unicode? and what about ints, or pickled values?
112 value
= value
if isinstance(value
, basestring
) else str(value
)
113 self
._write
('%s %s %s\r\n%s\r\n' % (
114 'SETNX' if preserve
else 'SET', name
, len(value
), value
116 except UnicodeEncodeError, e
:
117 raise InvalidData("Error encoding unicode value for key '%s': %s." % (name
, e
))
118 return self
.get_response()
123 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '), r.set('d', '\\r\\n')
124 ('OK', 'OK', 'OK', 'OK')
134 ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
136 ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
141 self
._write
('GET %s\r\n' % name
)
142 return self
.get_response()
144 def mget(self
, *args
):
147 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n')
148 ('OK', 'OK', 'OK', 'OK')
149 >>> r.mget('a', 'b', 'c', 'd')
150 ['pippo', '15', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n', '\\r\\n']
154 self
._write
('MGET %s\r\n' % ' '.join(args
))
155 return self
.get_response()
157 def incr(self
, name
, amount
=1):
172 self
._write
('INCR %s\r\n' % name
)
174 self
._write
('INCRBY %s %s\r\n' % (name
, amount
))
175 return self
.get_response()
177 def decr(self
, name
, amount
=1):
195 self
._write
('DECR %s\r\n' % name
)
197 self
._write
('DECRBY %s %s\r\n' % (name
, amount
))
198 return self
.get_response()
200 def exists(self
, name
):
203 >>> r.exists('dsjhfksjdhfkdsjfh')
212 self
._write
('EXISTS %s\r\n' % name
)
213 return self
.get_response()
215 def delete(self
, name
):
218 >>> r.delete('dsjhfksjdhfkdsjfh')
231 self
._write
('DEL %s\r\n' % name
)
232 return self
.get_response()
234 def get_type(self
, name
):
241 >>> r.get_type('zzz')
245 self
._write
('TYPE %s\r\n' % name
)
246 res
= self
.get_response()
247 return None if res
== 'none' else res
249 def keys(self
, pattern
):
264 >>> r.keys('sjdfhskjh*')
269 self
._write
('KEYS %s\r\n' % pattern
)
270 return self
.get_response().split()
277 >>> isinstance(r.randomkey(), str)
281 #raise NotImplementedError("Implemented but buggy, do not use.")
283 self
._write
('RANDOMKEY\r\n')
284 return self
.get_response()
286 def rename(self
, src
, dst
, preserve
=False):
290 ... r.rename('a', 'a')
291 ... except ResponseError, e:
293 source and destination objects are the same
294 >>> r.rename('a', 'b')
297 ... r.rename('a', 'b')
298 ... except ResponseError, e:
303 >>> r.rename('b', 'a', preserve=True)
309 self
._write
('RENAMENX %s %s\r\n' % (src
, dst
))
310 return self
.get_response()
312 self
._write
('RENAME %s %s\r\n' % (src
, dst
))
313 return self
.get_response() #.strip()
315 def expire(self
, name
, time
):
322 >>> r.expire('zzzzz', 1)
327 self
._write
('EXPIRE %s %s\r\n' % (name
, time
))
328 return self
.get_response()
330 def push(self
, name
, value
, tail
=False):
341 ... except ResponseError, e:
343 Operation against a key holding the wrong kind of value
347 # same considerations on unicode as in set() apply here
349 value
= value
if isinstance(value
, basestring
) else str(value
)
350 self
._write
('%s %s %s\r\n%s\r\n' % (
351 'LPUSH' if tail
else 'RPUSH', name
, len(value
), value
353 except UnicodeEncodeError, e
:
354 raise InvalidData("Error encoding unicode value for element in list '%s': %s." % (name
, e
))
355 return self
.get_response()
357 def llen(self
, name
):
373 self
._write
('LLEN %s\r\n' % name
)
374 return self
.get_response()
376 def lrange(self
, name
, start
, end
):
381 >>> r.lrange('l', 0, 1)
383 >>> r.push('l', 'aaa')
385 >>> r.lrange('l', 0, 1)
387 >>> r.push('l', 'bbb')
389 >>> r.lrange('l', 0, 0)
391 >>> r.lrange('l', 0, 1)
393 >>> r.lrange('l', -1, 0)
395 >>> r.lrange('l', -1, -1)
400 self
._write
('LRANGE %s %s %s\r\n' % (name
, start
, end
))
401 return self
.get_response()
403 def ltrim(self
, name
, start
, end
):
409 ... r.ltrim('l', 0, 1)
410 ... except ResponseError, e:
413 >>> r.push('l', 'aaa')
415 >>> r.push('l', 'bbb')
417 >>> r.push('l', 'ccc')
419 >>> r.ltrim('l', 0, 1)
423 >>> r.ltrim('l', 99, 95)
430 self
._write
('LTRIM %s %s %s\r\n' % (name
, start
, end
))
431 return self
.get_response()
433 def lindex(self
, name
, index
):
436 >>> res = r.delete('l')
438 >>> r.push('l', 'aaa')
443 >>> r.push('l', 'ccc')
447 >>> r.lindex('l', -1)
452 self
._write
('LINDEX %s %s\r\n' % (name
, index
))
453 return self
.get_response()
455 def pop(self
, name
, tail
=False):
461 >>> r.push('l', 'aaa')
463 >>> r.push('l', 'bbb')
470 >>> r.push('l', 'aaa')
472 >>> r.push('l', 'bbb')
474 >>> r.pop('l', tail=True)
482 self
._write
('%s %s\r\n' % ('RPOP' if tail
else 'LPOP', name
))
483 return self
.get_response()
485 def lset(self
, name
, index
, value
):
491 ... r.lset('l', 0, 'a')
492 ... except ResponseError, e:
495 >>> r.push('l', 'aaa')
498 ... r.lset('l', 1, 'a')
499 ... except ResponseError, e:
502 >>> r.lset('l', 0, 'bbb')
504 >>> r.lrange('l', 0, 1)
510 value
= value
if isinstance(value
, basestring
) else str(value
)
511 self
._write
('LSET %s %s %s\r\n%s\r\n' % (
512 name
, index
, len(value
), value
514 except UnicodeEncodeError, e
:
515 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
516 return self
.get_response()
518 def lrem(self
, name
, value
, num
=0):
523 >>> r.push('l', 'aaa')
525 >>> r.push('l', 'bbb')
527 >>> r.push('l', 'aaa')
529 >>> r.lrem('l', 'aaa')
531 >>> r.lrange('l', 0, 10)
533 >>> r.push('l', 'aaa')
535 >>> r.push('l', 'aaa')
537 >>> r.lrem('l', 'aaa', 1)
539 >>> r.lrem('l', 'aaa', 1)
541 >>> r.lrem('l', 'aaa', 1)
547 value
= value
if isinstance(value
, basestring
) else str(value
)
548 self
._write
('LREM %s %s %s\r\n%s\r\n' % (
549 name
, num
, len(value
), value
551 except UnicodeEncodeError, e
:
552 raise InvalidData("Error encoding unicode value for element %s in list '%s': %s." % (index
, name
, e
))
553 return self
.get_response()
555 def sort(self
, name
, by
=None, get
=None, start
=None, num
=None, desc
=False, alpha
=False):
560 >>> r.push('l', 'ccc')
562 >>> r.push('l', 'aaa')
564 >>> r.push('l', 'ddd')
566 >>> r.push('l', 'bbb')
568 >>> r.sort('l', alpha=True)
569 ['aaa', 'bbb', 'ccc', 'ddd']
572 >>> for i in range(1, 5):
573 ... res = r.push('l', 1.0 / i)
575 ['0.25', '0.333333333333', '0.5', '1.0']
576 >>> r.sort('l', desc=True)
577 ['1.0', '0.5', '0.333333333333', '0.25']
578 >>> r.sort('l', desc=True, start=2, num=1)
580 >>> r.set('weight_0.5', 10)
582 >>> r.sort('l', desc=True, by='weight_*')
583 ['0.5', '1.0', '0.333333333333', '0.25']
584 >>> for i in r.sort('l', desc=True):
585 ... res = r.set('test_%s' % i, 100 - float(i))
586 >>> r.sort('l', desc=True, get='test_*')
587 ['99.0', '99.5', '99.6666666667', '99.75']
588 >>> r.sort('l', desc=True, by='weight_*', get='test_*')
589 ['99.5', '99.0', '99.6666666667', '99.75']
590 >>> r.sort('l', desc=True, by='weight_*', get='missing_*')
591 [None, None, None, None]
594 stmt
= ['SORT', name
]
596 stmt
.append("BY %s" % by
)
598 stmt
.append("LIMIT %s %s" % (start
, num
))
601 elif isinstance(get
, basestring
):
602 stmt
.append("GET %s" % get
)
603 elif isinstance(get
, list) or isinstance(get
, tuple):
605 stmt
.append("GET %s" % g
)
607 raise RedisError("Invalid parameter 'get' for Redis sort")
613 self
._write
(' '.join(stmt
+ ["\r\n"]))
614 return self
.get_response()
616 def sadd(self
, name
, value
):
619 >>> res = r.delete('s')
627 # same considerations on unicode as in set() apply here
629 value
= value
if isinstance(value
, basestring
) else str(value
)
630 self
._write
('SADD %s %s\r\n%s\r\n' % (
631 name
, len(value
), value
633 except UnicodeEncodeError, e
:
634 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
635 return self
.get_response()
637 def srem(self
, name
, value
):
642 >>> r.srem('s', 'aaa')
648 >>> r.sismember('s', 'b')
653 # same considerations on unicode as in set() apply here
655 value
= value
if isinstance(value
, basestring
) else str(value
)
656 self
._write
('SREM %s %s\r\n%s\r\n' % (
657 name
, len(value
), value
659 except UnicodeEncodeError, e
:
660 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
661 return self
.get_response()
663 def sismember(self
, name
, value
):
668 >>> r.sismember('s', 'b')
672 >>> r.sismember('s', 'b')
674 >>> r.sismember('s', 'a')
679 # same considerations on unicode as in set() apply here
681 value
= value
if isinstance(value
, basestring
) else str(value
)
682 self
._write
('SISMEMBER %s %s\r\n%s\r\n' % (
683 name
, len(value
), value
685 except UnicodeEncodeError, e
:
686 raise InvalidData("Error encoding unicode value for element in set '%s': %s." % (name
, e
))
687 return self
.get_response()
689 def sinter(self
, *args
):
692 >>> res = r.delete('s1')
693 >>> res = r.delete('s2')
694 >>> res = r.delete('s3')
695 >>> r.sadd('s1', 'a')
697 >>> r.sadd('s2', 'a')
699 >>> r.sadd('s3', 'b')
703 ... except ResponseError, e:
705 wrong number of arguments
708 ... except ResponseError, e:
710 Operation against a key holding the wrong kind of value
711 >>> r.sinter('s1', 's2', 's3')
713 >>> r.sinter('s1', 's2')
718 self
._write
('SINTER %s\r\n' % ' '.join(args
))
719 return set(self
.get_response())
721 def sinterstore(self
, dest
, *args
):
724 >>> res = r.delete('s1')
725 >>> res = r.delete('s2')
726 >>> res = r.delete('s3')
727 >>> r.sadd('s1', 'a')
729 >>> r.sadd('s2', 'a')
731 >>> r.sadd('s3', 'b')
733 >>> r.sinterstore('s_s', 's1', 's2', 's3')
735 >>> r.sinterstore('s_s', 's1', 's2')
737 >>> r.smembers('s_s')
742 self
._write
('SINTERSTORE %s %s\r\n' % (dest
, ' '.join(args
)))
743 return self
.get_response()
745 def smembers(self
, name
):
756 ... except ResponseError, e:
758 Operation against a key holding the wrong kind of value
764 self
._write
('SMEMBERS %s\r\n' % name
)
765 return set(self
.get_response())
767 def select(self
, db
):
782 self
._write
('SELECT %s\r\n' % db
)
783 return self
.get_response()
785 def move(self
, name
, db
):
811 self
._write
('MOVE %s %s\r\n' % (name
, db
))
812 return self
.get_response()
814 def save(self
, background
=False):
820 ... resp = r.save(background=True)
821 ... except ResponseError, e:
822 ... assert str(e) == 'background save already in progress', str(e)
824 ... assert resp == 'OK'
829 self
._write
('BGSAVE\r\n')
831 self
._write
('SAVE\r\n')
832 return self
.get_response()
838 >>> t = int(time.time())
841 >>> r.lastsave() >= t
846 self
._write
('LASTSAVE\r\n')
847 return self
.get_response()
849 def flush(self
, all_dbs
=False):
854 >>> # r.flush(all_dbs=True)
858 self
._write
('%s\r\n' % ('FLUSHALL' if all_dbs
else 'FLUSHDB'))
859 return self
.get_response()
865 >>> info and isinstance(info, dict)
867 >>> isinstance(info.get('connected_clients'), int)
872 self
._write
('INFO\r\n')
874 for l
in self
.get_response().split('\r\n'):
877 k
, v
= l
.split(':', 1)
878 info
[k
] = int(v
) if v
.isdigit() else v
881 def get_response(self
):
882 data
= self
._read
().strip()
885 raise ResponseError(data
[5:] if data
[:5] == '-ERR ' else data
[1:])
891 except (TypeError, ValueError):
892 raise InvalidResponse("Cannot convert multi-response header '%s' to integer" % data
)
895 result
.append(self
._get
_value
())
897 return self
._get
_value
(data
)
899 def _get_value(self
, data
=None):
900 data
= data
or self
._read
().strip()
904 c
, i
= data
[0], (int(data
[1:]) if data
.find('.') == -1 else float(data
[1:]))
906 raise InvalidResponse("Cannot convert data '%s' to integer" % data
)
910 raise InvalidResponse("Unkown response prefix for '%s'" % data
)
918 return ''.join(buf
)[:-2]
920 def disconnect(self
):
921 if isinstance(self
._sock
, socket
.socket
):
933 >>> isinstance(r._sock, socket.socket)
938 if isinstance(self
._sock
, socket
.socket
):
941 sock
= socket
.socket(socket
.AF_INET
, socket
.SOCK_STREAM
)
942 sock
.connect((self
.host
, self
.port
))
943 except socket
.error
, e
:
944 raise ConnectionError("Error %s connecting to %s:%s. %s." % (e
.args
[0], self
.host
, self
.port
, e
.args
[1]))
947 self
._fp
= self
._sock
.makefile('r')
952 if __name__
== '__main__':