]> git.saurik.com Git - redis.git/blob - client-libraries/python/redis.py
Python lib updated
[redis.git] / client-libraries / python / redis.py
1 #!/usr/bin/env python
2
3 """ redis.py - A client for the Redis daemon.
4
5 History:
6
7 - 20090603 fix missing errno import, add sunion and sunionstore commands,
8 generalize shebang (Jochen Kupperschmidt)
9
10 """
11
12 __author__ = "Ludovico Magnocavallo <ludo\x40qix\x2eit>"
13 __copyright__ = "Copyright 2009, Ludovico Magnocavallo"
14 __license__ = "MIT"
15 __version__ = "0.5"
16 __revision__ = "$LastChangedRevision: 175 $"[22:-2]
17 __date__ = "$LastChangedDate: 2009-03-17 16:15:55 +0100 (Mar, 17 Mar 2009) $"[18:-2]
18
19
20 # TODO: Redis._get_multi_response
21
22
23 import socket
24 import decimal
25 import errno
26
27
28 BUFSIZE = 4096
29
30
31 class RedisError(Exception): pass
32 class ConnectionError(RedisError): pass
33 class ResponseError(RedisError): pass
34 class InvalidResponse(RedisError): pass
35 class InvalidData(RedisError): pass
36
37
38 class Redis(object):
39 """The main Redis client.
40 """
41
42 def __init__(self, host=None, port=None, timeout=None, db=None, nodelay=None, charset='utf8', errors='strict'):
43 self.host = host or 'localhost'
44 self.port = port or 6379
45 if timeout:
46 socket.setdefaulttimeout(timeout)
47 self.nodelay = nodelay
48 self.charset = charset
49 self.errors = errors
50 self._sock = None
51 self._fp = None
52 self.db = db
53
54 def _encode(self, s):
55 if isinstance(s, str):
56 return s
57 if isinstance(s, unicode):
58 try:
59 return s.encode(self.charset, self.errors)
60 except UnicodeEncodeError, e:
61 raise InvalidData("Error encoding unicode value '%s': %s" % (value.encode(self.charset, 'replace'), e))
62 return str(s)
63
64 def _write(self, s):
65 """
66 >>> r = Redis(db=9)
67 >>> r.connect()
68 >>> r._sock.close()
69 >>> try:
70 ... r._write('pippo')
71 ... except ConnectionError, e:
72 ... print e
73 Error 9 while writing to socket. Bad file descriptor.
74 >>>
75 >>>
76 """
77 try:
78 self._sock.sendall(s)
79 except socket.error, e:
80 if e.args[0] == 32:
81 # broken pipe
82 self.disconnect()
83 raise ConnectionError("Error %s while writing to socket. %s." % tuple(e.args))
84
85 def _read(self):
86 try:
87 return self._fp.readline()
88 except socket.error, e:
89 if e.args and e.args[0] == errno.EAGAIN:
90 return
91 self.disconnect()
92 raise ConnectionError("Error %s while reading from socket. %s." % tuple(e.args))
93 if not data:
94 self.disconnect()
95 raise ConnectionError("Socket connection closed when reading.")
96 return data
97
98 def ping(self):
99 """
100 >>> r = Redis(db=9)
101 >>> r.ping()
102 'PONG'
103 >>>
104 """
105 self.connect()
106 self._write('PING\r\n')
107 return self.get_response()
108
109 def set(self, name, value, preserve=False, getset=False):
110 """
111 >>> r = Redis(db=9)
112 >>> r.set('a', 'pippo')
113 'OK'
114 >>> r.set('a', u'pippo \u3235')
115 'OK'
116 >>> r.get('a')
117 u'pippo \u3235'
118 >>> r.set('b', 105.2)
119 'OK'
120 >>> r.set('b', 'xxx', preserve=True)
121 0
122 >>> r.get('b')
123 Decimal("105.2")
124 >>>
125 """
126 self.connect()
127 # the following will raise an error for unicode values that can't be encoded to ascii
128 # we could probably add an 'encoding' arg to init, but then what do we do with get()?
129 # convert back to unicode? and what about ints, or pickled values?
130 if getset: command = 'GETSET'
131 elif preserve: command = 'SETNX'
132 else: command = 'SET'
133 value = self._encode(value)
134 self._write('%s %s %s\r\n%s\r\n' % (
135 command, name, len(value), value
136 ))
137 return self.get_response()
138
139 def get(self, name):
140 """
141 >>> r = Redis(db=9)
142 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', ' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '), r.set('d', '\\r\\n')
143 ('OK', 'OK', 'OK', 'OK')
144 >>> r.get('a')
145 u'pippo'
146 >>> r.get('b')
147 15
148 >>> r.get('d')
149 u'\\r\\n'
150 >>> r.get('b')
151 15
152 >>> r.get('c')
153 u' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
154 >>> r.get('c')
155 u' \\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n '
156 >>> r.get('ajhsd')
157 >>>
158 """
159 self.connect()
160 self._write('GET %s\r\n' % name)
161 return self.get_response()
162
163 def getset(self, name, value):
164 """
165 >>> r = Redis(db=9)
166 >>> r.set('a', 'pippo')
167 'OK'
168 >>> r.getset('a', 2)
169 u'pippo'
170 >>>
171 """
172 return self.set(name, value, getset=True)
173
174 def mget(self, *args):
175 """
176 >>> r = Redis(db=9)
177 >>> r.set('a', 'pippo'), r.set('b', 15), r.set('c', '\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n'), r.set('d', '\\r\\n')
178 ('OK', 'OK', 'OK', 'OK')
179 >>> r.mget('a', 'b', 'c', 'd')
180 [u'pippo', 15, u'\\r\\naaa\\nbbb\\r\\ncccc\\nddd\\r\\n', u'\\r\\n']
181 >>>
182 """
183 self.connect()
184 self._write('MGET %s\r\n' % ' '.join(args))
185 return self.get_response()
186
187 def incr(self, name, amount=1):
188 """
189 >>> r = Redis(db=9)
190 >>> r.delete('a')
191 1
192 >>> r.incr('a')
193 1
194 >>> r.incr('a')
195 2
196 >>> r.incr('a', 2)
197 4
198 >>>
199 """
200 self.connect()
201 if amount == 1:
202 self._write('INCR %s\r\n' % name)
203 else:
204 self._write('INCRBY %s %s\r\n' % (name, amount))
205 return self.get_response()
206
207 def decr(self, name, amount=1):
208 """
209 >>> r = Redis(db=9)
210 >>> if r.get('a'):
211 ... r.delete('a')
212 ... else:
213 ... print 1
214 1
215 >>> r.decr('a')
216 -1
217 >>> r.decr('a')
218 -2
219 >>> r.decr('a', 5)
220 -7
221 >>>
222 """
223 self.connect()
224 if amount == 1:
225 self._write('DECR %s\r\n' % name)
226 else:
227 self._write('DECRBY %s %s\r\n' % (name, amount))
228 return self.get_response()
229
230 def exists(self, name):
231 """
232 >>> r = Redis(db=9)
233 >>> r.exists('dsjhfksjdhfkdsjfh')
234 0
235 >>> r.set('a', 'a')
236 'OK'
237 >>> r.exists('a')
238 1
239 >>>
240 """
241 self.connect()
242 self._write('EXISTS %s\r\n' % name)
243 return self.get_response()
244
245 def delete(self, name):
246 """
247 >>> r = Redis(db=9)
248 >>> r.delete('dsjhfksjdhfkdsjfh')
249 0
250 >>> r.set('a', 'a')
251 'OK'
252 >>> r.delete('a')
253 1
254 >>> r.exists('a')
255 0
256 >>> r.delete('a')
257 0
258 >>>
259 """
260 self.connect()
261 self._write('DEL %s\r\n' % name)
262 return self.get_response()
263
264 def get_type(self, name):
265 """
266 >>> r = Redis(db=9)
267 >>> r.set('a', 3)
268 'OK'
269 >>> r.get_type('a')
270 'string'
271 >>> r.get_type('zzz')
272 >>>
273 """
274 self.connect()
275 self._write('TYPE %s\r\n' % name)
276 res = self.get_response()
277 return None if res == 'none' else res
278
279 def keys(self, pattern):
280 """
281 >>> r = Redis(db=9)
282 >>> r.flush()
283 'OK'
284 >>> r.set('a', 'a')
285 'OK'
286 >>> r.keys('a*')
287 [u'a']
288 >>> r.set('a2', 'a')
289 'OK'
290 >>> r.keys('a*')
291 [u'a', u'a2']
292 >>> r.delete('a2')
293 1
294 >>> r.keys('sjdfhskjh*')
295 []
296 >>>
297 """
298 self.connect()
299 self._write('KEYS %s\r\n' % pattern)
300 return self.get_response().split()
301
302 def randomkey(self):
303 """
304 >>> r = Redis(db=9)
305 >>> r.set('a', 'a')
306 'OK'
307 >>> isinstance(r.randomkey(), str)
308 True
309 >>>
310 """
311 #raise NotImplementedError("Implemented but buggy, do not use.")
312 self.connect()
313 self._write('RANDOMKEY\r\n')
314 return self.get_response()
315
316 def rename(self, src, dst, preserve=False):
317 """
318 >>> r = Redis(db=9)
319 >>> try:
320 ... r.rename('a', 'a')
321 ... except ResponseError, e:
322 ... print e
323 source and destination objects are the same
324 >>> r.rename('a', 'b')
325 'OK'
326 >>> try:
327 ... r.rename('a', 'b')
328 ... except ResponseError, e:
329 ... print e
330 no such key
331 >>> r.set('a', 1)
332 'OK'
333 >>> r.rename('b', 'a', preserve=True)
334 0
335 >>>
336 """
337 self.connect()
338 if preserve:
339 self._write('RENAMENX %s %s\r\n' % (src, dst))
340 return self.get_response()
341 else:
342 self._write('RENAME %s %s\r\n' % (src, dst))
343 return self.get_response() #.strip()
344
345 def dbsize(self):
346 """
347 >>> r = Redis(db=9)
348 >>> type(r.dbsize())
349 <type 'int'>
350 >>>
351 """
352 self.connect()
353 self._write('DBSIZE\r\n')
354 return self.get_response()
355
356 def ttl(self, name):
357 """
358 >>> r = Redis(db=9)
359 >>> r.ttl('a')
360 -1
361 >>> r.expire('a', 10)
362 1
363 >>> r.ttl('a')
364 10
365 >>> r.expire('a', 0)
366 0
367 >>>
368 """
369 self.connect()
370 self._write('TTL %s\r\n' % name)
371 return self.get_response()
372
373 def expire(self, name, time):
374 """
375 >>> r = Redis(db=9)
376 >>> r.set('a', 1)
377 'OK'
378 >>> r.expire('a', 1)
379 1
380 >>> r.expire('zzzzz', 1)
381 0
382 >>>
383 """
384 self.connect()
385 self._write('EXPIRE %s %s\r\n' % (name, time))
386 return self.get_response()
387
388 def push(self, name, value, tail=False):
389 """
390 >>> r = Redis(db=9)
391 >>> r.delete('l')
392 1
393 >>> r.push('l', 'a')
394 'OK'
395 >>> r.set('a', 'a')
396 'OK'
397 >>> try:
398 ... r.push('a', 'a')
399 ... except ResponseError, e:
400 ... print e
401 Operation against a key holding the wrong kind of value
402 >>>
403 """
404 self.connect()
405 value = self._encode(value)
406 self._write('%s %s %s\r\n%s\r\n' % (
407 'LPUSH' if tail else 'RPUSH', name, len(value), value
408 ))
409 return self.get_response()
410
411 def llen(self, name):
412 """
413 >>> r = Redis(db=9)
414 >>> r.delete('l')
415 1
416 >>> r.push('l', 'a')
417 'OK'
418 >>> r.llen('l')
419 1
420 >>> r.push('l', 'a')
421 'OK'
422 >>> r.llen('l')
423 2
424 >>>
425 """
426 self.connect()
427 self._write('LLEN %s\r\n' % name)
428 return self.get_response()
429
430 def lrange(self, name, start, end):
431 """
432 >>> r = Redis(db=9)
433 >>> r.delete('l')
434 1
435 >>> r.lrange('l', 0, 1)
436 []
437 >>> r.push('l', 'aaa')
438 'OK'
439 >>> r.lrange('l', 0, 1)
440 [u'aaa']
441 >>> r.push('l', 'bbb')
442 'OK'
443 >>> r.lrange('l', 0, 0)
444 [u'aaa']
445 >>> r.lrange('l', 0, 1)
446 [u'aaa', u'bbb']
447 >>> r.lrange('l', -1, 0)
448 []
449 >>> r.lrange('l', -1, -1)
450 [u'bbb']
451 >>>
452 """
453 self.connect()
454 self._write('LRANGE %s %s %s\r\n' % (name, start, end))
455 return self.get_response()
456
457 def ltrim(self, name, start, end):
458 """
459 >>> r = Redis(db=9)
460 >>> r.delete('l')
461 1
462 >>> try:
463 ... r.ltrim('l', 0, 1)
464 ... except ResponseError, e:
465 ... print e
466 no such key
467 >>> r.push('l', 'aaa')
468 'OK'
469 >>> r.push('l', 'bbb')
470 'OK'
471 >>> r.push('l', 'ccc')
472 'OK'
473 >>> r.ltrim('l', 0, 1)
474 'OK'
475 >>> r.llen('l')
476 2
477 >>> r.ltrim('l', 99, 95)
478 'OK'
479 >>> r.llen('l')
480 0
481 >>>
482 """
483 self.connect()
484 self._write('LTRIM %s %s %s\r\n' % (name, start, end))
485 return self.get_response()
486
487 def lindex(self, name, index):
488 """
489 >>> r = Redis(db=9)
490 >>> res = r.delete('l')
491 >>> r.lindex('l', 0)
492 >>> r.push('l', 'aaa')
493 'OK'
494 >>> r.lindex('l', 0)
495 u'aaa'
496 >>> r.lindex('l', 2)
497 >>> r.push('l', 'ccc')
498 'OK'
499 >>> r.lindex('l', 1)
500 u'ccc'
501 >>> r.lindex('l', -1)
502 u'ccc'
503 >>>
504 """
505 self.connect()
506 self._write('LINDEX %s %s\r\n' % (name, index))
507 return self.get_response()
508
509 def pop(self, name, tail=False):
510 """
511 >>> r = Redis(db=9)
512 >>> r.delete('l')
513 1
514 >>> r.pop('l')
515 >>> r.push('l', 'aaa')
516 'OK'
517 >>> r.push('l', 'bbb')
518 'OK'
519 >>> r.pop('l')
520 u'aaa'
521 >>> r.pop('l')
522 u'bbb'
523 >>> r.pop('l')
524 >>> r.push('l', 'aaa')
525 'OK'
526 >>> r.push('l', 'bbb')
527 'OK'
528 >>> r.pop('l', tail=True)
529 u'bbb'
530 >>> r.pop('l')
531 u'aaa'
532 >>> r.pop('l')
533 >>>
534 """
535 self.connect()
536 self._write('%s %s\r\n' % ('RPOP' if tail else 'LPOP', name))
537 return self.get_response()
538
539 def lset(self, name, index, value):
540 """
541 >>> r = Redis(db=9)
542 >>> r.delete('l')
543 1
544 >>> try:
545 ... r.lset('l', 0, 'a')
546 ... except ResponseError, e:
547 ... print e
548 no such key
549 >>> r.push('l', 'aaa')
550 'OK'
551 >>> try:
552 ... r.lset('l', 1, 'a')
553 ... except ResponseError, e:
554 ... print e
555 index out of range
556 >>> r.lset('l', 0, 'bbb')
557 'OK'
558 >>> r.lrange('l', 0, 1)
559 [u'bbb']
560 >>>
561 """
562 self.connect()
563 value = self._encode(value)
564 self._write('LSET %s %s %s\r\n%s\r\n' % (
565 name, index, len(value), value
566 ))
567 return self.get_response()
568
569 def lrem(self, name, value, num=0):
570 """
571 >>> r = Redis(db=9)
572 >>> r.delete('l')
573 1
574 >>> r.push('l', 'aaa')
575 'OK'
576 >>> r.push('l', 'bbb')
577 'OK'
578 >>> r.push('l', 'aaa')
579 'OK'
580 >>> r.lrem('l', 'aaa')
581 2
582 >>> r.lrange('l', 0, 10)
583 [u'bbb']
584 >>> r.push('l', 'aaa')
585 'OK'
586 >>> r.push('l', 'aaa')
587 'OK'
588 >>> r.lrem('l', 'aaa', 1)
589 1
590 >>> r.lrem('l', 'aaa', 1)
591 1
592 >>> r.lrem('l', 'aaa', 1)
593 0
594 >>>
595 """
596 self.connect()
597 value = self._encode(value)
598 self._write('LREM %s %s %s\r\n%s\r\n' % (
599 name, num, len(value), value
600 ))
601 return self.get_response()
602
603 def sort(self, name, by=None, get=None, start=None, num=None, desc=False, alpha=False):
604 """
605 >>> r = Redis(db=9)
606 >>> r.delete('l')
607 1
608 >>> r.push('l', 'ccc')
609 'OK'
610 >>> r.push('l', 'aaa')
611 'OK'
612 >>> r.push('l', 'ddd')
613 'OK'
614 >>> r.push('l', 'bbb')
615 'OK'
616 >>> r.sort('l', alpha=True)
617 [u'aaa', u'bbb', u'ccc', u'ddd']
618 >>> r.delete('l')
619 1
620 >>> for i in range(1, 5):
621 ... res = r.push('l', 1.0 / i)
622 >>> r.sort('l')
623 [Decimal("0.25"), Decimal("0.333333333333"), Decimal("0.5"), Decimal("1.0")]
624 >>> r.sort('l', desc=True)
625 [Decimal("1.0"), Decimal("0.5"), Decimal("0.333333333333"), Decimal("0.25")]
626 >>> r.sort('l', desc=True, start=2, num=1)
627 [Decimal("0.333333333333")]
628 >>> r.set('weight_0.5', 10)
629 'OK'
630 >>> r.sort('l', desc=True, by='weight_*')
631 [Decimal("0.5"), Decimal("1.0"), Decimal("0.333333333333"), Decimal("0.25")]
632 >>> for i in r.sort('l', desc=True):
633 ... res = r.set('test_%s' % i, 100 - float(i))
634 >>> r.sort('l', desc=True, get='test_*')
635 [Decimal("99.0"), Decimal("99.5"), Decimal("99.6666666667"), Decimal("99.75")]
636 >>> r.sort('l', desc=True, by='weight_*', get='test_*')
637 [Decimal("99.5"), Decimal("99.0"), Decimal("99.6666666667"), Decimal("99.75")]
638 >>> r.sort('l', desc=True, by='weight_*', get='missing_*')
639 [None, None, None, None]
640 >>>
641 """
642 stmt = ['SORT', name]
643 if by:
644 stmt.append("BY %s" % by)
645 if start and num:
646 stmt.append("LIMIT %s %s" % (start, num))
647 if get is None:
648 pass
649 elif isinstance(get, basestring):
650 stmt.append("GET %s" % get)
651 elif isinstance(get, list) or isinstance(get, tuple):
652 for g in get:
653 stmt.append("GET %s" % g)
654 else:
655 raise RedisError("Invalid parameter 'get' for Redis sort")
656 if desc:
657 stmt.append("DESC")
658 if alpha:
659 stmt.append("ALPHA")
660 self.connect()
661 self._write(' '.join(stmt + ["\r\n"]))
662 return self.get_response()
663
664 def sadd(self, name, value):
665 """
666 >>> r = Redis(db=9)
667 >>> res = r.delete('s')
668 >>> r.sadd('s', 'a')
669 1
670 >>> r.sadd('s', 'b')
671 1
672 >>>
673 """
674 self.connect()
675 value = self._encode(value)
676 self._write('SADD %s %s\r\n%s\r\n' % (
677 name, len(value), value
678 ))
679 return self.get_response()
680
681 def srem(self, name, value):
682 """
683 >>> r = Redis(db=9)
684 >>> r.delete('s')
685 1
686 >>> r.srem('s', 'aaa')
687 0
688 >>> r.sadd('s', 'b')
689 1
690 >>> r.srem('s', 'b')
691 1
692 >>> r.sismember('s', 'b')
693 0
694 >>>
695 """
696 self.connect()
697 value = self._encode(value)
698 self._write('SREM %s %s\r\n%s\r\n' % (
699 name, len(value), value
700 ))
701 return self.get_response()
702
703 def sismember(self, name, value):
704 """
705 >>> r = Redis(db=9)
706 >>> r.delete('s')
707 1
708 >>> r.sismember('s', 'b')
709 0
710 >>> r.sadd('s', 'a')
711 1
712 >>> r.sismember('s', 'b')
713 0
714 >>> r.sismember('s', 'a')
715 1
716 >>>
717 """
718 self.connect()
719 value = self._encode(value)
720 self._write('SISMEMBER %s %s\r\n%s\r\n' % (
721 name, len(value), value
722 ))
723 return self.get_response()
724
725 def sinter(self, *args):
726 """
727 >>> r = Redis(db=9)
728 >>> res = r.delete('s1')
729 >>> res = r.delete('s2')
730 >>> res = r.delete('s3')
731 >>> r.sadd('s1', 'a')
732 1
733 >>> r.sadd('s2', 'a')
734 1
735 >>> r.sadd('s3', 'b')
736 1
737 >>> try:
738 ... r.sinter()
739 ... except ResponseError, e:
740 ... print e
741 wrong number of arguments
742 >>> try:
743 ... r.sinter('l')
744 ... except ResponseError, e:
745 ... print e
746 Operation against a key holding the wrong kind of value
747 >>> r.sinter('s1', 's2', 's3')
748 set([])
749 >>> r.sinter('s1', 's2')
750 set([u'a'])
751 >>>
752 """
753 self.connect()
754 self._write('SINTER %s\r\n' % ' '.join(args))
755 return set(self.get_response())
756
757 def sinterstore(self, dest, *args):
758 """
759 >>> r = Redis(db=9)
760 >>> res = r.delete('s1')
761 >>> res = r.delete('s2')
762 >>> res = r.delete('s3')
763 >>> r.sadd('s1', 'a')
764 1
765 >>> r.sadd('s2', 'a')
766 1
767 >>> r.sadd('s3', 'b')
768 1
769 >>> r.sinterstore('s_s', 's1', 's2', 's3')
770 0
771 >>> r.sinterstore('s_s', 's1', 's2')
772 1
773 >>> r.smembers('s_s')
774 set([u'a'])
775 >>>
776 """
777 self.connect()
778 self._write('SINTERSTORE %s %s\r\n' % (dest, ' '.join(args)))
779 return self.get_response()
780
781 def smembers(self, name):
782 """
783 >>> r = Redis(db=9)
784 >>> r.delete('s')
785 1
786 >>> r.sadd('s', 'a')
787 1
788 >>> r.sadd('s', 'b')
789 1
790 >>> try:
791 ... r.smembers('l')
792 ... except ResponseError, e:
793 ... print e
794 Operation against a key holding the wrong kind of value
795 >>> r.smembers('s')
796 set([u'a', u'b'])
797 >>>
798 """
799 self.connect()
800 self._write('SMEMBERS %s\r\n' % name)
801 return set(self.get_response())
802
803 def sunion(self, *args):
804 """
805 >>> r = Redis(db=9)
806 >>> res = r.delete('s1')
807 >>> res = r.delete('s2')
808 >>> res = r.delete('s3')
809 >>> r.sadd('s1', 'a')
810 1
811 >>> r.sadd('s2', 'a')
812 1
813 >>> r.sadd('s3', 'b')
814 1
815 >>> r.sunion('s1', 's2', 's3')
816 set([u'a', u'b'])
817 >>> r.sadd('s2', 'c')
818 1
819 >>> r.sunion('s1', 's2', 's3')
820 set([u'a', u'c', u'b'])
821 >>>
822 """
823 self.connect()
824 self._write('SUNION %s\r\n' % ' '.join(args))
825 return set(self.get_response())
826
827 def sunionstore(self, dest, *args):
828 """
829 >>> r = Redis(db=9)
830 >>> res = r.delete('s1')
831 >>> res = r.delete('s2')
832 >>> res = r.delete('s3')
833 >>> r.sadd('s1', 'a')
834 1
835 >>> r.sadd('s2', 'a')
836 1
837 >>> r.sadd('s3', 'b')
838 1
839 >>> r.sunionstore('s4', 's1', 's2', 's3')
840 2
841 >>> r.smembers('s4')
842 set([u'a', u'b'])
843 >>>
844 """
845 self.connect()
846 self._write('SUNIONSTORE %s %s\r\n' % (dest, ' '.join(args)))
847 return self.get_response()
848
849 def select(self, db):
850 """
851 >>> r = Redis(db=9)
852 >>> r.delete('a')
853 1
854 >>> r.select(10)
855 'OK'
856 >>> r.set('a', 1)
857 'OK'
858 >>> r.select(9)
859 'OK'
860 >>> r.get('a')
861 >>>
862 """
863 self.connect()
864 self._write('SELECT %s\r\n' % db)
865 return self.get_response()
866
867 def move(self, name, db):
868 """
869 >>> r = Redis(db=9)
870 >>> r.set('a', 'a')
871 'OK'
872 >>> r.select(10)
873 'OK'
874 >>> if r.get('a'):
875 ... r.delete('a')
876 ... else:
877 ... print 1
878 1
879 >>> r.select(9)
880 'OK'
881 >>> r.move('a', 10)
882 1
883 >>> r.get('a')
884 >>> r.select(10)
885 'OK'
886 >>> r.get('a')
887 u'a'
888 >>> r.select(9)
889 'OK'
890 >>>
891 """
892 self.connect()
893 self._write('MOVE %s %s\r\n' % (name, db))
894 return self.get_response()
895
896 def save(self, background=False):
897 """
898 >>> r = Redis(db=9)
899 >>> r.save()
900 'OK'
901 >>> try:
902 ... resp = r.save(background=True)
903 ... except ResponseError, e:
904 ... assert str(e) == 'background save already in progress', str(e)
905 ... else:
906 ... assert resp == 'OK'
907 >>>
908 """
909 self.connect()
910 if background:
911 self._write('BGSAVE\r\n')
912 else:
913 self._write('SAVE\r\n')
914 return self.get_response()
915
916 def lastsave(self):
917 """
918 >>> import time
919 >>> r = Redis(db=9)
920 >>> t = int(time.time())
921 >>> r.save()
922 'OK'
923 >>> r.lastsave() >= t
924 True
925 >>>
926 """
927 self.connect()
928 self._write('LASTSAVE\r\n')
929 return self.get_response()
930
931 def flush(self, all_dbs=False):
932 """
933 >>> r = Redis(db=9)
934 >>> r.flush()
935 'OK'
936 >>> # r.flush(all_dbs=True)
937 >>>
938 """
939 self.connect()
940 self._write('%s\r\n' % ('FLUSHALL' if all_dbs else 'FLUSHDB'))
941 return self.get_response()
942
943 def info(self):
944 """
945 >>> r = Redis(db=9)
946 >>> info = r.info()
947 >>> info and isinstance(info, dict)
948 True
949 >>> isinstance(info.get('connected_clients'), int)
950 True
951 >>>
952 """
953 self.connect()
954 self._write('INFO\r\n')
955 info = dict()
956 for l in self.get_response().split('\r\n'):
957 if not l:
958 continue
959 k, v = l.split(':', 1)
960 info[k] = int(v) if v.isdigit() else v
961 return info
962
963 def auth(self, passwd):
964 self.connect()
965 self._write('AUTH %s\r\n' % passwd)
966 return self.get_response()
967
968 def get_response(self):
969 data = self._read().strip()
970 if not data:
971 self.disconnect()
972 raise ConnectionError("Socket closed on remote end")
973 c = data[0]
974 if c == '-':
975 raise ResponseError(data[5:] if data[:5] == '-ERR ' else data[1:])
976 if c == '+':
977 return data[1:]
978 if c == '*':
979 try:
980 num = int(data[1:])
981 except (TypeError, ValueError):
982 raise InvalidResponse("Cannot convert multi-response header '%s' to integer" % data)
983 result = list()
984 for i in range(num):
985 result.append(self._get_value())
986 return result
987 return self._get_value(data)
988
989 def _get_value(self, data=None):
990 data = data or self._read().strip()
991 if data == '$-1':
992 return None
993 try:
994 c, i = data[0], (int(data[1:]) if data.find('.') == -1 else float(data[1:]))
995 except ValueError:
996 raise InvalidResponse("Cannot convert data '%s' to integer" % data)
997 if c == ':':
998 return i
999 if c != '$':
1000 raise InvalidResponse("Unkown response prefix for '%s'" % data)
1001 buf = []
1002 while True:
1003 data = self._read()
1004 i -= len(data)
1005 buf.append(data)
1006 if i < 0:
1007 break
1008 data = ''.join(buf)[:-2]
1009 try:
1010 return int(data) if data.find('.') == -1 else decimal.Decimal(data)
1011 except (ValueError, decimal.InvalidOperation):
1012 return data.decode(self.charset)
1013
1014 def disconnect(self):
1015 if isinstance(self._sock, socket.socket):
1016 try:
1017 self._sock.close()
1018 except socket.error:
1019 pass
1020 self._sock = None
1021 self._fp = None
1022
1023 def connect(self):
1024 """
1025 >>> r = Redis(db=9)
1026 >>> r.connect()
1027 >>> isinstance(r._sock, socket.socket)
1028 True
1029 >>> r.disconnect()
1030 >>>
1031 """
1032 if isinstance(self._sock, socket.socket):
1033 return
1034 try:
1035 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1036 sock.connect((self.host, self.port))
1037 except socket.error, e:
1038 raise ConnectionError("Error %s connecting to %s:%s. %s." % (e.args[0], self.host, self.port, e.args[1]))
1039 else:
1040 self._sock = sock
1041 self._fp = self._sock.makefile('r')
1042 if self.db:
1043 self.select(self.db)
1044 if self.nodelay is not None:
1045 self._sock.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, self.nodelay)
1046
1047
1048 if __name__ == '__main__':
1049 import doctest
1050 doctest.testmod()
1051