Skip to content

Commit e7429b0

Browse files
authored
Fix for 380 (hincrby removes hash key ttl) (#381)
1 parent bf46e73 commit e7429b0

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

fakeredis/commands_mixins/hash_mixin.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class HashCommandsMixin:
2424
def _hset(self, key: CommandItem, *args: bytes) -> int:
2525
h = key.value
2626
keys_count = len(h.keys())
27-
h.update(dict(zip(*[iter(args)] * 2))) # type: ignore # https://stackoverflow.com/a/12739974/1056460
27+
h.update(dict(zip(*[iter(args)] * 2)), clear_expiration=True) # type: ignore # https://stackoverflow.com/a/12739974/1056460
2828
created = len(h.keys()) - keys_count
2929

3030
key.updated()
@@ -58,7 +58,7 @@ def hincrby(self, key: CommandItem, field: bytes, amount_bytes: bytes) -> int:
5858
amount = Int.decode(amount_bytes)
5959
field_value = Int.decode(key.value.get(field, b"0"), decode_error=msgs.INVALID_HASH_MSG)
6060
c = field_value + amount
61-
key.value[field] = self._encodeint(c)
61+
key.value.update({field: self._encodeint(c)}, clear_expiration=False)
6262
key.updated()
6363
return c
6464

@@ -68,7 +68,7 @@ def hincrbyfloat(self, key: CommandItem, field: bytes, amount: bytes) -> bytes:
6868
if not math.isfinite(c):
6969
raise SimpleError(msgs.NONFINITE_MSG)
7070
encoded = self._encodefloat(c, True)
71-
key.value[field] = encoded
71+
key.value.update({field: encoded}, clear_expiration=False)
7272
key.updated()
7373
return encoded
7474

fakeredis/model/_hash.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ def items(self) -> Iterable[Tuple[bytes, Any]]:
7676
self._expire_keys()
7777
return self._values.items()
7878

79-
def update(self, values: Dict[bytes, Any]) -> None:
79+
def update(self, values: Dict[bytes, Any], clear_expiration: bool) -> None:
8080
self._expire_keys()
81-
for k in values.keys():
82-
self.clear_key_expireat(k)
81+
if clear_expiration:
82+
for k in values.keys():
83+
self.clear_key_expireat(k)
8384
self._values.update(values)
8485

8586
def getall(self) -> Dict[bytes, Any]:

test/test_mixins/test_hash_commands.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33
import redis.client
44

55
from test import testtools
6-
from test.testtools import raw_command
7-
8-
9-
@pytest.mark.min_server("7.4")
10-
def test_hexpire_empty_key(r: redis.Redis):
11-
raw_command(r, "hexpire", b"", 2055010579, "fields", 2, b"\x89U\x04", b"6\x86\xf4\xdd")
126

137

148
def test_hstrlen_missing(r: redis.Redis):

test/test_mixins/test_hash_expire_commands.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,3 +339,28 @@ def test_hpttl_multiple_fields_mixed_conditions(r: redis.Redis):
339339
def test_hpttl_nonexistent_key(r: redis.Redis):
340340
r.delete("redis-key")
341341
assert r.hpttl("redis-key", "field1", "field2", "field3") == [-2, -2, -2]
342+
343+
344+
def test_hincrby_with_hash_key_expiration(r: redis.Redis):
345+
r.hincrby("foo", "counter")
346+
r.hexpire("foo", 10, "counter")
347+
assert r.hincrby("foo", "counter") == 2
348+
res = r.httl("foo", "counter")
349+
assert isinstance(res, list)
350+
assert len(res) == 1
351+
assert res[0] >= 0
352+
353+
354+
@pytest.mark.min_server("7.4")
355+
def test_hexpire_empty_key(r: redis.Redis):
356+
testtools.raw_command(r, "hexpire", b"", 2055010579, "fields", 2, b"\x89U\x04", b"6\x86\xf4\xdd")
357+
358+
359+
def test_hincrbyfloat_with_hash_key_expiration(r: redis.Redis):
360+
r.hincrbyfloat("foo", "counter", 1.0)
361+
r.hexpire("foo", 10, "counter")
362+
assert r.hincrbyfloat("foo", "counter", 2.0) == 3.0
363+
res = r.httl("foo", "counter")
364+
assert isinstance(res, list)
365+
assert len(res) == 1
366+
assert res[0] >= 0

0 commit comments

Comments
 (0)