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