Skip to content

Commit

Permalink
Python: add LINSERT command (#227) (valkey-io#1304)
Browse files Browse the repository at this point in the history
* Python: add LINSERT command (#227)

* Minor update to test for clarification

* Fix linter

* PR suggestions
  • Loading branch information
aaron-congo authored and cyip10 committed Jun 24, 2024
1 parent 7272242 commit 16d9ba2
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* Python: Added GEOHASH command ([#1281](https://github.com/aws/glide-for-redis/pull/1281))
* Python: Added ZLEXCOUNT command ([#1305](https://github.com/aws/glide-for-redis/pull/1305))
* Python: Added ZREMRANGEBYLEX command ([#1306](https://github.com/aws/glide-for-redis/pull/1306))
* Python: Added LINSERT command ([#1304](https://github.com/aws/glide-for-redis/pull/1304))

#### Fixes
* Python: Fix typing error "‘type’ object is not subscriptable" ([#1203](https://github.com/aws/glide-for-redis/pull/1203))
Expand Down
36 changes: 36 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,11 @@ def get_cmd_args(self) -> List[str]:
return [self.cmd_arg] if self.value is None else [self.cmd_arg, self.value]


class InsertPosition(Enum):
BEFORE = "BEFORE"
AFTER = "AFTER"


class CoreCommands(Protocol):
async def _execute_command(
self,
Expand Down Expand Up @@ -1060,6 +1065,37 @@ async def rpop_count(self, key: str, count: int) -> Optional[List[str]]:
await self._execute_command(RequestType.RPop, [key, str(count)]),
)

async def linsert(
self, key: str, position: InsertPosition, pivot: str, element: str
) -> int:
"""
Inserts `element` in the list at `key` either before or after the `pivot`.
See https://redis.io/commands/linsert/ for details.
Args:
key (str): The key of the list.
position (InsertPosition): The relative position to insert into - either `InsertPosition.BEFORE` or
`InsertPosition.AFTER` the `pivot`.
pivot (str): An element of the list.
element (str): The new element to insert.
Returns:
int: The list length after a successful insert operation.
If the `key` doesn't exist returns `-1`.
If the `pivot` wasn't found, returns `0`.
Examples:
>>> await client.linsert("my_list", InsertPosition.BEFORE, "World", "There")
3 # "There" was inserted before "World", and the new length of the list is 3.
"""
return cast(
int,
await self._execute_command(
RequestType.LInsert, [key, position.value, pivot, element]
),
)

async def sadd(self, key: str, members: List[str]) -> int:
"""
Add specified members to the set stored at `key`.
Expand Down
25 changes: 25 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ExpirySet,
GeospatialData,
InfoSection,
InsertPosition,
UpdateOptions,
)
from glide.async_commands.sorted_set import (
Expand Down Expand Up @@ -773,6 +774,30 @@ def rpop_count(self: TTransaction, key: str, count: int) -> TTransaction:
"""
return self.append_command(RequestType.RPop, [key, str(count)])

def linsert(
self: TTransaction, key: str, position: InsertPosition, pivot: str, element: str
) -> TTransaction:
"""
Inserts `element` in the list at `key` either before or after the `pivot`.
See https://redis.io/commands/linsert/ for details.
Args:
key (str): The key of the list.
position (InsertPosition): The relative position to insert into - either `InsertPosition.BEFORE` or
`InsertPosition.AFTER` the `pivot`.
pivot (str): An element of the list.
element (str): The new element to insert.
Command response:
int: The list length after a successful insert operation.
If the `key` doesn't exist returns `-1`.
If the `pivot` wasn't found, returns `0`.
"""
return self.append_command(
RequestType.LInsert, [key, position.value, pivot, element]
)

def sadd(self: TTransaction, key: str, members: List[str]) -> TTransaction:
"""
Add specified members to the set stored at `key`.
Expand Down
32 changes: 32 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
GeospatialData,
InfBound,
InfoSection,
InsertPosition,
UpdateOptions,
)
from glide.async_commands.sorted_set import (
Expand Down Expand Up @@ -908,6 +909,37 @@ async def test_rpushx(self, redis_client: TRedisClient):
with pytest.raises(RequestError):
await redis_client.rpushx(key2, [])

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_linsert(self, redis_client: TRedisClient):
key1 = get_random_string(10)
key2 = get_random_string(10)

assert await redis_client.lpush(key1, ["4", "3", "2", "1"]) == 4
assert await redis_client.linsert(key1, InsertPosition.BEFORE, "2", "1.5") == 5
assert await redis_client.linsert(key1, InsertPosition.AFTER, "3", "3.5") == 6
assert await redis_client.lrange(key1, 0, -1) == [
"1",
"1.5",
"2",
"3",
"3.5",
"4",
]

assert (
await redis_client.linsert(
"non_existing_key", InsertPosition.BEFORE, "pivot", "elem"
)
== 0
)
assert await redis_client.linsert(key1, InsertPosition.AFTER, "5", "6") == -1

# key exists, but it is not a list
assert await redis_client.set(key2, "value") == OK
with pytest.raises(RequestError):
await redis_client.linsert(key2, InsertPosition.AFTER, "p", "e")

@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):
Expand Down
5 changes: 4 additions & 1 deletion python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import pytest
from glide import RequestError
from glide.async_commands.core import GeospatialData
from glide.async_commands.core import GeospatialData, InsertPosition
from glide.async_commands.sorted_set import (
InfBound,
LexBoundary,
Expand Down Expand Up @@ -38,6 +38,7 @@ async def transaction_test(
key7 = "{{{}}}:{}".format(keyslot, get_random_string(3))
key8 = "{{{}}}:{}".format(keyslot, get_random_string(3))
key9 = "{{{}}}:{}".format(keyslot, get_random_string(3))
key10 = "{{{}}}:{}".format(keyslot, get_random_string(3)) # list

value = datetime.now(timezone.utc).strftime("%m/%d/%Y, %H:%M:%S")
value2 = get_random_string(5)
Expand Down Expand Up @@ -147,6 +148,8 @@ async def transaction_test(
args.append([value2, value])
transaction.lpop_count(key5, 2)
args.append([value2, value])
transaction.linsert(key5, InsertPosition.BEFORE, "non_existing_pivot", "element")
args.append(0)

transaction.rpush(key6, [value, value2, value2])
args.append(3)
Expand Down

0 comments on commit 16d9ba2

Please sign in to comment.