diff options
| author | jingchao <jingchaohu@gmail.com> | 2013-11-27 17:15:09 +0800 |
|---|---|---|
| committer | jingchao <jingchaohu@gmail.com> | 2013-11-27 17:15:09 +0800 |
| commit | 2047c61ce88842df511def65893c769ffa9b719c (patch) | |
| tree | 46cac82828e6f1aab22f247b0f1e73f26b64d665 | |
| parent | 08d1a55ae8c850b2dd759f62ad578b8538d77fa8 (diff) | |
| download | redis-py-2047c61ce88842df511def65893c769ffa9b719c.tar.gz | |
add 2.8.0 new methods: scan/sscan/hscan/zscan
| -rw-r--r-- | redis/client.py | 93 | ||||
| -rw-r--r-- | tests/test_commands.py | 39 |
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')]) |
