Skip to content

Commit

Permalink
Python: add GETRANGE command (valkey-io#1585)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
  • Loading branch information
2 people authored and cyip10 committed Jun 24, 2024
1 parent 1651287 commit b349ec8
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* Python: Added XLEN command ([#1503](https://github.com/aws/glide-for-redis/pull/1503))
* Python: Added LASTSAVE command ([#1509](https://github.com/aws/glide-for-redis/pull/1509))
* Python: Added GETDEL command ([#1514](https://github.com/aws/glide-for-redis/pull/1514))
* Python: Added GETRANGE command ([#1585](https://github.com/aws/glide-for-redis/pull/1585))
* Python: Added ZINTER, ZUNION commands ([#1478](https://github.com/aws/glide-for-redis/pull/1478))
* Python: Added SINTERCARD command ([#1511](https://github.com/aws/glide-for-redis/pull/1511))
* Python: Added SORT command ([#1439](https://github.com/aws/glide-for-redis/pull/1439))
Expand Down
37 changes: 37 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,43 @@ async def getdel(self, key: str) -> Optional[str]:
Optional[str], await self._execute_command(RequestType.GetDel, [key])
)

async def getrange(self, key: str, start: int, end: int) -> str:
"""
Returns the substring of the string value stored at `key`, determined by the offsets `start` and `end` (both are inclusive).
Negative offsets can be used in order to provide an offset starting from the end of the string.
So `-1` means the last character, `-2` the penultimate and so forth.
If `key` does not exist, an empty string is returned. If `start` or `end`
are out of range, returns the substring within the valid range of the string.
See https://valkey.io/commands/getrange/ for more details.
Args:
key (str): The key of the string.
start (int): The starting offset.
end (int): The ending offset.
Returns:
str: A substring extracted from the value stored at `key`.
Examples:
>>> await client.set("mykey", "This is a string")
>>> await client.getrange("mykey", 0, 3)
"This"
>>> await client.getrange("mykey", -3, -1)
"ing" # extracted last 3 characters of a string
>>> await client.getrange("mykey", 0, 100)
"This is a string"
>>> await client.getrange("non_existing", 5, 6)
""
"""
return cast(
str,
await self._execute_command(
RequestType.GetRange, [key, str(start), str(end)]
),
)

async def append(self, key: str, value: str) -> int:
"""
Appends a value to a key.
Expand Down
21 changes: 21 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,27 @@ def getdel(self: TTransaction, key: str) -> TTransaction:
"""
return self.append_command(RequestType.GetDel, [key])

def getrange(self: TTransaction, key: str, start: int, end: int) -> TTransaction:
"""
Returns the substring of the string value stored at `key`, determined by the offsets `start` and `end` (both are inclusive).
Negative offsets can be used in order to provide an offset starting from the end of the string.
So `-1` means the last character, `-2` the penultimate and so forth.
If `key` does not exist, an empty string is returned. If `start` or `end`
are out of range, returns the substring within the valid range of the string.
See https://valkey.io/commands/getrange/ for more details.
Args:
key (str): The key of the string.
start (int): The starting offset.
end (int): The ending offset.
Commands response:
str: A substring extracted from the value stored at `key`.
"""
return self.append_command(RequestType.GetRange, [key, str(start), str(end)])

def set(
self: TTransaction,
key: str,
Expand Down
34 changes: 34 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,40 @@ async def test_getdel(self, redis_client: TRedisClient):
with pytest.raises(RequestError) as e:
await redis_client.getdel(list_key)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_getrange(self, redis_client: TRedisClient):
key = get_random_string(16)
value = get_random_string(10)
non_string_key = get_random_string(10)

assert await redis_client.set(key, value) == OK
assert await redis_client.getrange(key, 0, 3) == value[:4]
assert await redis_client.getrange(key, -3, -1) == value[-3:]
assert await redis_client.getrange(key, 0, -1) == value

# out of range
assert await redis_client.getrange(key, 10, 100) == value[10:]
assert await redis_client.getrange(key, -200, -3) == value[-200:-2]
assert await redis_client.getrange(key, 100, 200) == ""

# incorrect range
assert await redis_client.getrange(key, -1, -3) == ""

# a redis bug, fixed in version 8: https://github.com/redis/redis/issues/13207
if await check_if_server_version_lt(redis_client, "8.0.0"):
assert await redis_client.getrange(key, -200, -100) == value[0]
else:
assert await redis_client.getrange(key, -200, -100) == ""

if await check_if_server_version_lt(redis_client, "8.0.0"):
assert await redis_client.getrange(non_string_key, 0, -1) == ""

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

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_config_reset_stat(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 @@ -101,6 +101,8 @@ async def transaction_test(

transaction.set(key, value)
args.append(OK)
transaction.getrange(key, 0, -1)
args.append(value)
transaction.getdel(key)
args.append(value)
transaction.getdel(key)
Expand Down

0 comments on commit b349ec8

Please sign in to comment.