summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjingchao <jingchaohu@gmail.com>2013-11-27 17:15:09 +0800
committerjingchao <jingchaohu@gmail.com>2013-11-27 17:15:09 +0800
commit2047c61ce88842df511def65893c769ffa9b719c (patch)
tree46cac82828e6f1aab22f247b0f1e73f26b64d665
parent08d1a55ae8c850b2dd759f62ad578b8538d77fa8 (diff)
downloadredis-py-2047c61ce88842df511def65893c769ffa9b719c.tar.gz
add 2.8.0 new methods: scan/sscan/hscan/zscan
-rw-r--r--redis/client.py93
-rw-r--r--tests/test_commands.py39
2 files changed, 131 insertions, 1 deletions
diff --git a/redis/client.py b/redis/client.py
index 79a2a54..d133989 100644
--- a/redis/client.py
+++ b/redis/client.py
@@ -240,6 +240,21 @@ def parse_script(response, **options):
return response
+def parse_scan(response, **options):
+ return response
+
+
+def parse_hscan(response, **options):
+ cursor, r = response
+ return [cursor, r and pairs_to_dict(r) or {}]
+
+
+def parse_zscan(response, **options):
+ score_cast_func = options.get('score_cast_func', float)
+ it = iter(response[1])
+ return [response[0], list(izip(it, imap(score_cast_func, it)))]
+
+
class StrictRedis(object):
"""
Implementation of the Redis protocol.
@@ -304,7 +319,11 @@ class StrictRedis(object):
'SCRIPT': parse_script,
'SET': lambda r: r and nativestr(r) == 'OK',
'TIME': lambda x: (int(x[0]), int(x[1])),
- 'SENTINEL': parse_sentinel
+ 'SENTINEL': parse_sentinel,
+ 'SCAN': parse_scan,
+ 'SSCAN': parse_scan,
+ 'HSCAN': parse_hscan,
+ 'ZSCAN': parse_zscan
}
)
@@ -1153,6 +1172,78 @@ class StrictRedis(object):
options = {'groups': len(get) if groups else None}
return self.execute_command('SORT', *pieces, **options)
+ #### SCAN COMMANDS ####
+ def scan(self, cursor, match=None, count=None):
+ """
+ Scan and return (nextcursor, keys)
+
+ ``match`` allows for filtering the keys by pattern
+
+ ``count`` allows for hint the minimum number of returns
+ """
+ pieces = [cursor]
+ if match is not None:
+ pieces.append('MATCH')
+ pieces.append(match)
+ if count is not None:
+ pieces.append('COUNT')
+ pieces.append(count)
+ return self.execute_command('SCAN', *pieces)
+
+ def sscan(self, name, cursor, match=None, count=None):
+ """
+ Scan and return (nextcursor, membersofset)
+
+ ``match`` allows for filtering the keys by pattern
+
+ ``count`` allows for hint the minimum number of returns
+ """
+ pieces = [name, cursor]
+ if match is not None:
+ pieces.append('MATCH')
+ pieces.append(match)
+ if count is not None:
+ pieces.append('COUNT')
+ pieces.append(count)
+ return self.execute_command('SSCAN', *pieces)
+
+ def hscan(self, name, cursor, match=None, count=None):
+ """
+ Scan and return (nextcursor, dict)
+
+ ``match`` allows for filtering the keys by pattern
+
+ ``count`` allows for hint the minimum number of returns
+ """
+ pieces = [name, cursor]
+ if match is not None:
+ pieces.append('MATCH')
+ pieces.append(match)
+ if count is not None:
+ pieces.append('COUNT')
+ pieces.append(count)
+ return self.execute_command('HSCAN', *pieces)
+
+ def zscan(self, name, cursor, match=None, count=None, score_cast_func=float):
+ """
+ Scan and return (nextcursor, pairs)
+
+ ``match`` allows for filtering the keys by pattern
+
+ ``count`` allows for hint the minimum number of returns
+
+ ``score_cast_func`` a callable used to cast the score return value
+ """
+ pieces = [name, cursor]
+ if match is not None:
+ pieces.append('MATCH')
+ pieces.append(match)
+ if count is not None:
+ pieces.append('COUNT')
+ pieces.append(count)
+ options = {'score_cast_func': score_cast_func}
+ return self.execute_command('ZSCAN', *pieces, **options)
+
#### SET COMMANDS ####
def sadd(self, name, *values):
"Add ``value(s)`` to set ``name``"
diff --git a/tests/test_commands.py b/tests/test_commands.py
index 24bf11d..b82f0f5 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -635,6 +635,45 @@ class TestRedisCommands(object):
assert r.rpushx('a', '4') == 4
assert r.lrange('a', 0, -1) == [b('1'), b('2'), b('3'), b('4')]
+ ### SCAN COMMANDS ###
+ @skip_if_server_version_lt('2.8.0')
+ def test_scan(self, r):
+ r.set('a', 1)
+ r.set('b', 2)
+ r.set('c', 3)
+ cursor, keys = r.scan(0)
+ assert cursor == b('0')
+ assert set(keys) == set([b('a'), b('b'), b('c')])
+ _, keys = r.scan(0, match='a')
+ assert set(keys) == set([b('a')])
+
+ @skip_if_server_version_lt('2.8.0')
+ def test_sscan(self, r):
+ r.sadd('a', 1, 2, 3)
+ cursor, members = r.sscan('a', 0)
+ assert cursor == b('0')
+ assert set(members) == set([b('1'), b('2'), b('3')])
+ _, members = r.sscan('a', 0, match=b('1'))
+ assert set(members) == set([b('1')])
+
+ @skip_if_server_version_lt('2.8.0')
+ def test_hscan(self, r):
+ r.hmset('a', dict(a=1, b=2, c=3))
+ cursor, dic = r.hscan('a', 0)
+ assert cursor == b('0')
+ assert dic == dict(a=b('1'), b=b('2'), c=b('3'))
+ _, dic = r.hscan('a', 0, match='a')
+ assert dic == dict(a=b('1'))
+
+ @skip_if_server_version_lt('2.8.0')
+ def test_zscan(self, r):
+ r.zadd('a', 'a', 1, 'b', 2, 'c', 3)
+ cursor, pairs = r.zscan('a', 0)
+ assert cursor == b('0')
+ assert set(pairs) == set([(b('a'), 1), (b('b'), 2), (b('c'), 3)])
+ _, pairs = r.zscan('a', 0, match='a')
+ assert set(pairs) == set([(b('a'), 1)])
+
### SET COMMANDS ###
def test_sadd(self, r):
members = set([b('1'), b('2'), b('3')])