/client-libraries/python/redis.py

http://redis.googlecode.com/ · Python · 930 lines · 881 code · 24 blank · 25 comment · 34 complexity · 314fccd652d2b0d68615e677818dbb70 MD5 · raw file

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