Skip to content

Commit

Permalink
Python: adds HSETNX command
Browse files Browse the repository at this point in the history
  • Loading branch information
shohamazon committed Feb 20, 2024
1 parent 5d0a2e7 commit bef4427
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 5 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 @@
* Node: Added ZCOUNT command ([#909](https://github.com/aws/glide-for-redis/pull/909))
* Python: Added ECHO command ([#953](https://github.com/aws/glide-for-redis/pull/953))
* Python: Added ZPOPMIN command ([#975](https://github.com/aws/glide-for-redis/pull/975))
* Python: Added HSETNX command. ([#954](https://github.com/aws/glide-for-redis/pull/954))

#### Features
* Python, Node: Added support in Lua Scripts ([#775](https://github.com/aws/glide-for-redis/pull/775), [#860](https://github.com/aws/glide-for-redis/pull/860))
Expand Down
2 changes: 1 addition & 1 deletion glide-core/src/client/value_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ pub(crate) fn expected_type_for_cmd(cmd: &Cmd) -> Option<ExpectedReturnType> {
Some(ExpectedReturnType::Map)
}
b"INCRBYFLOAT" | b"HINCRBYFLOAT" => Some(ExpectedReturnType::Double),
b"HEXISTS" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT" => {
b"HEXISTS" | b"HSETNX" | b"EXPIRE" | b"EXPIREAT" | b"PEXPIRE" | b"PEXPIREAT" => {
Some(ExpectedReturnType::Boolean)
}
b"SMEMBERS" => Some(ExpectedReturnType::Set),
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ enum RequestType {
HLen = 69;
Echo = 70;
ZPopMin = 71;
HSetNX = 72;
}

message Command {
Expand Down
1 change: 1 addition & 0 deletions glide-core/src/socket_listener.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ fn get_command(request: &Command) -> Option<Cmd> {
RequestType::HLen => Some(cmd("HLEN")),
RequestType::Echo => Some(cmd("ECHO")),
RequestType::ZPopMin => Some(cmd("ZPOPMIN")),
RequestType::HSetNX => Some(cmd("HSETNX")),
}
}

Expand Down
37 changes: 34 additions & 3 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,7 @@ async def hset(self, key: str, field_value_map: Mapping[str, str]) -> int:
int: The number of fields that were added to the hash.
Example:
>>> hset("my_hash", {"field": "value", "field2": "value2"})
>>> await client.hset("my_hash", {"field": "value", "field2": "value2"})
2
"""
field_value_list: List[str] = [key]
Expand All @@ -444,16 +444,47 @@ async def hget(self, key: str, field: str) -> Optional[str]:
Returns None if `field` is not presented in the hash or `key` does not exist.
Examples:
>>> hget("my_hash", "field")
>>> await client.hget("my_hash", "field")
"value"
>>> hget("my_hash", "nonexistent_field")
>>> await client.hget("my_hash", "nonexistent_field")
None
"""
return cast(
Optional[str],
await self._execute_command(RequestType.HashGet, [key, field]),
)

async def hsetnx(
self,
key: str,
field: str,
value: str,
) -> bool:
"""
Sets `field` in the hash stored at `key` to `value`, only if `field` does not yet exist.
If `key` does not exist, a new key holding a hash is created.
If `field` already exists, this operation has no effect.
See https://redis.io/commands/hsetnx/ for more details.
Args:
key (str): The key of the hash.
field (str): The field to set the value for.
value (str): The value to set.
Returns:
bool: True if the field was set, False if the field already existed and was not set.
Examples:
>>> await client.hsetnx("my_hash", "field", "value")
True # Indicates that the field "field" was set successfully in the hash "my_hash".
>>> await client.hsetnx("my_hash", "field", "new_value")
False # Indicates that the field "field" already existed in the hash "my_hash" and was not set again.
"""
return cast(
bool,
await self._execute_command(RequestType.HSetNX, [key, field, value]),
)

async def hincrby(self, key: str, field: str, amount: int) -> int:
"""
Increment or decrement the value of a `field` in the hash stored at `key` by the specified amount.
Expand Down
22 changes: 22 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,28 @@ def hget(self: TTransaction, key: str, field: str) -> TTransaction:
"""
return self.append_command(RequestType.HashGet, [key, field])

def hsetnx(
self: TTransaction,
key: str,
field: str,
value: str,
) -> TTransaction:
"""
Sets `field` in the hash stored at `key` to `value`, only if `field` does not yet exist.
If `key` does not exist, a new key holding a hash is created.
If `field` already exists, this operation has no effect.
See https://redis.io/commands/hsetnx/ for more details.
Args:
key (str): The key of the hash.
field (str): The field to set the value for.
value (str): The value to set.
Commands response:
bool: True if the field was set, False if the field already existed and was not set.
"""
return self.append_command(RequestType.HSetNX, [key, field, value])

def hincrby(self: TTransaction, key: str, field: str, amount: int) -> TTransaction:
"""
Increment or decrement the value of a `field` in the hash stored at `key` by the specified amount.
Expand Down
14 changes: 14 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,20 @@ async def test_hdel(self, redis_client: TRedisClient):
assert await redis_client.hdel(key, ["nonExistingField"]) == 0
assert await redis_client.hdel("nonExistingKey", [field3]) == 0

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

assert await redis_client.hsetnx(key, field, "value") == True
assert await redis_client.hsetnx(key, field, "new value") == False
assert await redis_client.hget(key, field) == "value"
key = get_random_string(5)
assert await redis_client.set(key, "value") == OK
with pytest.raises(RequestError):
await redis_client.hsetnx(key, field, "value")

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_hmget(self, redis_client: TRedisClient):
Expand Down
3 changes: 2 additions & 1 deletion python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ def transaction_test(
args.append(value2)
transaction.hlen(key4)
args.append(2)

transaction.hsetnx(key4, key, value)
args.append(False)
transaction.hincrby(key4, key3, 5)
args.append(5)
transaction.hincrbyfloat(key4, key3, 5.5)
Expand Down

0 comments on commit bef4427

Please sign in to comment.