Browse files

initial commit

  • Loading branch information...
0 parents commit b94dd404d9b75e8afdcec66c4ff2e8002dbd00ae @salimane committed Sep 7, 2012
Showing with 2,140 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +3 −0 HISTORY.md
  3. +6 −0 INSTALL
  4. +22 −0 LICENSE
  5. +3 −0 MANIFEST.in
  6. +60 −0 README.md
  7. +1 −0 files.txt
  8. +16 −0 rediscluster/.travis.yml
  9. +19 −0 rediscluster/__init__.py
  10. +50 −0 rediscluster/_compat.py
  11. +177 −0 rediscluster/client.py
  12. +35 −0 rediscluster/exceptions.py
  13. +2 −0 requirements.txt
  14. +9 −0 run_tests
  15. +49 −0 setup.py
  16. +15 −0 tests/__init__.py
  17. +17 −0 tests/config.py
  18. +1,651 −0 tests/server_commands.py
5 .gitignore
@@ -0,0 +1,5 @@
+*.pyc
+redis.egg-info
+build/
+dist/
+dump.rdb
3 HISTORY.md
@@ -0,0 +1,3 @@
+## 0.1.0 (2012-09-07)
+
+* First release.
6 INSTALL
@@ -0,0 +1,6 @@
+
+Please use
+ python setup.py install
+
+and report errors to Salimane Adjao Moustapha (me@salimane.com)
+
22 LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Salimane Adjao Moustapha
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
3 MANIFEST.in
@@ -0,0 +1,3 @@
+include INSTALL
+include LICENSE
+include README.md
60 README.md
@@ -0,0 +1,60 @@
+rediscluster-py - a Python interface to a Cluster of Redis key-value store.
+===========================================================================
+
+## Installation
+
+ $ sudo pip install rediscluster
+
+or alternatively (you really should be using pip though):
+
+ $ sudo easy_install rediscluster
+
+From source:
+
+ $ sudo python setup.py install
+
+## Running Tests
+
+ $ git clone https://github.com/salimane/rediscluster-py.git
+ $ cd rediscluster-py
+ $ vi tests/config.py
+ $ ./run_tests
+
+
+
+## Getting Started
+
+ >>> import rediscluster
+ >>> cluster = {
+ ... 'nodes' : {
+ ... 'node_1' : {'host' : '127.0.0.1', 'port' : 63791},
+ ... 'node_2' : {'host' : '127.0.0.1', 'port' : 63792},
+ ... 'node_5' : {'host' : '127.0.0.1', 'port' : 63795},
+ ... 'node_6' : {'host' : '127.0.0.1', 'port' : 63796}
+ ... },
+ ... 'master_of' : {
+ ... 'node_1' : 'node_6', #node_6 slaveof node_1 in redis6.conf
+ ... 'node_2' : 'node_5' #node_5 slaveof node_2 in redis5.conf
+ ... },
+ ... 'default_node' : 'node_1'
+ ... }
+ >>> r = rediscluster.StrictRedis(cluster=cluster, db=0)
+ >>> r.set('foo', 'bar')
+ True
+ >>> r.get('foo')
+ 'bar'
+
+
+## Information
+
+* Code: `git clone git://github.com/salimane/rediscluster-py.git`
+* Home: <http://github.com/salimane/rediscluster-py>
+* Bugs: <http://github.com/salimane/rediscluster-py/issues>
+
+
+Author
+------
+
+rediscluster-py is developed and maintained by Salimane Adjao Moustapha (me@salimane.com).
+It can be found here: http://github.com/salimane/rediscluster-py
+
1 files.txt
@@ -0,0 +1 @@
+/usr/local/lib/python2.7/dist-packages/rediscluster-0.1.0-py2.7.egg
16 rediscluster/.travis.yml
@@ -0,0 +1,16 @@
+language: python
+python:
+ - "2.6"
+ - "2.7"
+ - "2.5"
+ - "2.6"
+ - "2.7"
+ - "3"
+ - "3.2"
+ - "3.3"
+# - "pypy"
+# command to install dependencies
+install:
+ - pip install -r requirements.txt --use-mirrors
+# command to run tests
+script: run_tests
19 rediscluster/__init__.py
@@ -0,0 +1,19 @@
+from rediscluster.client import StrictRedis
+from redis.exceptions import (
+ AuthenticationError,
+ ConnectionError,
+ DataError,
+ InvalidResponse,
+ PubSubError,
+ RedisError,
+ ResponseError,
+ WatchError,
+)
+
+__version__ = '0.1.0'
+VERSION = tuple(map(int, __version__.split('.')))
+
+__all__ = [
+ 'StrictRedis', 'RedisError', 'ConnectionError', 'ResponseError', 'AuthenticationError',
+ 'InvalidResponse', 'DataError', 'PubSubError', 'WatchError',
+]
50 rediscluster/_compat.py
@@ -0,0 +1,50 @@
+"""Internal module for Python 2 backwards compatibility."""
+import sys
+
+
+if sys.version_info[0] < 3:
+ from urlparse import urlparse
+ from itertools import imap, izip
+ from string import letters as ascii_letters
+ try:
+ from cStringIO import StringIO as BytesIO
+ except ImportError:
+ from StringIO import StringIO as BytesIO
+
+ iteritems = lambda x: x.iteritems()
+ dictkeys = lambda x: x.keys()
+ dictvalues = lambda x: x.values()
+ nativestr = lambda x: \
+ x if isinstance(x, str) else x.encode('utf-8', 'replace')
+ u = lambda x: x.decode()
+ b = lambda x: x
+ next = lambda x: x.next()
+ byte_to_chr = lambda x: x
+ unichr = unichr
+ xrange = xrange
+ basestring = basestring
+ unicode = unicode
+ bytes = str
+ long = long
+else:
+ from urllib.parse import urlparse
+ from io import BytesIO
+ from string import ascii_letters
+
+ iteritems = lambda x: x.items()
+ dictkeys = lambda x: list(x.keys())
+ dictvalues = lambda x: list(x.values())
+ byte_to_chr = lambda x: chr(x)
+ nativestr = lambda x: \
+ x if isinstance(x, str) else x.decode('utf-8', 'replace')
+ u = lambda x: x
+ b = lambda x: x.encode('iso-8859-1')
+ next = next
+ unichr = chr
+ imap = map
+ izip = zip
+ xrange = range
+ basestring = str
+ unicode = str
+ bytes = bytes
+ long = int
177 rediscluster/client.py
@@ -0,0 +1,177 @@
+# -*- coding: UTF-8 -*-
+import redis
+import binascii
+
+class StrictRedis:
+ """
+ Implementation of the Redis Cluster Client using redis.StrictRedis
+
+ This abstract class provides a Python interface to all Redis commands on the cluster of redis servers.
+ and implementing how the commands are sent to and received from the cluster.
+
+ """
+
+ read_keys = {
+ 'debug' : 'debug', 'object' : 'object', 'getbit' : 'getbit',
+ 'get' : 'get', 'getrange' : 'getrange', 'hget' : 'hget',
+ 'hgetall' : 'hgetall', 'hkeys' : 'hkeys', 'hlen' : 'hlen', 'hmget' : 'hmget',
+ 'hvals' : 'hvals', 'lindex' : 'lindex', 'llen' : 'llen',
+ 'lrange' : 'lrange', 'object' : 'object',
+ 'scard' : 'scard', 'sismember' : 'sismember', 'smembers' : 'smembers',
+ 'srandmember' : 'srandmember', 'strlen' : 'strlen', 'type' : 'type',
+ 'zcard' : 'zcard', 'zcount' : 'zcount', 'zrange' : 'zrange', 'zrangebyscore' : 'zrangebyscore',
+ 'zrank' : 'zrank', 'zrevrange' : 'zrevrange', 'zrevrangebyscore' : 'zrevrangebyscore',
+ 'zrevrank' : 'zrevrank', 'zscore' : 'zscore',
+ 'mget' : 'mget', 'bitcount' : 'bitcount'
+ }
+
+ write_keys = {
+ 'append' : 'append', 'blpop' : 'blpop', 'brpop' : 'brpop', 'brpoplpush' : 'brpoplpush',
+ 'decr' : 'decr', 'decrby' : 'decrby', 'del' : 'del', 'exists' : 'exists', 'hexists' : 'hexists',
+ 'expire' : 'expire', 'expireat' : 'expireat', 'getset' : 'getset', 'hdel' : 'hdel',
+ 'hincrby' : 'hincrby', 'hset' : 'hset', 'hsetnx' : 'hsetnx', 'hmset' : 'hmset',
+ 'incr' : 'incr', 'incrby' : 'incrby', 'linsert' : 'linsert', 'lpop' : 'lpop',
+ 'lpush' : 'lpush', 'lpushx' : 'lpushx', 'lrem' : 'lrem', 'lset' : 'lset',
+ 'ltrim' : 'ltrim', 'move' : 'move',
+ 'persist' : 'persist', 'publish' : 'publish', 'psubscribe' : 'psubscribe', 'punsubscribe' : 'punsubscribe',
+ 'rpop' : 'rpop', 'rpoplpush' : 'rpoplpush', 'rpush' : 'rpush',
+ 'rpushx' : 'rpushx', 'sadd' : 'sadd', 'sdiff' : 'sdiff', 'sdiffstore' : 'sdiffstore',
+ 'set' : 'set', 'setbit' : 'setbit', 'setex' : 'setex', 'setnx' : 'setnx',
+ 'setrange' : 'setrange', 'sinter' : 'sinter', 'sinterstore' : 'sinterstore', 'smove' : 'smove',
+ 'sort' : 'sort', 'spop' : 'spop', 'srem' : 'srem', 'subscribe' : 'subscribe',
+ 'sunion' : 'sunion', 'sunionstore' : 'sunionstore', 'unsubscribe' : 'unsubscribe', 'unwatch' : 'unwatch',
+ 'watch' : 'watch', 'zadd' : 'zadd', 'zincrby' : 'zincrby', 'zinterstore' : 'zinterstore',
+ 'zrem' : 'zrem', 'zremrangebyrank' : 'zremrangebyrank', 'zremrangebyscore' : 'zremrangebyscore', 'zunionstore' : 'zunionstore',
+ 'mset' : 'mset','msetnx' : 'msetnx', 'rename' : 'rename', 'renamenx' : 'renamenx',
+ 'del' : 'del', 'delete' : 'delete', 'ttl' : 'ttl', 'flushall' : 'flushall', 'flushdb' : 'flushdb',
+ }
+
+ dont_hash = {
+ 'auth' : 'auth', 'config' : 'config',
+ 'monitor' : 'monitor', 'quit' : 'quit',
+ 'randomkey' : 'randomkey', 'shutdown' : 'shutdown',
+ 'slaveof' : 'slaveof', 'slowlog' : 'slowlog', 'sync' : 'sync',
+ 'flushall' : 'flushall', 'flushdb' : 'flushdb',
+ 'discard' : 'discard', 'echo' : 'echo', 'exec' : 'exec', 'multi' : 'multi',
+ 'config_set' : 'config_set', 'config_get' : 'config_get'
+ }
+
+ tag_keys = {
+ 'sinter' : 'sinter', 'sdiff' : 'sdiff', 'sdiffstore' : 'sdiffstore', 'sinterstore' : 'sinterstore',
+ 'smove' : 'smove', 'sunion' : 'sunion', 'sunionstore' : 'sunionstore',
+ 'zinterstore' : 'zinterstore', 'zunionstore' : 'zunionstore'
+ }
+
+ banned_keys = {
+ 'mget' : 'mget',
+ 'mset' : 'mset', 'msetnx' : 'msetnx', 'rename' : 'rename', 'renamenx' : 'renamenx',
+ }
+
+ loop_keys = {
+ 'keys' : 'keys',
+ 'save' : 'save', 'bgsave' : 'bgsave',
+ 'bgrewriteaof' : 'bgrewriteaof',
+ 'dbsize' : 'dbsize', 'info' : 'info',
+ 'lastsave' : 'lastsave', 'ping' : 'ping',
+ 'flushall' : 'flushall', 'flushdb' : 'flushdb',
+ 'randomkey' : 'randomkey', 'sync' : 'sync',
+ }
+
+ def __init__(self, cluster = {}, db = 0):
+ #die when wrong server array
+ if 'nodes' not in cluster or 'master_of' not in cluster:
+ raise Exception("rediscluster: Please set a correct array of redis cluster.")
+
+ self.cluster = cluster
+ self.no_servers = len(cluster['master_of'])
+ slaves = cluster['master_of'].values()
+ self.redises = {}
+ #connect to all servers
+ for alias, server in cluster['nodes'].iteritems():
+ try:
+ self.__redis = redis.StrictRedis(host=server['host'], port=server['port'], db=db)
+ sla = self.__redis.config_get('slaveof')['slaveof']
+ if alias in slaves and sla == '':
+ raise Exception("rediscluster: server %s:%s is not a slave." % (server['host'], server['port']))
+ except Exception as e:
+ try:
+ self.__redis = redis.StrictRedis(host=server['host'], port=server['port'], db=db)
+ sla = self.__redis.config_get('slaveof')['slaveof']
+ if alias in slaves and sla == '':
+ raise Exception("rediscluster: server %s:%s is not a slave." % (server['host'], server['port']))
+ except Exception as e:
+ #if node is slave and is down, replace its connection with its master's
+ try:
+ ms = [k for k, v in cluster['master_of'].iteritems() if v == alias and sla != ''][0]
+ except IndexError as ie:
+ ms = None
+
+ if ms is not None:
+ try:
+ self.__redis = redis.StrictRedis(host=cluster['nodes'][ms]['host'], port=cluster['nodes'][ms]['port'], db=db)
+ self.__redis.info()
+ except Exception as e:
+ try:
+ self.__redis = redis.StrictRedis(host=cluster['nodes'][ms]['host'], port=cluster['nodes'][ms]['port'], db=db)
+ self.__redis.info()
+ except Exception as e:
+ raise Exception("rediscluster cannot connect to: %s:%s %s" % (cluster['nodes'][ms]['host'], cluster['nodes'][ms]['port'], e))
+
+ else:
+ raise Exception("rediscluster cannot connect to: %s:%s %s" % (server['host'], server['port'], e))
+
+ self.redises[alias] = self.__redis
+
+
+ def __getattr__(self, name, *args, **kwargs):
+ """
+ Magic method to handle all redis commands
+ - string name The name of the command called.
+ - tuple args of supplied arguments to the command.
+ """
+ def function(*args, **kwargs):
+ if name not in StrictRedis.loop_keys:
+ #trigger error msg on banned keys unless u're using it with tagged keys
+ if name in StrictRedis.banned_keys and not isinstance(args[0], list) :
+ raise Exception("rediscluster: Command %s Not Supported (each key name has its own node)" % name)
+
+ #get the hash key depending on tags or not
+ hkey = args[0]
+ #take care of tagged key names for forcing multiple keys on the same node, e.g. r.set(['userinfo', "age:uid"], value)
+ if isinstance(args[0], list) :
+ hkey = args[0][0]
+ args[0] = args[0][1]
+
+ #get the node number
+ node = str((abs(binascii.crc32(hkey) & 0xffffffff) % self.no_servers) + 1)
+ redisent = self.redises[self.cluster['default_node']]
+ if name in StrictRedis.write_keys:
+ redisent = self.redises['node_' + node]
+ elif name in StrictRedis.read_keys:
+ redisent = self.redises[self.cluster['master_of']['node_' + node]]
+
+ #Execute the command on the server
+ return getattr(redisent, name)(*args, **kwargs)
+
+ else:
+ result = {}
+ for alias, redisent in self.redises.iteritems():
+ if name in StrictRedis.write_keys and alias not in self.cluster['master_of']:
+ res = None
+ else:
+ res = getattr(redisent, name)(*args, **kwargs)
+
+ if name == 'keys':
+ result.append(res)
+ else:
+ result[alias] = res
+
+ return result
+
+ return function
+
+
+
+
+
+
35 rediscluster/exceptions.py
@@ -0,0 +1,35 @@
+"Core exceptions raised by the rediscluster client"
+
+from redis import (RedisError, AuthenticationError, ConnectionError, ResponseError,
+ InvalidResponse, DataError, PubSubError, WatchError)
+
+class RedisError(RedisError):
+ pass
+
+
+class AuthenticationError(AuthenticationError):
+ pass
+
+
+class ConnectionError(ConnectionError):
+ pass
+
+
+class ResponseError(ResponseError):
+ pass
+
+
+class InvalidResponse(InvalidResponse):
+ pass
+
+
+class DataError(DataError):
+ pass
+
+
+class PubSubError(PubSubError):
+ pass
+
+
+class WatchError(WatchError):
+ pass
2 requirements.txt
@@ -0,0 +1,2 @@
+redis>=2.4.12
+hiredis
9 run_tests
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+import unittest
+from tests import all_tests
+
+
+if __name__ == "__main__":
+ tests = all_tests()
+ results = unittest.TextTestRunner().run(tests)
49 setup.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python -tt
+import os
+
+from rediscluster import __version__
+
+try:
+ from setuptools import setup
+except ImportError:
+ from distutils.core import setup
+
+f = open(os.path.join(os.path.dirname(__file__), 'README.md'))
+long_description = f.read()
+f.close()
+
+setup(
+ name='rediscluster',
+ version=__version__,
+ description='Python client for Cluster of Redis key-value store',
+ long_description=long_description,
+ url='http://github.com/salimane/rediscluster-py',
+ download_url=('http://cloud.github.com/downloads/salimane/'
+ 'rediscluster-py/rediscluster-%s.tar.gz' % __version__),
+ install_requires=[
+ 'redis>=2.4.0',
+ 'hiredis',
+ ],
+ author='Salimane Adjao Moustapha',
+ author_email='me@salimane.com',
+ maintainer='Salimane Adjao Moustapha',
+ maintainer_email='me@salimane.com',
+ keywords=['Redis Cluster', 'Redis', 'cluster of key-value store'],
+ license='MIT',
+ packages=['rediscluster'],
+ test_suite='tests.all_tests',
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ ]
+)
15 tests/__init__.py
@@ -0,0 +1,15 @@
+import unittest
+
+from tests.server_commands import ServerCommandsTestCase
+
+
+try:
+ import hiredis
+ use_hiredis = True
+except ImportError:
+ use_hiredis = False
+
+def all_tests():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(ServerCommandsTestCase))
+ return suite
17 tests/config.py
@@ -0,0 +1,17 @@
+cluster = {
+ # node names
+ 'nodes' : {
+ 'node_1' : {'host' : '127.0.0.1', 'port' : 63791},
+ 'node_2' : {'host' : '127.0.0.1', 'port' : 63792},
+
+ 'node_5' : {'host' : '127.0.0.1', 'port' : 63795},
+ 'node_6' : {'host' : '127.0.0.1', 'port' : 63796},
+ },
+ # replication information
+ 'master_of' : {
+ 'node_1' : 'node_6', #node_6 slaveof node_1 in redis6.conf
+ 'node_2' : 'node_5', #node_5 slaveof node_2 in redis5.conf
+ },
+
+ 'default_node' : 'node_1'
+ }
1,651 tests/server_commands.py
@@ -0,0 +1,1651 @@
+from distutils.version import StrictVersion
+import unittest
+import datetime
+import time
+import config
+import binascii
+
+from rediscluster._compat import (unichr, u, b, ascii_letters, iteritems, dictkeys,
+ dictvalues)
+
+import rediscluster
+
+class ServerCommandsTestCase(unittest.TestCase):
+ def get_client(self, cls=rediscluster.StrictRedis):
+ return cls(cluster=config.cluster, db=4)
+
+ def setUp(self):
+ self.client = self.get_client()
+ self.client.flushdb()
+
+ def tearDown(self):
+ self.client.flushdb()
+ #self.client.connection_pool.disconnect()
+
+ # GENERAL SERVER COMMANDS
+ def test_dbsize(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client.set('a', 'foo')
+ self.client.set('b', 'foo')
+ self.assertEquals(self.client.dbsize(), 2)
+
+ def test_get_and_set(self):
+ # get and set can't be tested independently of each other
+ client = self.get_client()
+ self.assertEquals(client.get('a'), None)
+ byte_string = b('value')
+ integer = 5
+ unicode_string = unichr(3456) + u('abcd') + unichr(3421)
+ self.assert_(client.set('byte_string', byte_string))
+ self.assert_(client.set('integer', 5))
+ self.assert_(client.set('unicode_string', unicode_string))
+ self.assertEquals(client.get('byte_string'), byte_string)
+ self.assertEquals(client.get('integer'), b(str(integer)))
+ self.assertEquals(
+ client.get('unicode_string').decode('utf-8'),
+ unicode_string)
+
+ def test_getitem_and_setitem(self):
+ self.client.set('a', 'bar')
+ self.assertEquals(self.client.get('a'), b('bar'))
+ self.assertRaises(KeyError, self.client.__getitem__, 'b')
+
+ def test_delete(self):
+ self.assertEquals(self.client.delete('a'), False)
+ self.client.set('a', 'foo')
+ self.assertEquals(self.client.delete('a'), True)
+
+ def test_delitem(self):
+ self.client.set('a', 'foo')
+ self.client.delete('a')
+ self.assertEquals(self.client.get('a'), None)
+
+ def test_config_get(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ data = self.client.config_get()
+ self.assert_('maxmemory' in data)
+ self.assert_(data['maxmemory'].isdigit())
+
+ def test_config_set(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ data = self.client.config_get()
+ rdbname = data['dbfilename']
+ self.assert_(self.client.config_set('dbfilename', 'redis_py_test.rdb'))
+ self.assertEquals(
+ self.client.config_get()['dbfilename'],
+ 'redis_py_test.rdb'
+ )
+ self.assert_(self.client.config_set('dbfilename', rdbname))
+ self.assertEquals(self.client.config_get()['dbfilename'], rdbname)
+
+ def test_debug_object(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client.set('a', 'foo')
+ debug_info = self.client.debug_object('a')
+ self.assert_(len(debug_info) > 0)
+ self.assertEquals(debug_info['refcount'], 1)
+ self.assert_(debug_info['serializedlength'] > 0)
+ self.client.rpush('b', 'a1')
+ debug_info = self.client.debug_object('a')
+
+ def test_echo(self):
+ self.assertEquals(self.client.echo('foo bar'), b('foo bar'))
+
+ def test_info(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client.set('a', 'foo')
+ self.client.set('b', 'foo')
+ info = self.client.info()
+ self.assert_(isinstance(info, dict))
+ self.assertEquals(info['db9']['keys'], 2)
+
+ def test_lastsave(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.assert_(isinstance(self.client.lastsave(), datetime.datetime))
+
+ def test_object(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client.set('a', 'foo')
+ self.assert_(isinstance(self.client.object('refcount', 'a'), int))
+ self.assert_(isinstance(self.client.object('idletime', 'a'), int))
+ self.assertEquals(self.client.object('encoding', 'a'), b('raw'))
+
+ def test_ping(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.assertEquals(self.client.ping(), True)
+
+ def test_time(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ t = self.client.time()
+ self.assertEquals(len(t), 2)
+ self.assert_(isinstance(t[0], int))
+ self.assert_(isinstance(t[1], int))
+
+ # KEYS
+ def test_append(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # invalid key type
+ self.client.rpush('a', 'a1')
+ self.assertRaises(rediscluster.ResponseError, self.client.append, 'a', 'a1')
+ self.client.delete('a')
+ # real logic
+ self.assertEquals(self.client.append('a', 'a1'), 2)
+ self.assertEquals(self.client.get('a'), b('a1'))
+ self.assert_(self.client.append('a', 'a2'), 4)
+ self.assertEquals(self.client.get('a'), b('a1a2'))
+
+ def test_getrange(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client.set('a', 'foo')
+ self.assertEquals(self.client.getrange('a', 0, 0), b('f'))
+ self.assertEquals(self.client.getrange('a', 0, 2), b('foo'))
+ self.assertEquals(self.client.getrange('a', 3, 4), b(''))
+
+
+ def test_decr(self):
+ self.assertEquals(self.client.decr('a'), -1)
+ self.assertEquals(self.client.get('a'), b('-1'))
+ self.assertEquals(self.client.decr('a'), -2)
+ self.assertEquals(self.client.get('a'), b('-2'))
+ self.assertEquals(self.client.decr('a', amount=5), -7)
+ self.assertEquals(self.client.get('a'), b('-7'))
+
+ def test_exists(self):
+ self.assertEquals(self.client.exists('a'), False)
+ self.client.set('a', 'foo')
+ self.assertEquals(self.client.exists('a'), True)
+
+ def test_expire(self):
+ self.assertEquals(self.client.expire('a', 10), False)
+ self.client.set('a', 'foo')
+ self.assertEquals(self.client.expire('a', 10), True)
+ self.assertEquals(self.client.ttl('a'), 10)
+ self.assertEquals(self.client.persist('a'), True)
+ self.assertEquals(self.client.ttl('a'), -1)
+
+ def test_expireat(self):
+ expire_at = datetime.datetime.now() + datetime.timedelta(minutes=1)
+ self.assertEquals(self.client.expireat('a', expire_at), False)
+ self.client.set('a', 'foo')
+ # expire at in unix time
+ expire_at_seconds = int(time.mktime(expire_at.timetuple()))
+ self.assertEquals(self.client.expireat('a', expire_at_seconds), True)
+ self.assertAlmostEquals(self.client.ttl('a'), 60, delta=2)
+ # expire at given a datetime object
+ self.client.set('b', 'bar')
+ self.assertEquals(self.client.expireat('b', expire_at), True)
+ self.assertAlmostEquals(self.client.ttl('b'), 60, delta=2)
+
+ def test_get_set_bit(self):
+ self.assertEquals(self.client.getbit('a', 5), False)
+ self.assertEquals(self.client.setbit('a', 5, True), False)
+ self.assertEquals(self.client.getbit('a', 5), True)
+ self.assertEquals(self.client.setbit('a', 4, False), False)
+ self.assertEquals(self.client.getbit('a', 4), False)
+ self.assertEquals(self.client.setbit('a', 4, True), False)
+ self.assertEquals(self.client.setbit('a', 5, True), True)
+ self.assertEquals(self.client.getbit('a', 4), True)
+ self.assertEquals(self.client.getbit('a', 5), True)
+
+ def test_bitcount(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ self.client.setbit('a', 5, True)
+ self.assertEquals(self.client.bitcount('a'), 1)
+ self.client.setbit('a', 6, True)
+ self.assertEquals(self.client.bitcount('a'), 2)
+ self.client.setbit('a', 5, False)
+ self.assertEquals(self.client.bitcount('a'), 1)
+ self.client.setbit('a', 9, True)
+ self.client.setbit('a', 17, True)
+ self.client.setbit('a', 25, True)
+ self.client.setbit('a', 33, True)
+ self.assertEquals(self.client.bitcount('a'), 5)
+ self.assertEquals(self.client.bitcount('a', 2, 3), 2)
+ self.assertEquals(self.client.bitcount('a', 2, -1), 3)
+ self.assertEquals(self.client.bitcount('a', -2, -1), 2)
+ self.assertEquals(self.client.bitcount('a', 1, 1), 1)
+
+
+ def test_bitop_not_empty_string(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ self.client.set('a', '')
+ self.client.bitop('not', 'r', 'a')
+ self.assertEquals(self.client.get('r'), None)
+
+ def test_bitop_not(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ test_str = b('\xAA\x00\xFF\x55')
+ correct = ~0xAA00FF55 & 0xFFFFFFFF
+ self.client.set('a', test_str)
+ self.client.bitop('not', 'r', 'a')
+ self.assertEquals(
+ int(binascii.hexlify(self.client.get('r')), 16),
+ correct)
+
+ def test_bitop_not_in_place(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ test_str = b('\xAA\x00\xFF\x55')
+ correct = ~0xAA00FF55 & 0xFFFFFFFF
+ self.client.set('a', test_str)
+ self.client.bitop('not', 'a', 'a')
+ self.assertEquals(
+ int(binascii.hexlify(self.client.get('a')), 16),
+ correct)
+
+ def test_bitop_single_string(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ test_str = b('\x01\x02\xFF')
+ self.client.set('a', test_str)
+ self.client.bitop('and', 'res1', 'a')
+ self.client.bitop('or', 'res2', 'a')
+ self.client.bitop('xor', 'res3', 'a')
+ self.assertEquals(self.client.get('res1'), test_str)
+ self.assertEquals(self.client.get('res2'), test_str)
+ self.assertEquals(self.client.get('res3'), test_str)
+
+ def test_bitop_string_operands(self):
+ for info in self.client.info().itervalues():
+ version = info['redis_version']
+ if StrictVersion(version) < StrictVersion('2.6.0'):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+
+ self.client.set('a', b('\x01\x02\xFF\xFF'))
+ self.client.set('b', b('\x01\x02\xFF'))
+ self.client.bitop('and', 'res1', 'a', 'b')
+ self.client.bitop('or', 'res2', 'a', 'b')
+ self.client.bitop('xor', 'res3', 'a', 'b')
+ self.assertEquals(
+ int(binascii.hexlify(self.client.get('res1')), 16),
+ 0x0102FF00)
+ self.assertEquals(
+ int(binascii.hexlify(self.client.get('res2')), 16),
+ 0x0102FFFF)
+ self.assertEquals(
+ int(binascii.hexlify(self.client.get('res3')), 16),
+ 0x000000FF)
+
+ def test_getset(self):
+ self.assertEquals(self.client.getset('a', 'foo'), None)
+ self.assertEquals(self.client.getset('a', 'bar'), b('foo'))
+
+ def test_incr(self):
+ self.assertEquals(self.client.incr('a'), 1)
+ self.assertEquals(self.client.get('a'), b('1'))
+ self.assertEquals(self.client.incr('a'), 2)
+ self.assertEquals(self.client.get('a'), b('2'))
+ self.assertEquals(self.client.incr('a', amount=5), 7)
+ self.assertEquals(self.client.get('a'), b('7'))
+
+ def test_setex(self):
+ self.assertEquals(self.client.setex('a', 60, '1'), True)
+ self.assertEquals(self.client.get('a'), b('1'))
+ self.assertEquals(self.client.ttl('a'), 60)
+
+ def test_setnx(self):
+ self.assert_(self.client.setnx('a', '1'))
+ self.assertEquals(self.client.get('a'), b('1'))
+ self.assert_(not self.client.setnx('a', '2'))
+ self.assertEquals(self.client.get('a'), b('1'))
+
+ def test_setrange(self):
+ self.assertEquals(self.client.setrange('a', 5, 'abcdef'), 11)
+ self.assertEquals(self.client.get('a'), b('\0\0\0\0\0abcdef'))
+ self.client.set('a', 'Hello World')
+ self.assertEquals(self.client.setrange('a', 6, 'Redis'), 11)
+ self.assertEquals(self.client.get('a'), b('Hello Redis'))
+
+ def test_strlen(self):
+ self.client.set('a', 'abcdef')
+ self.assertEquals(self.client.strlen('a'), 6)
+
+ def test_substr(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # invalid key type
+ self.client.rpush('a', 'a1')
+ self.assertRaises(rediscluster.ResponseError, self.client.substr, 'a', 0)
+ self.client.delete('a')
+ # real logic
+ self.client.set('a', 'abcdefghi')
+ self.assertEquals(self.client.substr('a', 0), b('abcdefghi'))
+ self.assertEquals(self.client.substr('a', 2), b('cdefghi'))
+ self.assertEquals(self.client.substr('a', 3, 5), b('def'))
+ self.assertEquals(self.client.substr('a', 3, -2), b('defgh'))
+ self.client['a'] = 123456 # does substr work with ints?
+ self.assertEquals(self.client.substr('a', 2, -2), b('345'))
+
+ def test_type(self):
+ self.assertEquals(self.client.type('a'), b('none'))
+ self.client.set('a', '1')
+ self.assertEquals(self.client.type('a'), b('string'))
+ self.client.delete('a')
+ self.client.lpush('a', '1')
+ self.assertEquals(self.client.type('a'), b('list'))
+ self.client.delete('a')
+ self.client.sadd('a', '1')
+ self.assertEquals(self.client.type('a'), b('set'))
+ self.client.delete('a')
+ self.client.zadd('a', **{'1': 1})
+ self.assertEquals(self.client.type('a'), b('zset'))
+
+ # LISTS
+ def make_list(self, name, l):
+ for i in l:
+ self.client.rpush(name, i)
+
+ def test_blpop(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', 'ab')
+ self.make_list('b', 'cd')
+ self.assertEquals(
+ self.client.blpop(['b', 'a'], timeout=1),
+ (b('b'), b('c')))
+ self.assertEquals(
+ self.client.blpop(['b', 'a'], timeout=1),
+ (b('b'), b('d')))
+ self.assertEquals(
+ self.client.blpop(['b', 'a'], timeout=1),
+ (b('a'), b('a')))
+ self.assertEquals(
+ self.client.blpop(['b', 'a'], timeout=1),
+ (b('a'), b('b')))
+ self.assertEquals(self.client.blpop(['b', 'a'], timeout=1), None)
+ self.make_list('c', 'a')
+ self.assertEquals(self.client.blpop('c', timeout=1), (b('c'), b('a')))
+
+ def test_brpop(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', 'ab')
+ self.make_list('b', 'cd')
+ self.assertEquals(
+ self.client.brpop(['b', 'a'], timeout=1),
+ (b('b'), b('d')))
+ self.assertEquals(
+ self.client.brpop(['b', 'a'], timeout=1),
+ (b('b'), b('c')))
+ self.assertEquals(
+ self.client.brpop(['b', 'a'], timeout=1),
+ (b('a'), b('b')))
+ self.assertEquals(
+ self.client.brpop(['b', 'a'], timeout=1),
+ (b('a'), b('a')))
+ self.assertEquals(self.client.brpop(['b', 'a'], timeout=1), None)
+ self.make_list('c', 'a')
+ self.assertEquals(self.client.brpop('c', timeout=1), (b('c'), b('a')))
+
+ def test_brpoplpush(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', '12')
+ self.make_list('b', '34')
+ self.assertEquals(self.client.brpoplpush('a', 'b'), b('2'))
+ self.assertEquals(self.client.brpoplpush('a', 'b'), b('1'))
+ self.assertEquals(self.client.brpoplpush('a', 'b', timeout=1), None)
+ self.assertEquals(self.client.lrange('a', 0, -1), [])
+ self.assertEquals(
+ self.client.lrange('b', 0, -1),
+ [b('1'), b('2'), b('3'), b('4')])
+
+ def test_lindex(self):
+ # no key
+ self.assertEquals(self.client.lindex('a', '0'), None)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lindex, 'a', '0')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.lindex('a', '0'), b('a'))
+ self.assertEquals(self.client.lindex('a', '1'), b('b'))
+ self.assertEquals(self.client.lindex('a', '2'), b('c'))
+
+ def test_linsert(self):
+ # no key
+ self.assertEquals(self.client.linsert('a', 'after', 'x', 'y'), 0)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.linsert, 'a', 'after', 'x', 'y'
+ )
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.linsert('a', 'after', 'b', 'b1'), 4)
+ self.assertEquals(
+ self.client.lrange('a', 0, -1),
+ [b('a'), b('b'), b('b1'), b('c')])
+ self.assertEquals(self.client.linsert('a', 'before', 'b', 'a1'), 5)
+ self.assertEquals(
+ self.client.lrange('a', 0, -1),
+ [b('a'), b('a1'), b('b'), b('b1'), b('c')])
+
+ def test_llen(self):
+ # no key
+ self.assertEquals(self.client.llen('a'), 0)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.llen, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.llen('a'), 3)
+
+ def test_lpop(self):
+ # no key
+ self.assertEquals(self.client.lpop('a'), None)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lpop, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.lpop('a'), b('a'))
+ self.assertEquals(self.client.lpop('a'), b('b'))
+ self.assertEquals(self.client.lpop('a'), b('c'))
+ self.assertEquals(self.client.lpop('a'), None)
+
+ def test_lpush(self):
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lpush, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.assertEqual(1, self.client.lpush('a', 'b'))
+ self.assertEqual(2, self.client.lpush('a', 'a'))
+
+ self.assertEquals(self.client.lindex('a', 0), b('a'))
+ self.assertEquals(self.client.lindex('a', 1), b('b'))
+
+ def test_lpushx(self):
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lpushx, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.assertEquals(self.client.lpushx('a', 'b'), 0)
+ self.assertEquals(self.client.lrange('a', 0, -1), [])
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.lpushx('a', 'd'), 4)
+ self.assertEquals(
+ self.client.lrange('a', 0, -1),
+ [b('d'), b('a'), b('b'), b('c')])
+
+ def test_lrange(self):
+ # no key
+ self.assertEquals(self.client.lrange('a', 0, 1), [])
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lrange, 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abcde')
+ self.assertEquals(
+ self.client.lrange('a', 0, 2),
+ [b('a'), b('b'), b('c')])
+ self.assertEquals(
+ self.client.lrange('a', 2, 10),
+ [b('c'), b('d'), b('e')])
+
+ def test_lrem(self):
+ # no key
+ self.assertEquals(self.client.lrem('a', 0, 'foo'), 0)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lrem, 'a', 0, 'b')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'aaaa')
+ self.assertEquals(self.client.lrem('a', 1, 'a'), 1)
+ self.assertEquals(
+ self.client.lrange('a', 0, 3),
+ [b('a'), b('a'), b('a')])
+ self.assertEquals(self.client.lrem('a', 0, 'a'), 3)
+ # remove all the elements in the list means the key is deleted
+ self.assertEquals(self.client.lrange('a', 0, 1), [])
+
+ def test_lset(self):
+ # no key
+ self.assertRaises(rediscluster.ResponseError, self.client.lset, 'a', 1, 'b')
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.lset, 'a', 1, 'b')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(
+ self.client.lrange('a', 0, 2),
+ [b('a'), b('b'), b('c')])
+ self.assert_(self.client.lset('a', 1, 'd'))
+ self.assertEquals(
+ self.client.lrange('a', 0, 2),
+ [b('a'), b('d'), b('c')])
+
+ def test_ltrim(self):
+ # no key -- TODO: Not sure why this is actually true.
+ self.assert_(self.client.ltrim('a', 0, 2))
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.ltrim, 'a', 0, 2)
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assert_(self.client.ltrim('a', 0, 1))
+ self.assertEquals(self.client.lrange('a', 0, 5), [b('a'), b('b')])
+
+ def test_rpop(self):
+ # no key
+ self.assertEquals(self.client.rpop('a'), None)
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.rpop, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.rpop('a'), b('c'))
+ self.assertEquals(self.client.rpop('a'), b('b'))
+ self.assertEquals(self.client.rpop('a'), b('a'))
+ self.assertEquals(self.client.rpop('a'), None)
+
+ def test_rpoplpush(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # no src key
+ self.make_list('b', ['b1'])
+ self.assertEquals(self.client.rpoplpush('a', 'b'), None)
+ # no dest key
+ self.assertEquals(self.client.rpoplpush('b', 'a'), b('b1'))
+ self.assertEquals(self.client.lindex('a', 0), b('b1'))
+ self.client.delete('a')
+ self.client.delete('b')
+ # src key is not a list
+ self.client.set('a', 'a1')
+ self.assertRaises(rediscluster.ResponseError, self.client.rpoplpush, 'a', 'b')
+ self.client.delete('a')
+ # dest key is not a list
+ self.make_list('a', ['a1'])
+ self.client.set('b', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.rpoplpush, 'a', 'b')
+ self.client.delete('a')
+ self.client.delete('b')
+ # real logic
+ self.make_list('a', ['a1', 'a2', 'a3'])
+ self.make_list('b', ['b1', 'b2', 'b3'])
+ self.assertEquals(self.client.rpoplpush('a', 'b'), b('a3'))
+ self.assertEquals(self.client.lrange('a', 0, 2), [b('a1'), b('a2')])
+ self.assertEquals(
+ self.client.lrange('b', 0, 4),
+ [b('a3'), b('b1'), b('b2'), b('b3')])
+
+ def test_rpush(self):
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.rpush, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.assertEqual(1, self.client.rpush('a', 'a'))
+ self.assertEqual(2, self.client.rpush('a', 'b'))
+
+ self.assertEquals(self.client.lindex('a', 0), b('a'))
+ self.assertEquals(self.client.lindex('a', 1), b('b'))
+
+ def test_rpushx(self):
+ # key is not a list
+ self.client.set('a', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.rpushx, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.assertEquals(self.client.rpushx('a', 'b'), 0)
+ self.assertEquals(self.client.lrange('a', 0, -1), [])
+ self.make_list('a', 'abc')
+ self.assertEquals(self.client.rpushx('a', 'd'), 4)
+ self.assertEquals(
+ self.client.lrange('a', 0, -1),
+ [b('a'), b('b'), b('c'), b('d')])
+
+ # Set commands
+ def make_set(self, name, l):
+ for i in l:
+ self.client.sadd(name, i)
+
+ def test_sadd(self):
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.sadd, 'a', 'a1')
+ self.client.delete('a')
+ # real logic
+ members = set([b('a1'), b('a2'), b('a3')])
+ self.make_set('a', members)
+ self.assertEquals(self.client.smembers('a'), members)
+
+ def test_scard(self):
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.scard, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_set('a', 'abc')
+ self.assertEquals(self.client.scard('a'), 3)
+
+ def test_sdiff(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.sdiff, ['a', 'b'])
+ self.client.delete('b')
+ # real logic
+ self.make_set('b', ['b1', 'a2', 'b3'])
+ self.assertEquals(
+ self.client.sdiff(['a', 'b']),
+ set([b('a1'), b('a3')]))
+
+ def test_sdiffstore(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.sdiffstore,
+ 'c', ['a', 'b'])
+ self.client.delete('b')
+ self.make_set('b', ['b1', 'a2', 'b3'])
+ # dest key always gets overwritten, even if it's not a set, so don't
+ # test for that
+ # real logic
+ self.assertEquals(self.client.sdiffstore('c', ['a', 'b']), 2)
+ self.assertEquals(self.client.smembers('c'), set([b('a1'), b('a3')]))
+
+ def test_sinter(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.sinter, ['a', 'b'])
+ self.client.delete('b')
+ # real logic
+ self.make_set('b', ['a1', 'b2', 'a3'])
+ self.assertEquals(
+ self.client.sinter(['a', 'b']),
+ set([b('a1'), b('a3')]))
+
+ def test_sinterstore(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.sinterstore,
+ 'c', ['a', 'b'])
+ self.client.delete('b')
+ self.make_set('b', ['a1', 'b2', 'a3'])
+ # dest key always gets overwritten, even if it's not a set, so don't
+ # test for that
+ # real logic
+ self.assertEquals(self.client.sinterstore('c', ['a', 'b']), 2)
+ self.assertEquals(self.client.smembers('c'), set([b('a1'), b('a3')]))
+
+ def test_sismember(self):
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.sismember, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_set('a', 'abc')
+ self.assertEquals(self.client.sismember('a', 'a'), True)
+ self.assertEquals(self.client.sismember('a', 'b'), True)
+ self.assertEquals(self.client.sismember('a', 'c'), True)
+ self.assertEquals(self.client.sismember('a', 'd'), False)
+
+ def test_smembers(self):
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.smembers, 'a')
+ self.client.delete('a')
+ # set doesn't exist
+ self.assertEquals(self.client.smembers('a'), set())
+ # real logic
+ self.make_set('a', 'abc')
+ self.assertEquals(
+ self.client.smembers('a'),
+ set([b('a'), b('b'), b('c')]))
+
+ def test_smove(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # src key is not set
+ self.make_set('b', ['b1', 'b2'])
+ self.assertEquals(self.client.smove('a', 'b', 'a1'), 0)
+ # src key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.smove,
+ 'a', 'b', 'a1')
+ self.client.delete('a')
+ self.make_set('a', ['a1', 'a2'])
+ # dest key is not a set
+ self.client.delete('b')
+ self.client.set('b', 'b')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.smove,
+ 'a', 'b', 'a1')
+ self.client.delete('b')
+ self.make_set('b', ['b1', 'b2'])
+ # real logic
+ self.assert_(self.client.smove('a', 'b', 'a1'))
+ self.assertEquals(self.client.smembers('a'), set([b('a2')]))
+ self.assertEquals(
+ self.client.smembers('b'),
+ set([b('b1'), b('b2'), b('a1')]))
+
+ def test_spop(self):
+ # key is not set
+ self.assertEquals(self.client.spop('a'), None)
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.spop, 'a')
+ self.client.delete('a')
+ # real logic
+ s = [b('a'), b('b'), b('c')]
+ self.make_set('a', s)
+ value = self.client.spop('a')
+ self.assert_(value in s)
+ self.assertEquals(self.client.smembers('a'), set(s) - set([value]))
+
+ def test_srandmember(self):
+ # key is not set
+ self.assertEquals(self.client.srandmember('a'), None)
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.srandmember, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_set('a', 'abc')
+ self.assert_(self.client.srandmember('a') in b('abc'))
+
+ def test_srem(self):
+ # key is not set
+ self.assertEquals(self.client.srem('a', 'a'), False)
+ # key is not a set
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.srem, 'a', 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_set('a', 'abc')
+ self.assertEquals(self.client.srem('a', 'd'), False)
+ self.assertEquals(self.client.srem('a', 'b'), True)
+ self.assertEquals(self.client.smembers('a'), set([b('a'), b('c')]))
+
+ def test_sunion(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(rediscluster.ResponseError, self.client.sunion, ['a', 'b'])
+ self.client.delete('b')
+ # real logic
+ self.make_set('b', ['a1', 'b2', 'a3'])
+ self.assertEquals(
+ self.client.sunion(['a', 'b']),
+ set([b('a1'), b('a2'), b('a3'), b('b2')]))
+
+ def test_sunionstore(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # some key is not a set
+ self.make_set('a', ['a1', 'a2', 'a3'])
+ self.client.set('b', 'b')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.sunionstore,
+ 'c', ['a', 'b'])
+ self.client.delete('b')
+ self.make_set('b', ['a1', 'b2', 'a3'])
+ # dest key always gets overwritten, even if it's not a set, so don't
+ # test for that
+ # real logic
+ self.assertEquals(self.client.sunionstore('c', ['a', 'b']), 4)
+ self.assertEquals(
+ self.client.smembers('c'),
+ set([b('a1'), b('a2'), b('a3'), b('b2')]))
+
+ # SORTED SETS
+ def make_zset(self, name, d):
+ for k, v in d.items():
+ self.client.zadd(name, **{k: v})
+
+ def test_zadd(self):
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(
+ self.client.zrange('a', 0, 3),
+ [b('a1'), b('a2'), b('a3')])
+
+ def test_zcard(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zcard, 'a')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zcard('a'), 3)
+
+ def test_zcount(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zcount, 'a', 0, 0)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zcount('a', '-inf', '+inf'), 3)
+ self.assertEquals(self.client.zcount('a', 1, 2), 2)
+ self.assertEquals(self.client.zcount('a', 10, 20), 0)
+
+ def test_zincrby(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zincrby, 'a', 'a1')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zincrby('a', 'a2'), 3.0)
+ self.assertEquals(self.client.zincrby('a', 'a3', amount=5), 8.0)
+ self.assertEquals(self.client.zscore('a', 'a2'), 3.0)
+ self.assertEquals(self.client.zscore('a', 'a3'), 8.0)
+
+ def test_zinterstore(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_zset('a', {'a1': 1, 'a2': 1, 'a3': 1})
+ self.make_zset('b', {'a1': 2, 'a3': 2, 'a4': 2})
+ self.make_zset('c', {'a1': 6, 'a3': 5, 'a4': 4})
+
+ # sum, no weight
+ self.assert_(self.client.zinterstore('z', ['a', 'b', 'c']))
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [(b('a3'), 8), (b('a1'), 9)]
+ )
+
+ # max, no weight
+ self.assert_(
+ self.client.zinterstore('z', ['a', 'b', 'c'], aggregate='MAX')
+ )
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [(b('a3'), 5), (b('a1'), 6)]
+ )
+
+ # with weight
+ self.assert_(self.client.zinterstore('z', {'a': 1, 'b': 2, 'c': 3}))
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [(b('a3'), 20), (b('a1'), 23)]
+ )
+
+ def test_zrange(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zrange, 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zrange('a', 0, 1), [b('a1'), b('a2')])
+ self.assertEquals(self.client.zrange('a', 1, 2), [b('a2'), b('a3')])
+ self.assertEquals(
+ self.client.zrange('a', 0, 1, withscores=True),
+ [(b('a1'), 1.0), (b('a2'), 2.0)])
+ self.assertEquals(
+ self.client.zrange('a', 1, 2, withscores=True),
+ [(b('a2'), 2.0), (b('a3'), 3.0)])
+ # test a custom score casting function returns the correct value
+ self.assertEquals(
+ self.client.zrange('a', 0, 1, withscores=True,
+ score_cast_func=int),
+ [(b('a1'), 1), (b('a2'), 2)])
+ # a non existant key should return empty list
+ self.assertEquals(self.client.zrange('b', 0, 1, withscores=True), [])
+
+ def test_zrangebyscore(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.zrangebyscore,
+ 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
+ self.assertEquals(
+ self.client.zrangebyscore('a', 2, 4),
+ [b('a2'), b('a3'), b('a4')])
+ self.assertEquals(
+ self.client.zrangebyscore('a', 2, 4, start=1, num=2),
+ [b('a3'), b('a4')])
+ self.assertEquals(
+ self.client.zrangebyscore('a', 2, 4, withscores=True),
+ [(b('a2'), 2.0), (b('a3'), 3.0), (b('a4'), 4.0)])
+ # a non existant key should return empty list
+ self.assertEquals(
+ self.client.zrangebyscore('b', 0, 1, withscores=True), [])
+
+ def test_zrank(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zrank, 'a', 'a4')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
+ self.assertEquals(self.client.zrank('a', 'a1'), 0)
+ self.assertEquals(self.client.zrank('a', 'a2'), 1)
+ self.assertEquals(self.client.zrank('a', 'a3'), 2)
+ self.assertEquals(self.client.zrank('a', 'a4'), 3)
+ self.assertEquals(self.client.zrank('a', 'a5'), 4)
+ # non-existent value in zset
+ self.assertEquals(self.client.zrank('a', 'a6'), None)
+
+ def test_zrem(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zrem, 'a', 'a1')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zrem('a', 'a2'), True)
+ self.assertEquals(self.client.zrange('a', 0, 5), [b('a1'), b('a3')])
+ self.assertEquals(self.client.zrem('a', 'b'), False)
+ self.assertEquals(self.client.zrange('a', 0, 5), [b('a1'), b('a3')])
+
+ def test_zremrangebyrank(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.zremrangebyscore,
+ 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
+ self.assertEquals(self.client.zremrangebyrank('a', 1, 3), 3)
+ self.assertEquals(self.client.zrange('a', 0, 5), [b('a1'), b('a5')])
+
+ def test_zremrangebyscore(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.zremrangebyscore,
+ 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
+ self.assertEquals(self.client.zremrangebyscore('a', 2, 4), 3)
+ self.assertEquals(self.client.zrange('a', 0, 5), [b('a1'), b('a5')])
+ self.assertEquals(self.client.zremrangebyscore('a', 2, 4), 0)
+ self.assertEquals(self.client.zrange('a', 0, 5), [b('a1'), b('a5')])
+
+ def test_zrevrange(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.zrevrange,
+ 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.zrevrange('a', 0, 1), [b('a3'), b('a2')])
+ self.assertEquals(self.client.zrevrange('a', 1, 2), [b('a2'), b('a1')])
+ self.assertEquals(
+ self.client.zrevrange('a', 0, 1, withscores=True),
+ [(b('a3'), 3.0), (b('a2'), 2.0)])
+ self.assertEquals(
+ self.client.zrevrange('a', 1, 2, withscores=True),
+ [(b('a2'), 2.0), (b('a1'), 1.0)])
+ # a non existant key should return empty list
+ self.assertEquals(self.client.zrange('b', 0, 1, withscores=True), [])
+
+ def test_zrevrangebyscore(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(
+ rediscluster.ResponseError, self.client.zrevrangebyscore,
+ 'a', 0, 1)
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 1, 'a2': 2, 'a3': 3, 'a4': 4, 'a5': 5})
+ self.assertEquals(
+ self.client.zrevrangebyscore('a', 4, 2),
+ [b('a4'), b('a3'), b('a2')])
+ self.assertEquals(
+ self.client.zrevrangebyscore('a', 4, 2, start=1, num=2),
+ [b('a3'), b('a2')])
+ self.assertEquals(
+ self.client.zrevrangebyscore('a', 4, 2, withscores=True),
+ [(b('a4'), 4.0), (b('a3'), 3.0), (b('a2'), 2.0)])
+ # a non existant key should return empty list
+ self.assertEquals(
+ self.client.zrevrangebyscore('b', 1, 0, withscores=True),
+ [])
+
+ def test_zrevrank(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zrevrank, 'a', 'a4')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 5, 'a2': 4, 'a3': 3, 'a4': 2, 'a5': 1})
+ self.assertEquals(self.client.zrevrank('a', 'a1'), 0)
+ self.assertEquals(self.client.zrevrank('a', 'a2'), 1)
+ self.assertEquals(self.client.zrevrank('a', 'a3'), 2)
+ self.assertEquals(self.client.zrevrank('a', 'a4'), 3)
+ self.assertEquals(self.client.zrevrank('a', 'a5'), 4)
+ self.assertEquals(self.client.zrevrank('a', 'b'), None)
+
+ def test_zscore(self):
+ # key is not a zset
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.zscore, 'a', 'a1')
+ self.client.delete('a')
+ # real logic
+ self.make_zset('a', {'a1': 0, 'a2': 1, 'a3': 2})
+ self.assertEquals(self.client.zscore('a', 'a1'), 0.0)
+ self.assertEquals(self.client.zscore('a', 'a2'), 1.0)
+ # test a non-existant member
+ self.assertEquals(self.client.zscore('a', 'a4'), None)
+
+ def test_zunionstore(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_zset('a', {'a1': 1, 'a2': 1, 'a3': 1})
+ self.make_zset('b', {'a1': 2, 'a3': 2, 'a4': 2})
+ self.make_zset('c', {'a1': 6, 'a4': 5, 'a5': 4})
+
+ # sum, no weight
+ self.assert_(self.client.zunionstore('z', ['a', 'b', 'c']))
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [
+ (b('a2'), 1),
+ (b('a3'), 3),
+ (b('a5'), 4),
+ (b('a4'), 7),
+ (b('a1'), 9)
+ ]
+ )
+
+ # max, no weight
+ self.assert_(
+ self.client.zunionstore('z', ['a', 'b', 'c'], aggregate='MAX')
+ )
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [
+ (b('a2'), 1),
+ (b('a3'), 2),
+ (b('a5'), 4),
+ (b('a4'), 5),
+ (b('a1'), 6)
+ ]
+ )
+
+ # with weight
+ self.assert_(self.client.zunionstore('z', {'a': 1, 'b': 2, 'c': 3}))
+ self.assertEquals(
+ self.client.zrange('z', 0, -1, withscores=True),
+ [
+ (b('a2'), 1),
+ (b('a3'), 5),
+ (b('a5'), 12),
+ (b('a4'), 19),
+ (b('a1'), 23)
+ ]
+ )
+
+
+
+ # HASHES
+ def make_hash(self, key, d):
+ for k, v in iteritems(d):
+ self.client.hset(key, k, v)
+
+ def test_hget_and_hset(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hget, 'a', 'a1')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hget('a', 'a1'), None)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hget('a', 'a1'), b('1'))
+ self.assertEquals(self.client.hget('a', 'a2'), b('2'))
+ self.assertEquals(self.client.hget('a', 'a3'), b('3'))
+ # field was updated, redis returns 0
+ self.assertEquals(self.client.hset('a', 'a2', 5), 0)
+ self.assertEquals(self.client.hget('a', 'a2'), b('5'))
+ # field is new, redis returns 1
+ self.assertEquals(self.client.hset('a', 'a4', 4), 1)
+ self.assertEquals(self.client.hget('a', 'a4'), b('4'))
+ # key inside of hash that doesn't exist returns null value
+ self.assertEquals(self.client.hget('a', 'b'), None)
+
+ def test_hsetnx(self):
+ # Initially set the hash field
+ self.client.hsetnx('a', 'a1', 1)
+ self.assertEqual(self.client.hget('a', 'a1'), b('1'))
+ # Try and set the existing hash field to a different value
+ self.client.hsetnx('a', 'a1', 2)
+ self.assertEqual(self.client.hget('a', 'a1'), b('1'))
+
+ def test_hmset(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ d = {b('a'): b('1'), b('b'): b('2'), b('c'): b('3')}
+ self.assert_(self.client.hmset('foo', d))
+ self.assertEqual(self.client.hgetall('foo'), d)
+ self.assertRaises(rediscluster.DataError, self.client.hmset, 'foo', {})
+
+ def test_hmset_empty_value(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ d = {b('a'): b('1'), b('b'): b('2'), b('c'): b('')}
+ self.assert_(self.client.hmset('foo', d))
+ self.assertEqual(self.client.hgetall('foo'), d)
+
+ def test_hmget(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ d = {'a': 1, 'b': 2, 'c': 3}
+ self.assert_(self.client.hmset('foo', d))
+ self.assertEqual(
+ self.client.hmget('foo', ['a', 'b', 'c']), [b('1'), b('2'), b('3')])
+ self.assertEqual(self.client.hmget('foo', ['a', 'c']), [b('1'), b('3')])
+ # using *args type args
+ self.assertEquals(self.client.hmget('foo', 'a', 'c'), [b('1'), b('3')])
+
+ def test_hmget_empty(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.assertEqual(self.client.hmget('foo', ['a', 'b']), [None, None])
+
+ def test_hmget_no_keys(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.assertRaises(rediscluster.ResponseError, self.client.hmget, 'foo', [])
+
+ def test_hdel(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hdel, 'a', 'a1')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hdel('a', 'a1'), False)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hget('a', 'a2'), b('2'))
+ self.assert_(self.client.hdel('a', 'a2'))
+ self.assertEquals(self.client.hget('a', 'a2'), None)
+
+ def test_hexists(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hexists, 'a', 'a1')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hexists('a', 'a1'), False)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hexists('a', 'a1'), True)
+ self.assertEquals(self.client.hexists('a', 'a4'), False)
+ self.client.hdel('a', 'a1')
+ self.assertEquals(self.client.hexists('a', 'a1'), False)
+
+ def test_hgetall(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hgetall, 'a')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hgetall('a'), {})
+ # real logic
+ h = {b('a1'): b('1'), b('a2'): b('2'), b('a3'): b('3')}
+ self.make_hash('a', h)
+ remote_hash = self.client.hgetall('a')
+ self.assertEquals(h, remote_hash)
+
+ def test_hincrby(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hincrby, 'a', 'a1')
+ self.client.delete('a')
+ # no key should create the hash and incr the key's value to 1
+ self.assertEquals(self.client.hincrby('a', 'a1'), 1)
+ # real logic
+ self.assertEquals(self.client.hincrby('a', 'a1'), 2)
+ self.assertEquals(self.client.hincrby('a', 'a1', amount=2), 4)
+ # negative values decrement
+ self.assertEquals(self.client.hincrby('a', 'a1', amount=-3), 1)
+ # hash that exists, but key that doesn't
+ self.assertEquals(self.client.hincrby('a', 'a2', amount=3), 3)
+ # finally a key that's not an int
+ self.client.hset('a', 'a3', 'foo')
+ self.assertRaises(rediscluster.ResponseError, self.client.hincrby, 'a', 'a3')
+
+ def test_hkeys(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hkeys, 'a')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hkeys('a'), [])
+ # real logic
+ h = {b('a1'): b('1'), b('a2'): b('2'), b('a3'): b('3')}
+ self.make_hash('a', h)
+ keys = dictkeys(h)
+ keys.sort()
+ remote_keys = self.client.hkeys('a')
+ remote_keys.sort()
+ self.assertEquals(keys, remote_keys)
+
+ def test_hlen(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hlen, 'a')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hlen('a'), 0)
+ # real logic
+ self.make_hash('a', {'a1': 1, 'a2': 2, 'a3': 3})
+ self.assertEquals(self.client.hlen('a'), 3)
+ self.client.hdel('a', 'a3')
+ self.assertEquals(self.client.hlen('a'), 2)
+
+ def test_hvals(self):
+ # key is not a hash
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.hvals, 'a')
+ self.client.delete('a')
+ # no key
+ self.assertEquals(self.client.hvals('a'), [])
+ # real logic
+ h = {b('a1'): b('1'), b('a2'): b('2'), b('a3'): b('3')}
+ self.make_hash('a', h)
+ vals = dictvalues(h)
+ vals.sort()
+ remote_vals = self.client.hvals('a')
+ remote_vals.sort()
+ self.assertEquals(vals, remote_vals)
+
+ # SORT
+ def test_sort_bad_key(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ # key is not set
+ self.assertEquals(self.client.sort('a'), [])
+ # key is a string value
+ self.client.set('a', 'a')
+ self.assertRaises(rediscluster.ResponseError, self.client.sort, 'a')
+ self.client.delete('a')
+
+ def test_sort_basic(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', '3214')
+ self.assertEquals(
+ self.client.sort('a'),
+ [b('1'), b('2'), b('3'), b('4')])
+
+ def test_sort_limited(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', '3214')
+ self.assertEquals(
+ self.client.sort('a', start=1, num=2),
+ [b('2'), b('3')])
+
+ def test_sort_by(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client['score:1'] = 8
+ self.client['score:2'] = 3
+ self.client['score:3'] = 5
+ self.make_list('a_values', '123')
+ self.assertEquals(
+ self.client.sort('a_values', by='score:*'),
+ [b('2'), b('3'), b('1')])
+
+ def test_sort_get(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client['user:1'] = 'u1'
+ self.client['user:2'] = 'u2'
+ self.client['user:3'] = 'u3'
+ self.make_list('a', '231')
+ self.assertEquals(
+ self.client.sort('a', get='user:*'),
+ [b('u1'), b('u2'), b('u3')])
+
+ def test_sort_get_multi(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.client['user:1'] = 'u1'
+ self.client['user:2'] = 'u2'
+ self.client['user:3'] = 'u3'
+ self.make_list('a', '231')
+ self.assertEquals(
+ self.client.sort('a', get=('user:*', '#')),
+ [b('u1'), b('1'), b('u2'), b('2'), b('u3'), b('3')])
+
+ def test_sort_desc(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', '231')
+ self.assertEquals(
+ self.client.sort('a', desc=True),
+ [b('3'), b('2'), b('1')])
+
+ def test_sort_alpha(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', 'ecbda')
+ self.assertEquals(
+ self.client.sort('a', alpha=True),
+ [b('a'), b('b'), b('c'), b('d'), b('e')])
+
+ def test_sort_store(self):
+ try:
+ raise unittest.SkipTest()
+ except AttributeError:
+ return
+ self.make_list('a', '231')
+ self.assertEquals(self.client.sort('a', store='sorted_values'), 3)
+ self.assertEquals(
+ self.client.lrange('sorted_values', 0, 5),
+ [b('1'), b('2'), b('3')])