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