Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python: add RENAMENX command #320

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Python: Added OBJECT ENCODING command ([#1471](https://github.com/aws/glide-for-redis/pull/1471))
* Python: Added OBJECT FREQ command ([#1472](https://github.com/aws/glide-for-redis/pull/1472))
* Python: Added OBJECT IDLETIME command ([#1474](https://github.com/aws/glide-for-redis/pull/1474))
* Python: Added RENAMENX command (TODO: add PR link)

## 0.4.0 (2024-05-26)

Expand Down
25 changes: 25 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,31 @@ async def rename(self, key: str, new_key: str) -> TOK:
TOK, await self._execute_command(RequestType.Rename, [key, new_key])
)

async def renamenx(self, key: str, new_key: str) -> bool:
"""
Renames `key` to `new_key` if `new_key` does not yet exist.

See https://valkey.io/commands/renamenx for more details.

Note:
When in cluster mode, both `key` and `new_key` must map to the same hash slot.

Args:
key (str): The key to rename.
new_key (str): The new key name.

Returns:
bool: True if `key` was renamed to `new_key`, or False if `new_key` already exists.

Examples:
>>> await client.renamenx("old_key", "new_key")
True # "old_key" was renamed to "new_key"
"""
return cast(
bool,
await self._execute_command(RequestType.RenameNX, [key, new_key]),
)

async def delete(self, keys: List[str]) -> int:
"""
Delete one or more keys from the database. A key is ignored if it does not exist.
Expand Down
15 changes: 15 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,21 @@ def rename(self: TTransaction, key: str, new_key: str) -> TTransaction:
"""
return self.append_command(RequestType.Rename, [key, new_key])

def renamenx(self: TTransaction, key: str, new_key: str) -> TTransaction:
"""
Renames `key` to `new_key` if `new_key` does not yet exist.

See https://valkey.io/commands/renamenx for more details.

Args:
key (str): The key to rename.
new_key (str): The new key name.

Command response:
bool: True if `key` was renamed to `new_key`, or False if `new_key` already exists.
"""
return self.append_command(RequestType.RenameNX, [key, new_key])

def custom_command(self: TTransaction, command_args: List[str]) -> TTransaction:
"""
Executes a single command, without checking inputs.
Expand Down
23 changes: 23 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,28 @@ async def test_rename(self, redis_client: TRedisClient):
"{same_slot}" + "non_existing_key", "{same_slot}" + "_rename"
)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_renamenx(self, redis_client: TRedisClient):
key1 = f"{{testKey}}:1-{get_random_string(10)}"
key2 = f"{{testKey}}:2-{get_random_string(10)}"
key3 = f"{{testKey}}:3-{get_random_string(10)}"
string_key = f"{{testKey}}:4-{get_random_string(10)}"
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"

with pytest.raises(RequestError):
assert await redis_client.renamenx(non_existing_key, key1)

assert await redis_client.set(key1, "foo") == OK
assert await redis_client.renamenx(key1, key2) is True
assert await redis_client.set(key3, "key3") == OK
assert await redis_client.renamenx(key2, key3) is False
assert await redis_client.get(key2) == "foo"
assert await redis_client.delete([key1, key2]) == 1

# Verify key3 remains unchanged
assert await redis_client.get(key3) == "key3"

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_exists(self, redis_client: TRedisClient):
Expand Down Expand Up @@ -3451,6 +3473,7 @@ async def test_multi_key_command_returns_cross_slot_error(
redis_client.sinterstore("abc", ["zxy", "lkn"]),
redis_client.sdiff(["abc", "zxy", "lkn"]),
redis_client.sdiffstore("abc", ["def", "ghi"]),
redis_client.renamenx("abc", "def"),
]

if not await check_if_server_version_lt(redis_client, "7.0.0"):
Expand Down
3 changes: 3 additions & 0 deletions python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ async def transaction_test(
transaction.mget([key, key2])
args.append([value, value2])

transaction.renamenx(key, key2)
args.append(False)

transaction.incr(key3)
args.append(1)
transaction.incrby(key3, 2)
Expand Down
Loading