Skip to content

Commit 2251957

Browse files
committed
Python: adds MSETNX command
1 parent 26b69d1 commit 2251957

File tree

5 files changed

+71
-0
lines changed

5 files changed

+71
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* Node: Added XLEN command ([#1555](https://github.com/aws/glide-for-redis/pull/1555))
2525
* Node: Added ZINTERCARD command ([#1553](https://github.com/aws/glide-for-redis/pull/1553))
2626
* Python: Added LMPOP and BLMPOP commands ([#1547](https://github.com/aws/glide-for-redis/pull/1547))
27+
* Python: Added MSETNX command ([#1565](https://github.com/aws/glide-for-redis/pull/1565))
2728

2829
### Breaking Changes
2930
* Node: Update XREAD to return a Map of Map ([#1494](https://github.com/aws/glide-for-redis/pull/1494))

python/python/glide/async_commands/core.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,36 @@ async def mset(self, key_value_map: Mapping[str, str]) -> TOK:
733733
parameters.extend(pair)
734734
return cast(TOK, await self._execute_command(RequestType.MSet, parameters))
735735

736+
async def msetnx(self, key_value_map: Mapping[str, str]) -> bool:
737+
"""
738+
Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
739+
more keys already exist, the entire operation fails.
740+
741+
Note:
742+
When in cluster mode, all `keys` must map to the same hash slot.
743+
744+
See https://valkey.io/commands/msetnx/ for more details.
745+
746+
Args:
747+
key_value_map (Mapping[str, str]): A key-value map consisting of keys and their respective values to set.
748+
749+
Returns:
750+
bool: True if all keys were set. False if no key was set.
751+
752+
Examples:
753+
>>> await client.msetnx({"key1": "value1", "key2": "value2"})
754+
True
755+
>>> await client.msetnx({"key2": "value4", "key3": "value5"})
756+
False
757+
"""
758+
parameters: List[str] = []
759+
for pair in key_value_map.items():
760+
parameters.extend(pair)
761+
return cast(
762+
bool,
763+
await self._execute_command(RequestType.MSetNX, parameters),
764+
)
765+
736766
async def mget(self, keys: List[str]) -> List[Optional[str]]:
737767
"""
738768
Retrieve the values of multiple keys.

python/python/glide/async_commands/transaction.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,24 @@ def mset(self: TTransaction, key_value_map: Mapping[str, str]) -> TTransaction:
309309
parameters.extend(pair)
310310
return self.append_command(RequestType.MSet, parameters)
311311

312+
def msetnx(self, key_value_map: Mapping[str, str]) -> bool:
313+
"""
314+
Sets multiple keys to values if the key does not exist. The operation is atomic, and if one or
315+
more keys already exist, the entire operation fails.
316+
317+
See https://valkey.io/commands/msetnx/ for more details.
318+
319+
Args:
320+
key_value_map (Mapping[str, str]): A key-value map consisting of keys and their respective values to set.
321+
322+
Commands response:
323+
bool: True if all keys were set. False if no key was set.
324+
"""
325+
parameters: List[str] = []
326+
for pair in key_value_map.items():
327+
parameters.extend(pair)
328+
return self.append_command(RequestType.MSetNX, parameters)
329+
312330
def mget(self: TTransaction, keys: List[str]) -> TTransaction:
313331
"""
314332
Retrieve the values of multiple keys.

python/python/tests/test_async_client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,25 @@ async def test_mset_mget(self, redis_client: TRedisClient):
524524
keys[-1] = None
525525
assert mget_res == keys
526526

527+
@pytest.mark.parametrize("cluster_mode", [True, False])
528+
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
529+
async def test_msetnx(self, redis_client: TRedisClient):
530+
key1 = f"{{key}}-1{get_random_string(5)}"
531+
key2 = f"{{key}}-2{get_random_string(5)}"
532+
key3 = f"{{key}}-3{get_random_string(5)}"
533+
non_existing = get_random_string(5)
534+
value = get_random_string(5)
535+
key_value_map1 = {key1: value, key2: value}
536+
key_value_map2 = {key2: get_random_string(5), key3: value}
537+
538+
assert await redis_client.msetnx(key_value_map1) is True
539+
mget_res = await redis_client.mget([key1, key2, non_existing])
540+
assert mget_res == [value, value, None]
541+
542+
assert await redis_client.msetnx(key_value_map2) is False
543+
assert await redis_client.get(key3) is None
544+
assert await redis_client.get(key2) == value
545+
527546
@pytest.mark.parametrize("cluster_mode", [True, False])
528547
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
529548
async def test_ping(self, redis_client: TRedisClient):
@@ -4183,6 +4202,7 @@ async def test_multi_key_command_returns_cross_slot_error(
41834202
redis_client.blmove(
41844203
"abc", "zxy", ListDirection.LEFT, ListDirection.LEFT, 1
41854204
),
4205+
redis_client.msetnx({"abc": "abc", "zxy": "zyx"}),
41864206
]
41874207

41884208
if not await check_if_server_version_lt(redis_client, "7.0.0"):

python/python/tests/test_transaction.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ async def transaction_test(
103103

104104
transaction.mset({key: value, key2: value2})
105105
args.append(OK)
106+
transaction.msetnx({key: value, key2: value2})
107+
args.append(False)
106108
transaction.mget([key, key2])
107109
args.append([value, value2])
108110

0 commit comments

Comments
 (0)