From b4d8c1bf8a8acfc893730229a6c03421c91e0651 Mon Sep 17 00:00:00 2001 From: Shoham Elias Date: Thu, 15 Feb 2024 15:32:06 +0000 Subject: [PATCH] Python: adds SISMEMBER command --- CHANGELOG.md | 3 +- glide-core/src/client/value_conversion.rs | 2 +- glide-core/src/protobuf/redis_request.proto | 1 + glide-core/src/socket_listener.rs | 1 + python/python/glide/async_commands/core.py | 29 +++++++++++++++++++ .../glide/async_commands/transaction.py | 20 +++++++++++++ python/python/tests/test_async_client.py | 11 +++++++ python/python/tests/test_transaction.py | 2 ++ 8 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54fd099a94..4c86df257e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,9 +11,10 @@ * Python, Node: When recieving LPOP/RPOP with count, convert result to Array. ([#811](https://github.com/aws/glide-for-redis/pull/811)) * Python, Node: Added TYPE command ([#945](https://github.com/aws/glide-for-redis/pull/945), [#980](https://github.com/aws/glide-for-redis/pull/980)) * Python: Added HLEN command ([#944](https://github.com/aws/glide-for-redis/pull/944)) -* Node: Added ZCOUNT command ([#909](https://github.com/aws/glide-for-redis/pull/909)) +* Python, Node: Added ZCOUNT command ([#878](https://github.com/aws/glide-for-redis/pull/878)) ([#909](https://github.com/aws/glide-for-redis/pull/909)) * Python: Added ECHO command ([#953](https://github.com/aws/glide-for-redis/pull/953)) * Python: Added ZPOPMIN command ([#975](https://github.com/aws/glide-for-redis/pull/975)) +* Python: Added SISMEMBER command ([#971](https://github.com/aws/glide-for-redis/pull/971)) #### Features * Python, Node: Added support in Lua Scripts ([#775](https://github.com/aws/glide-for-redis/pull/775), [#860](https://github.com/aws/glide-for-redis/pull/860)) diff --git a/glide-core/src/client/value_conversion.rs b/glide-core/src/client/value_conversion.rs index c4864310d0..701eb78d29 100644 --- a/glide-core/src/client/value_conversion.rs +++ b/glide-core/src/client/value_conversion.rs @@ -201,7 +201,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option { Some(ExpectedReturnType::Map) } b"INCRBYFLOAT" | b"HINCRBYFLOAT" => Some(ExpectedReturnType::Double), - b"HEXISTS" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT" => { + b"HEXISTS" | b"EXPIRE" | b"SISMEMBER" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT" => { Some(ExpectedReturnType::Boolean) } b"SMEMBERS" => Some(ExpectedReturnType::Set), diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 30f8e3c046..a7a805cfe0 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -107,6 +107,7 @@ enum RequestType { HLen = 69; Echo = 70; ZPopMin = 71; + SIsMember = 72; } message Command { diff --git a/glide-core/src/socket_listener.rs b/glide-core/src/socket_listener.rs index 0090a1e20e..9b326a84d6 100644 --- a/glide-core/src/socket_listener.rs +++ b/glide-core/src/socket_listener.rs @@ -350,6 +350,7 @@ fn get_command(request: &Command) -> Option { RequestType::HLen => Some(cmd("HLEN")), RequestType::Echo => Some(cmd("ECHO")), RequestType::ZPopMin => Some(cmd("ZPOPMIN")), + RequestType::SIsMember => Some(cmd("SISMEMBER")), } } diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py index 868c2afac7..1925387898 100644 --- a/python/python/glide/async_commands/core.py +++ b/python/python/glide/async_commands/core.py @@ -871,6 +871,35 @@ async def scard(self, key: str) -> int: """ return cast(int, await self._execute_command(RequestType.SCard, [key])) + async def sismember( + self, + key: str, + member: str, + ) -> bool: + """ + Returns if `member` is a member of the set stored at `key`. + + See https://redis.io/commands/sismember/ for more details. + + Args: + key (str): The key of the set. + member (str): The member to check for existence in the set. + + Returns: + bool: True if the member exists in the set, False otherwise. + If `key` doesn't exist, it is treated as an empty set and the command returns False. + + Examples: + >>> await client.sismember("my_set", "member1") + True # Indicates that "member1" exists in the set "my_set". + >>> await client.sismember("my_set", "non_existing_member") + False # Indicates that "non_existing_member" does not exist in the set "my_set". + """ + return cast( + bool, + await self._execute_command(RequestType.SIsMember, [key, member]), + ) + async def ltrim(self, key: str, start: int, end: int) -> TOK: """ Trim an existing list so that it will contain only the specified range of elements specified. diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 110847aa5c..3a6dc16cf5 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -673,6 +673,26 @@ def scard(self: TTransaction, key: str) -> TTransaction: """ return self.append_command(RequestType.SCard, [key]) + def sismember( + self: TTransaction, + key: str, + member: str, + ) -> TTransaction: + """ + Returns if `member` is a member of the set stored at `key`. + + See https://redis.io/commands/sismember/ for more details. + + Args: + key (str): The key of the set. + member (str): The member to check for existence in the set. + + Commands response: + bool: True if the member exists in the set, False otherwise. + If `key` doesn't exist, it is treated as an empty set and the command returns False. + """ + return self.append_command(RequestType.SIsMember, [key, member]) + def ltrim(self: TTransaction, key: str, start: int, end: int) -> TTransaction: """ Trim an existing list so that it will contain only the specified range of elements specified. diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index df31821545..1042ea79c6 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -843,6 +843,17 @@ async def test_sadd_srem_smembers_scard_wrong_type_raise_error( await redis_client.smembers(key) assert "Operation against a key holding the wrong kind of value" in str(e) + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_sismember(self, redis_client: TRedisClient): + key = get_random_string(10) + member = get_random_string(5) + assert await redis_client.sadd(key, [member]) == 1 + assert await redis_client.sismember(key, member) == True + + assert await redis_client.sismember(key, get_random_string(5)) == False + assert await redis_client.sismember("non_existing_key", member) == False + @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) async def test_ltrim(self, redis_client: TRedisClient): diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index 9fcf259eea..5089795c2a 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -134,6 +134,8 @@ def transaction_test( args.append({"bar"}) transaction.scard(key7) args.append(1) + transaction.sismember(key7, "bar") + args.append(True) transaction.zadd(key8, {"one": 1, "two": 2, "three": 3}) args.append(3)