Skip to content

Commit

Permalink
Python: add RENAMENX command (#1492)
Browse files Browse the repository at this point in the history
* Python: add RENAMENX command (#320)

* Update tests to align with Node tests
  • Loading branch information
aaron-congo authored May 29, 2024
1 parent 6b425f1 commit 7c4c9b7
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Python: Added OBJECT IDLETIME command ([#1474](https://github.com/aws/glide-for-redis/pull/1474))
* Node: Added RENAMENX command ([#1483](https://github.com/aws/glide-for-redis/pull/1483))
* Python: Added OBJECT REFCOUNT command ([#1485](https://github.com/aws/glide-for-redis/pull/1485))
* Python: Added RENAMENX command ([#1492](https://github.com/aws/glide-for-redis/pull/1492))

## 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
25 changes: 25 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,30 @@ 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)}"
non_existing_key = f"{{testKey}}:5-{get_random_string(10)}"

# Verify that attempting to rename a non-existing key throws an error
with pytest.raises(RequestError):
assert await redis_client.renamenx(non_existing_key, key1)

# Test RENAMENX with string values
assert await redis_client.set(key1, "key1") == OK
assert await redis_client.set(key3, "key3") == OK
# Test that RENAMENX can rename key1 to key2 (where key2 does not yet exist)
assert await redis_client.renamenx(key1, key2) is True
# Verify that key2 now holds the value that was in key1
assert await redis_client.get(key2) == "key1"
# Verify that RENAMENX doesn't rename key2 to key3, since key3 already exists
assert await redis_client.renamenx(key2, key3) is False
# Verify that 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 @@ -3462,6 +3486,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

0 comments on commit 7c4c9b7

Please sign in to comment.