Skip to content

Commit

Permalink
Python: adds MSETNX command (valkey-io#1565)
Browse files Browse the repository at this point in the history
  • Loading branch information
shohamazon authored and cyip10 committed Jun 24, 2024
1 parent db9d3e5 commit d608d44
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
* Node: Added XLEN command ([#1555](https://github.com/aws/glide-for-redis/pull/1555))
* Node: Added ZINTERCARD command ([#1553](https://github.com/aws/glide-for-redis/pull/1553))
* Python: Added LMPOP and BLMPOP commands ([#1547](https://github.com/aws/glide-for-redis/pull/1547))
* Python: Added MSETNX command ([#1565](https://github.com/aws/glide-for-redis/pull/1565))
* Python: Added MOVE command ([#1566](https://github.com/aws/glide-for-redis/pull/1566))
* Node: Added OBJECT IDLETIME command ([#1567](https://github.com/aws/glide-for-redis/pull/1567))
* Node: Added OBJECT REFCOUNT command ([#1568](https://github.com/aws/glide-for-redis/pull/1568))
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 @@ -733,6 +733,36 @@ async def mset(self, key_value_map: Mapping[str, str]) -> TOK:
parameters.extend(pair)
return cast(TOK, await self._execute_command(RequestType.MSet, parameters))

async def msetnx(self, key_value_map: Mapping[str, str]) -> bool:
"""
Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
more keys already exist, the entire operation fails.
Note:
When in cluster mode, all keys in `key_value_map` must map to the same hash slot.
See https://valkey.io/commands/msetnx/ for more details.
Args:
key_value_map (Mapping[str, str]): A key-value map consisting of keys and their respective values to set.
Returns:
bool: True if all keys were set. False if no key was set.
Examples:
>>> await client.msetnx({"key1": "value1", "key2": "value2"})
True
>>> await client.msetnx({"key2": "value4", "key3": "value5"})
False
"""
parameters: List[str] = []
for pair in key_value_map.items():
parameters.extend(pair)
return cast(
bool,
await self._execute_command(RequestType.MSetNX, parameters),
)

async def mget(self, keys: List[str]) -> List[Optional[str]]:
"""
Retrieve the values of multiple keys.
Expand Down
18 changes: 18 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,24 @@ def mset(self: TTransaction, key_value_map: Mapping[str, str]) -> TTransaction:
parameters.extend(pair)
return self.append_command(RequestType.MSet, parameters)

def msetnx(self: TTransaction, key_value_map: Mapping[str, str]) -> TTransaction:
"""
Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
more keys already exist, the entire operation fails.
See https://valkey.io/commands/msetnx/ for more details.
Args:
key_value_map (Mapping[str, str]): A key-value map consisting of keys and their respective values to set.
Commands response:
bool: True if all keys were set. False if no key was set.
"""
parameters: List[str] = []
for pair in key_value_map.items():
parameters.extend(pair)
return self.append_command(RequestType.MSetNX, parameters)

def mget(self: TTransaction, keys: List[str]) -> TTransaction:
"""
Retrieve the values of multiple keys.
Expand Down
20 changes: 20 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,25 @@ async def test_mset_mget(self, redis_client: TRedisClient):
keys[-1] = None
assert mget_res == keys

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_msetnx(self, redis_client: TRedisClient):
key1 = f"{{key}}-1{get_random_string(5)}"
key2 = f"{{key}}-2{get_random_string(5)}"
key3 = f"{{key}}-3{get_random_string(5)}"
non_existing = get_random_string(5)
value = get_random_string(5)
key_value_map1 = {key1: value, key2: value}
key_value_map2 = {key2: get_random_string(5), key3: value}

assert await redis_client.msetnx(key_value_map1) is True
mget_res = await redis_client.mget([key1, key2, non_existing])
assert mget_res == [value, value, None]

assert await redis_client.msetnx(key_value_map2) is False
assert await redis_client.get(key3) is None
assert await redis_client.get(key2) == value

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_ping(self, redis_client: TRedisClient):
Expand Down Expand Up @@ -4221,6 +4240,7 @@ async def test_multi_key_command_returns_cross_slot_error(
redis_client.blmove(
"abc", "zxy", ListDirection.LEFT, ListDirection.LEFT, 1
),
redis_client.msetnx({"abc": "abc", "zxy": "zyx"}),
]

if not await check_if_server_version_lt(redis_client, "7.0.0"):
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 @@ -104,6 +104,8 @@ async def transaction_test(

transaction.mset({key: value, key2: value2})
args.append(OK)
transaction.msetnx({key: value, key2: value2})
args.append(False)
transaction.mget([key, key2])
args.append([value, value2])

Expand Down

0 comments on commit d608d44

Please sign in to comment.