Skip to content

Add support for SMISMEMBER command added in redis 6.2.0 #1486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker/base/Dockerfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
FROM redis:6.0.9-buster
FROM redis:6.2.3-buster
14 changes: 14 additions & 0 deletions redis/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -594,6 +598,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
Expand Down Expand Up @@ -2387,6 +2393,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)
Expand Down
22 changes: 14 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
38 changes: 33 additions & 5 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -123,9 +136,10 @@ def teardown():
'categories': ['-@all'],
'commands': [],
'enabled': True,
'flags': ['on', 'nopass'],
'flags': nopass_flags,
'keys': [],
'passwords': [],
**channels_itm,
}

# test all args
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -1272,6 +1293,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')
Expand Down