From e294260bd738155daa96ae1168a6ed6c2758a3a1 Mon Sep 17 00:00:00 2001 From: ported Date: Mon, 24 May 2021 13:09:12 +0200 Subject: [PATCH 1/2] add support for SMISMEMBER command added in redis 6.2.0, bump redis version used for tests --- docker/base/Dockerfile | 2 +- redis/client.py | 10 ++++++++++ tests/test_commands.py | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index 003eac1f89..8c1357181f 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -1 +1 @@ -FROM redis:6.0.9-buster +FROM redis:6.2.3-buster diff --git a/redis/client.py b/redis/client.py index 59575cd835..c068cc4713 100755 --- a/redis/client.py +++ b/redis/client.py @@ -594,6 +594,8 @@ class Redis: 'SDIFF SINTER SMEMBERS SUNION', lambda r: r and set(r) or set() ), + **string_keys_to_dict('SMISMEMBER', + lambda r: [bool(int(v)) for v in r]), **string_keys_to_dict( 'ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE', zset_score_pairs @@ -2387,6 +2389,14 @@ def smembers(self, name): "Return all members of the set ``name``" return self.execute_command('SMEMBERS', name) + def smismember(self, name, values, *args): + """ + Return whether each value in ``values`` is a member of the set ``name`` + as a list of ``bool`` in the order of ``values`` + """ + args = list_or_args(values, args) + return self.execute_command('SMISMEMBER', name, *args) + def smove(self, src, dst, value): "Move ``value`` from set ``src`` to set ``dst`` atomically" return self.execute_command('SMOVE', src, dst, value) diff --git a/tests/test_commands.py b/tests/test_commands.py index d1f85b7306..1119d3e4f7 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -1272,6 +1272,13 @@ def test_smembers(self, r): r.sadd('a', '1', '2', '3') assert r.smembers('a') == {b'1', b'2', b'3'} + @skip_if_server_version_lt('6.2.0') + def test_smismember(self, r): + r.sadd('a', '1', '2', '3') + result_list = [True, False, True, True] + assert r.smismember('a', '1', '4', '2', '3') == result_list + assert r.smismember('a', ['1', '4', '2', '3']) == result_list + def test_smove(self, r): r.sadd('a', 'a1', 'a2') r.sadd('b', 'b1', 'b2') From e72daae74b8b2c551d8f31c155dbcfd87063d1d6 Mon Sep 17 00:00:00 2001 From: ported Date: Mon, 24 May 2021 16:05:31 +0200 Subject: [PATCH 2/2] add support for redis 6.2.0 channel rules in acl_getuser and tests --- redis/client.py | 4 ++++ tests/conftest.py | 22 ++++++++++++++-------- tests/test_commands.py | 31 ++++++++++++++++++++++++++----- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/redis/client.py b/redis/client.py index c068cc4713..a2cba72011 100755 --- a/redis/client.py +++ b/redis/client.py @@ -503,6 +503,10 @@ def parse_acl_getuser(response, **options): data['commands'] = commands data['categories'] = categories data['enabled'] = 'on' in data['flags'] + + # "channels" was added in Redis 6.2.0 + if "channels" in data: + data['channels'] = list(map(str_if_bytes, data['channels'])) return data diff --git a/tests/conftest.py b/tests/conftest.py index cd4d4894a3..d2638058c4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -40,20 +40,26 @@ def pytest_sessionstart(session): REDIS_INFO["arch_bits"] = arch_bits -def skip_if_server_version_lt(min_version): +def server_version_lt(min_version): + redis_version = REDIS_INFO["version"] + return StrictVersion(redis_version) < StrictVersion(min_version) + + +def server_version_gte(max_version): redis_version = REDIS_INFO["version"] - check = StrictVersion(redis_version) < StrictVersion(min_version) + return StrictVersion(redis_version) >= StrictVersion(max_version) + + +def skip_if_server_version_lt(min_version): return pytest.mark.skipif( - check, + server_version_lt(min_version), reason="Redis version required >= {}".format(min_version)) -def skip_if_server_version_gte(min_version): - redis_version = REDIS_INFO["version"] - check = StrictVersion(redis_version) >= StrictVersion(min_version) +def skip_if_server_version_gte(max_version): return pytest.mark.skipif( - check, - reason="Redis version required < {}".format(min_version)) + server_version_gte(max_version), + reason="Redis version required < {}".format(max_version)) def skip_unless_arch_bits(arch_bits): diff --git a/tests/test_commands.py b/tests/test_commands.py index 1119d3e4f7..2fed09c2ee 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -12,6 +12,7 @@ from .conftest import ( _get_client, REDIS_6_VERSION, + server_version_gte, skip_if_server_version_gte, skip_if_server_version_lt, skip_unless_arch_bits, @@ -106,15 +107,27 @@ def teardown(): r.acl_deluser(username) request.addfinalizer(teardown) + if server_version_gte("6.2.0"): + channels_itm = {'channels': ['*']} + enabled_false_flags = ['off', 'allchannels', 'sanitize-payload'] + nopass_flags = ['on', 'allchannels', 'nopass', 'sanitize-payload'] + allargs_flags = ['on', 'allchannels', 'sanitize-payload'] + else: + channels_itm = {} + enabled_false_flags = ['off'] + nopass_flags = ['on', 'nopass'] + allargs_flags = ['on'] + # test enabled=False assert r.acl_setuser(username, enabled=False, reset=True) assert r.acl_getuser(username) == { 'categories': ['-@all'], 'commands': [], 'enabled': False, - 'flags': ['off'], + 'flags': enabled_false_flags, 'keys': [], 'passwords': [], + **channels_itm, } # test nopass=True @@ -123,9 +136,10 @@ def teardown(): 'categories': ['-@all'], 'commands': [], 'enabled': True, - 'flags': ['on', 'nopass'], + 'flags': nopass_flags, 'keys': [], 'passwords': [], + **channels_itm, } # test all args @@ -138,9 +152,11 @@ def teardown(): assert set(acl['categories']) == set(['-@all', '+@set', '+@hash']) assert set(acl['commands']) == set(['+get', '+mget', '-hset']) assert acl['enabled'] is True - assert acl['flags'] == ['on'] + assert acl['flags'] == allargs_flags assert set(acl['keys']) == set([b'cache:*', b'objects:*']) assert len(acl['passwords']) == 2 + if server_version_gte("6.2.0"): + assert acl["channels"] == ['*'] # test reset=False keeps existing ACL and applies new ACL on top assert r.acl_setuser(username, enabled=True, reset=True, @@ -157,9 +173,11 @@ def teardown(): assert set(acl['categories']) == set(['-@all', '+@set', '+@hash']) assert set(acl['commands']) == set(['+get', '+mget']) assert acl['enabled'] is True - assert acl['flags'] == ['on'] + assert acl['flags'] == allargs_flags assert set(acl['keys']) == set([b'cache:*', b'objects:*']) assert len(acl['passwords']) == 2 + if server_version_gte("6.2.0"): + assert acl["channels"] == ['*'] # test removal of passwords assert r.acl_setuser(username, enabled=True, reset=True, @@ -196,7 +214,10 @@ def teardown(): assert r.acl_setuser(username, enabled=False, reset=True) users = r.acl_list() - assert 'user %s off -@all' % username in users + if server_version_gte("6.2.0"): + assert 'user %s off sanitize-payload &* -@all' % username in users + else: + assert 'user %s off -@all' % username in users @skip_if_server_version_lt(REDIS_6_VERSION) def test_acl_log(self, r, request):