Skip to content

Commit 69b8245

Browse files
authored
Merge pull request #246 from jogehl/main
Add HSETEX
2 parents c291080 + 2c05ea3 commit 69b8245

File tree

3 files changed

+128
-0
lines changed

3 files changed

+128
-0
lines changed

tests/test_asyncio/test_commands.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2151,6 +2151,32 @@ async def test_hstrlen(self, r: valkey.Valkey):
21512151
assert await r.hstrlen("a", "1") == 2
21522152
assert await r.hstrlen("a", "2") == 3
21532153

2154+
@skip_if_server_version_lt("9.0.0")
2155+
async def test_hsetex(self, r):
2156+
assert await r.hsetex("a", "field1", "value1", ex=5) == 1
2157+
assert await r.hget("a", "field1") == b"value1"
2158+
assert await r.hsetex("a", "field1", "value2", ex=5) == 1
2159+
assert await r.hget("a", "field1") == b"value2"
2160+
2161+
@skip_if_server_version_lt("9.0.0")
2162+
async def test_hsetex_invalid_params(self, r):
2163+
with pytest.raises(exceptions.DataError):
2164+
await r.hsetex("a", "field1", "value1", ex=5, px=5000)
2165+
2166+
@skip_if_server_version_lt("9.0.0")
2167+
async def test_hsetex_px(self, r):
2168+
assert await r.hsetex("a", "field1", "value1", px=5000) == 1
2169+
assert await r.hget("a", "field1") == b"value1"
2170+
assert await r.hsetex("a", "field1", "value2", px=5000) == 1
2171+
assert await r.hget("a", "field1") == b"value2"
2172+
2173+
@skip_if_server_version_lt("9.0.0")
2174+
async def test_hsetex_mapping(self, r):
2175+
mapping = {"field1": "value1", "field2": "value2"}
2176+
assert await r.hsetex("a", mapping=mapping, ex=5) == 1
2177+
assert await r.hget("a", "field1") == b"value1"
2178+
assert await r.hget("a", "field2") == b"value2"
2179+
21542180
# SORT
21552181
async def test_sort_basic(self, r: valkey.Valkey):
21562182
await r.rpush("a", "3", "2", "1", "4")

tests/test_commands.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3236,6 +3236,32 @@ def test_hstrlen(self, r):
32363236
assert r.hstrlen("a", "1") == 2
32373237
assert r.hstrlen("a", "2") == 3
32383238

3239+
@skip_if_server_version_lt("9.0.0")
3240+
def test_hsetex(self, r):
3241+
assert r.hsetex("a", "field1", "value1", ex=5) == 1
3242+
assert r.hget("a", "field1") == b"value1"
3243+
assert r.hsetex("a", "field1", "value2", ex=5) == 1
3244+
assert r.hget("a", "field1") == b"value2"
3245+
3246+
@skip_if_server_version_lt("9.0.0")
3247+
def test_hsetex_invalid_params(self, r):
3248+
with pytest.raises(exceptions.DataError):
3249+
r.hsetex("a", "field1", "value1", ex=5, px=5000) # Both ex and px provided
3250+
3251+
@skip_if_server_version_lt("9.0.0")
3252+
def test_hsetex_px(self, r):
3253+
assert r.hsetex("a", "field1", "value1", px=5000) == 1
3254+
assert r.hget("a", "field1") == b"value1"
3255+
assert r.hsetex("a", "field1", "value2", px=5000) == 1
3256+
assert r.hget("a", "field1") == b"value2"
3257+
3258+
@skip_if_server_version_lt("9.0.0")
3259+
def test_hsetex_mapping(self, r):
3260+
mapping = {"field1": "value1", "field2": "value2"}
3261+
assert r.hsetex("a", mapping=mapping, ex=5) == 1
3262+
assert r.hget("a", "field1") == b"value1"
3263+
assert r.hget("a", "field2") == b"value2"
3264+
32393265
# SORT
32403266
def test_sort_basic(self, r):
32413267
r.rpush("a", "3", "2", "1", "4")

valkey/commands/core.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5055,6 +5055,82 @@ def hsetnx(self, name: str, key: str, value: str) -> Union[Awaitable[bool], bool
50555055
"""
50565056
return self.execute_command("HSETNX", name, key, value)
50575057

5058+
def hsetex(
5059+
self,
5060+
name: str,
5061+
key: Optional[str] = None,
5062+
value: Optional[str] = None,
5063+
mapping: Optional[dict] = None,
5064+
items: Optional[list] = None,
5065+
ex: Union[ExpiryT, None] = None,
5066+
px: Union[ExpiryT, None] = None,
5067+
exat: Union[AbsExpiryT, None] = None,
5068+
pxat: Union[AbsExpiryT, None] = None,
5069+
keepttl: bool = False,
5070+
nx: bool = False,
5071+
xx: bool = False,
5072+
fnx: bool = False,
5073+
fxx: bool = False,
5074+
) -> Union[Awaitable[bool], bool]:
5075+
"""
5076+
Set key to value within hash ``name``,
5077+
``mapping`` accepts a dict of key/value pairs to be added to hash ``name``.
5078+
``items`` accepts a list of key/value pairs to be added to hash ``name``.
5079+
Set expiration options for the hash fields.
5080+
5081+
For more information see https://valkey.io/commands/hsetex
5082+
"""
5083+
5084+
if key is None and not mapping and not items:
5085+
raise DataError("'hsetex' with no key value pairs")
5086+
if int(key is not None) + int(mapping is not None) + int(items is not None) > 1:
5087+
raise DataError(
5088+
"Only one of 'key/value', 'mapping', or 'items' can be specified."
5089+
)
5090+
5091+
if int(keepttl) + sum(arg is not None for arg in [ex, px, exat, pxat]) > 1:
5092+
raise DataError(
5093+
"Only one of 'ex', 'px', 'exat', 'pxat', or 'keepttl' can be specified."
5094+
)
5095+
if nx and xx:
5096+
raise DataError("Only one of 'nx' or 'xx' can be specified.")
5097+
if fnx and fxx:
5098+
raise DataError("Only one of 'fnx' or 'fxx' can be specified.")
5099+
pieces = []
5100+
if ex is not None:
5101+
pieces.extend(["EX", ex])
5102+
if px is not None:
5103+
pieces.extend(["PX", px])
5104+
if exat is not None:
5105+
pieces.extend(["EXAT", exat])
5106+
if pxat is not None:
5107+
pieces.extend(["PXAT", pxat])
5108+
if nx:
5109+
pieces.append("NX")
5110+
if xx:
5111+
pieces.append("XX")
5112+
if fnx:
5113+
pieces.append("FNX")
5114+
if fxx:
5115+
pieces.append("FXX")
5116+
pieces.append("FIELDS")
5117+
if key is not None and value is not None:
5118+
pieces.append(1) # for one field
5119+
pieces.append(key)
5120+
pieces.append(value)
5121+
if mapping:
5122+
pieces.append(len(mapping))
5123+
for key, value in mapping.items():
5124+
if key is None or value is None:
5125+
raise DataError("'hsetex' mapping contains None key or value")
5126+
pieces.append(key)
5127+
pieces.append(value)
5128+
if items:
5129+
pieces.append(len(items) // 2)
5130+
pieces.extend(items)
5131+
5132+
return self.execute_command("HSETEX", name, *pieces)
5133+
50585134
def hmset(self, name: str, mapping: dict) -> Union[Awaitable[str], str]:
50595135
"""
50605136
Set key to value within hash ``name`` for each corresponding

0 commit comments

Comments
 (0)