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