Skip to content

Commit

Permalink
Python: Add SETRANGE command (valkey-io#1453)
Browse files Browse the repository at this point in the history
* Added Python: SETRANGE command (#299)

* Update CHANGELOG to include Python: SETRANGE command

* Updated comments

* Updated test comment

---------

Co-authored-by: Andrew Carbonetto <[email protected]>
  • Loading branch information
yipin-chen and acarbonetto authored May 24, 2024
1 parent 7e1e04d commit 201f551
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* Python: Added SDIFFSTORE command ([#1449](https://github.com/aws/glide-for-redis/pull/1449))
* Python: Added SINTERSTORE command ([#1459](https://github.com/aws/glide-for-redis/pull/1459))
* Python: Added SMISMEMBER command ([#1461](https://github.com/aws/glide-for-redis/pull/1461))

* Python: Added SETRANGE command ([#1453](https://github.com/aws/glide-for-redis/pull/1453)

#### Fixes
* Python: Fix typing error "‘type’ object is not subscriptable" ([#1203](https://github.com/aws/glide-for-redis/pull/1203))
Expand Down
30 changes: 30 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,36 @@ async def incrbyfloat(self, key: str, amount: float) -> float:
await self._execute_command(RequestType.IncrByFloat, [key, str(amount)]),
)

async def setrange(self, key: str, offset: int, value: str) -> int:
"""
Overwrites part of the string stored at `key`, starting at the specified
`offset`, for the entire length of `value`.
If the `offset` is larger than the current length of the string at `key`,
the string is padded with zero bytes to make `offset` fit. Creates the `key`
if it doesn't exist.
See https://valkey.io/commands/setrange for more details.
Args:
key (str): The key of the string to update.
offset (int): The position in the string where `value` should be written.
value (str): The string written with `offset`.
Returns:
int: The length of the string stored at `key` after it was modified.
Examples:
>>> await client.set("key", "Hello World")
>>> await client.setrange("key", 6, "Redis")
11 # The length of the string stored at `key` after it was modified.
"""
return cast(
int,
await self._execute_command(
RequestType.SetRange, [key, str(offset), value]
),
)

async def mset(self, key_value_map: Mapping[str, str]) -> TOK:
"""
Set multiple keys to multiple values in a single atomic operation.
Expand Down
20 changes: 20 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,26 @@ def decrby(self: TTransaction, key: str, amount: int) -> TTransaction:
"""
return self.append_command(RequestType.DecrBy, [key, str(amount)])

def setrange(self: TTransaction, key: str, offset: int, value: str) -> TTransaction:
"""
Overwrites part of the string stored at `key`, starting at the specified
`offset`, for the entire length of `value`.
If the `offset` is larger than the current length of the string at `key`,
the string is padded with zero bytes to make `offset` fit. Creates the `key`
if it doesn't exist.
See https://valkey.io/commands/setrange for more details.
Args:
key (str): The key of the string to update.
offset (int): The position in the string where `value` should be written.
value (str): The string written with `offset`.
Command response:
int: The length of the string stored at `key` after it was modified.
"""
return self.append_command(RequestType.SetRange, [key, str(offset), value])

def hset(
self: TTransaction, key: str, field_value_map: Mapping[str, str]
) -> TTransaction:
Expand Down
22 changes: 22 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,28 @@ async def test_decr_with_str_value(self, redis_client: TRedisClient):

assert "value is not an integer" in str(e)

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

# test new key and existing key
assert await redis_client.setrange(key1, 0, "Hello World") == 11
assert await redis_client.setrange(key1, 6, "GLIDE") == 11

# offset > len
assert await redis_client.setrange(key1, 15, "GLIDE") == 20

# negative offset
with pytest.raises(RequestError):
assert await redis_client.setrange(key1, -1, "GLIDE")

# non-string key throws RequestError
assert await redis_client.lpush(key2, ["_"]) == 1
with pytest.raises(RequestError):
assert await redis_client.setrange(key2, 0, "_")

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_hset_hget_hgetall(self, redis_client: TRedisClient):
Expand Down
2 changes: 2 additions & 0 deletions python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ async def transaction_test(

transaction.set(key, value)
args.append(OK)
transaction.setrange(key, 0, value)
args.append(len(value))
transaction.get(key)
args.append(value)
transaction.type(key)
Expand Down

0 comments on commit 201f551

Please sign in to comment.