From 1651287c62f8ec20bab38df39dbd560a4d985f14 Mon Sep 17 00:00:00 2001 From: Shoham Elias <116083498+shohamazon@users.noreply.github.com> Date: Tue, 18 Jun 2024 12:00:03 +0300 Subject: [PATCH] Python: adds LSET command (#1584) --- CHANGELOG.md | 1 + python/python/glide/async_commands/core.py | 27 +++++++++++++++++ .../glide/async_commands/transaction.py | 20 +++++++++++++ python/python/tests/test_async_client.py | 30 +++++++++++++++++++ python/python/tests/test_transaction.py | 2 ++ 5 files changed, 80 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 284c13b628..543c2275ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * Python: Added HSTRLEN command ([#1564](https://github.com/aws/glide-for-redis/pull/1564)) * Python: Added MSETNX command ([#1565](https://github.com/aws/glide-for-redis/pull/1565)) * Python: Added MOVE command ([#1566](https://github.com/aws/glide-for-redis/pull/1566)) +* Python: Added LSET command ([#1584](https://github.com/aws/glide-for-redis/pull/1584)) * Node: Added OBJECT IDLETIME command ([#1567](https://github.com/aws/glide-for-redis/pull/1567)) * Node: Added OBJECT REFCOUNT command ([#1568](https://github.com/aws/glide-for-redis/pull/1568)) * Python: Added SETBIT command ([#1571](https://github.com/aws/glide-for-redis/pull/1571)) diff --git a/python/python/glide/async_commands/core.py b/python/python/glide/async_commands/core.py index 52b4e447e0..85d7233338 100644 --- a/python/python/glide/async_commands/core.py +++ b/python/python/glide/async_commands/core.py @@ -1462,6 +1462,33 @@ async def lindex( await self._execute_command(RequestType.LIndex, [key, str(index)]), ) + async def lset(self, key: str, index: int, element: str) -> TOK: + """ + Sets the list element at `index` to `element`. + + The index is zero-based, so `0` means the first element, `1` the second element and so on. + Negative indices can be used to designate elements starting at the tail of the list. + Here, `-1` means the last element, `-2` means the penultimate and so forth. + + See https://valkey.io/commands/lset/ for details. + + Args: + key (str): The key of the list. + index (int): The index of the element in the list to be set. + element (str): The new element to set at the specified index. + + Returns: + TOK: A simple `OK` response. + + Examples: + >>> await client.lset("testKey", 1, "two") + OK + """ + return cast( + TOK, + await self._execute_command(RequestType.LSet, [key, str(index), element]), + ) + async def rpush(self, key: str, elements: List[str]) -> int: """ Inserts all the specified values at the tail of the list stored at `key`. diff --git a/python/python/glide/async_commands/transaction.py b/python/python/glide/async_commands/transaction.py index 2dfc3c662f..ed2e321382 100644 --- a/python/python/glide/async_commands/transaction.py +++ b/python/python/glide/async_commands/transaction.py @@ -954,6 +954,26 @@ def lindex( """ return self.append_command(RequestType.LIndex, [key, str(index)]) + def lset(self: TTransaction, key: str, index: int, element: str) -> TTransaction: + """ + Sets the list element at `index` to `element`. + + The index is zero-based, so `0` means the first element, `1` the second element and so on. + Negative indices can be used to designate elements starting at the tail of the list. + Here, `-1` means the last element, `-2` means the penultimate and so forth. + + See https://valkey.io/commands/lset/ for details. + + Args: + key (str): The key of the list. + index (int): The index of the element in the list to be set. + element (str): The new element to set at the specified index. + + Commands response: + TOK: A simple `OK` response. + """ + return self.append_command(RequestType.LSet, [key, str(index), element]) + def rpush(self: TTransaction, key: str, elements: List[str]) -> TTransaction: """Inserts all the specified values at the tail of the list stored at `key`. `elements` are inserted one after the other to the tail of the list, from the leftmost element diff --git a/python/python/tests/test_async_client.py b/python/python/tests/test_async_client.py index 78b43f239a..59445b07c5 100644 --- a/python/python/tests/test_async_client.py +++ b/python/python/tests/test_async_client.py @@ -1380,6 +1380,36 @@ async def endless_blmove_call(): with pytest.raises(asyncio.TimeoutError): await asyncio.wait_for(endless_blmove_call(), timeout=3) + @pytest.mark.parametrize("cluster_mode", [True, False]) + @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) + async def test_lset(self, redis_client: TRedisClient): + key = get_random_string(10) + element = get_random_string(5) + values = [get_random_string(5) for _ in range(4)] + + # key does not exist + with pytest.raises(RequestError): + await redis_client.lset("non_existing_key", 0, element) + + # pushing elements to list + await redis_client.lpush(key, values) == 4 + + # index out of range + with pytest.raises(RequestError): + await redis_client.lset(key, 10, element) + + # assert lset result + assert await redis_client.lset(key, 0, element) == OK + + values = [element] + values[:-1][::-1] + assert await redis_client.lrange(key, 0, -1) == values + + # assert lset with a negative index for the last element in the list + assert await redis_client.lset(key, -1, element) == OK + + values[-1] = element + assert await redis_client.lrange(key, 0, -1) == values + @pytest.mark.parametrize("cluster_mode", [True, False]) @pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3]) async def test_sadd_srem_smembers_scard(self, redis_client: TRedisClient): diff --git a/python/python/tests/test_transaction.py b/python/python/tests/test_transaction.py index 1d6827cafd..ee84635834 100644 --- a/python/python/tests/test_transaction.py +++ b/python/python/tests/test_transaction.py @@ -224,6 +224,8 @@ async def transaction_test( args.append([key9, value3]) transaction.brpop([key9], 1) args.append([key9, value]) + transaction.lset(key9, 0, value2) + args.append(OK) transaction.sadd(key7, ["foo", "bar"]) args.append(2)