From 32108c2400da82d8146a756b9b10fe186436e867 Mon Sep 17 00:00:00 2001 From: Salvatore Mesoraca Date: Wed, 27 Nov 2024 16:19:41 +0100 Subject: [PATCH] Remove expiration/TTL commands that are not supported by Valkey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for this commands may come in the future[1]. But it will take some time, so for now it's better to drop them. This is a breaking change for 6.1. Close #78 [1]: https://github.com/valkey-io/valkey/issues/640 Signed-off-by: Salvatore Mesoraca Signed-off-by: Raphaƫl Vinot --- tests/test_asyncio/test_hash.py | 303 ------------------------ tests/test_hash.py | 371 ----------------------------- valkey/commands/core.py | 404 -------------------------------- 3 files changed, 1078 deletions(-) delete mode 100644 tests/test_asyncio/test_hash.py delete mode 100644 tests/test_hash.py diff --git a/tests/test_asyncio/test_hash.py b/tests/test_asyncio/test_hash.py deleted file mode 100644 index 13549c75..00000000 --- a/tests/test_asyncio/test_hash.py +++ /dev/null @@ -1,303 +0,0 @@ -import asyncio -from datetime import datetime, timedelta - -import pytest -from tests.conftest import skip_if_server_version_lt - -pytestmark = pytest.mark.skip - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpire_basic(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert await r.hexpire("test:hash", 1, "field1") == [1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpire_with_timedelta(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert await r.hexpire("test:hash", timedelta(seconds=1), "field1") == [1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpire_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1"}) - assert await r.hexpire("test:hash", 2, "field1", xx=True) == [0] - assert await r.hexpire("test:hash", 2, "field1", nx=True) == [1] - assert await r.hexpire("test:hash", 1, "field1", xx=True) == [1] - assert await r.hexpire("test:hash", 2, "field1", nx=True) == [0] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - await r.hset("test:hash", "field1", "value1") - await r.hexpire("test:hash", 2, "field1") - assert await r.hexpire("test:hash", 1, "field1", gt=True) == [0] - assert await r.hexpire("test:hash", 1, "field1", lt=True) == [1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpire_nonexistent_key_or_field(r): - await r.delete("test:hash") - assert await r.hexpire("test:hash", 1, "field1") == [] - await r.hset("test:hash", "field1", "value1") - assert await r.hexpire("test:hash", 1, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpire_multiple_fields(r): - await r.delete("test:hash") - await r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - assert await r.hexpire("test:hash", 1, "field1", "field2") == [1, 1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is False - assert await r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpire_basic(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert await r.hpexpire("test:hash", 500, "field1") == [1] - await asyncio.sleep(0.6) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpire_with_timedelta(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert await r.hpexpire("test:hash", timedelta(milliseconds=500), "field1") == [1] - await asyncio.sleep(0.6) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpire_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1"}) - assert await r.hpexpire("test:hash", 1500, "field1", xx=True) == [0] - assert await r.hpexpire("test:hash", 1500, "field1", nx=True) == [1] - assert await r.hpexpire("test:hash", 500, "field1", xx=True) == [1] - assert await r.hpexpire("test:hash", 1500, "field1", nx=True) == [0] - await asyncio.sleep(0.6) - assert await r.hexists("test:hash", "field1") is False - await r.hset("test:hash", "field1", "value1") - await r.hpexpire("test:hash", 1000, "field1") - assert await r.hpexpire("test:hash", 500, "field1", gt=True) == [0] - assert await r.hpexpire("test:hash", 500, "field1", lt=True) == [1] - await asyncio.sleep(0.6) - assert await r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpire_nonexistent_key_or_field(r): - await r.delete("test:hash") - assert await r.hpexpire("test:hash", 500, "field1") == [] - await r.hset("test:hash", "field1", "value1") - assert await r.hpexpire("test:hash", 500, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpire_multiple_fields(r): - await r.delete("test:hash") - await r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - assert await r.hpexpire("test:hash", 500, "field1", "field2") == [1, 1] - await asyncio.sleep(0.6) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is False - assert await r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpireat_basic(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert await r.hexpireat("test:hash", exp_time, "field1") == [1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpireat_with_datetime(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = datetime.now() + timedelta(seconds=1) - assert await r.hexpireat("test:hash", exp_time, "field1") == [1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpireat_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1"}) - future_exp_time = int((datetime.now() + timedelta(seconds=2)).timestamp()) - past_exp_time = int((datetime.now() - timedelta(seconds=1)).timestamp()) - assert await r.hexpireat("test:hash", future_exp_time, "field1", xx=True) == [0] - assert await r.hexpireat("test:hash", future_exp_time, "field1", nx=True) == [1] - assert await r.hexpireat("test:hash", past_exp_time, "field1", gt=True) == [0] - assert await r.hexpireat("test:hash", past_exp_time, "field1", lt=True) == [2] - assert await r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpireat_nonexistent_key_or_field(r): - await r.delete("test:hash") - future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert await r.hexpireat("test:hash", future_exp_time, "field1") == [] - await r.hset("test:hash", "field1", "value1") - assert await r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpireat_multiple_fields(r): - await r.delete("test:hash") - await r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert await r.hexpireat("test:hash", exp_time, "field1", "field2") == [1, 1] - await asyncio.sleep(1.1) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is False - assert await r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpireat_basic(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = int((datetime.now() + timedelta(milliseconds=400)).timestamp() * 1000) - assert await r.hpexpireat("test:hash", exp_time, "field1") == [1] - await asyncio.sleep(0.5) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpireat_with_datetime(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = datetime.now() + timedelta(milliseconds=400) - assert await r.hpexpireat("test:hash", exp_time, "field1") == [1] - await asyncio.sleep(0.5) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpireat_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1"}) - future_exp_time = int( - (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 - ) - past_exp_time = int( - (datetime.now() - timedelta(milliseconds=500)).timestamp() * 1000 - ) - assert await r.hpexpireat("test:hash", future_exp_time, "field1", xx=True) == [0] - assert await r.hpexpireat("test:hash", future_exp_time, "field1", nx=True) == [1] - assert await r.hpexpireat("test:hash", past_exp_time, "field1", gt=True) == [0] - assert await r.hpexpireat("test:hash", past_exp_time, "field1", lt=True) == [2] - assert await r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpireat_nonexistent_key_or_field(r): - await r.delete("test:hash") - future_exp_time = int( - (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 - ) - assert await r.hpexpireat("test:hash", future_exp_time, "field1") == [] - await r.hset("test:hash", "field1", "value1") - assert await r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpireat_multiple_fields(r): - await r.delete("test:hash") - await r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - exp_time = int((datetime.now() + timedelta(milliseconds=400)).timestamp() * 1000) - assert await r.hpexpireat("test:hash", exp_time, "field1", "field2") == [1, 1] - await asyncio.sleep(0.5) - assert await r.hexists("test:hash", "field1") is False - assert await r.hexists("test:hash", "field2") is False - assert await r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -async def test_hpersist_multiple_fields_mixed_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - await r.hexpire("test:hash", 5000, "field1") - assert await r.hpersist("test:hash", "field1", "field2", "field3") == [1, -1, -2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hexpiretime_multiple_fields_mixed_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - await r.hexpireat("test:hash", future_time, "field1") - result = await r.hexpiretime("test:hash", "field1", "field2", "field3") - assert future_time - 10 < result[0] <= future_time - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -async def test_hpexpiretime_multiple_fields_mixed_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - await r.hexpireat("test:hash", future_time, "field1") - result = await r.hpexpiretime("test:hash", "field1", "field2", "field3") - assert future_time * 1000 - 10000 < result[0] <= future_time * 1000 - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -async def test_ttl_multiple_fields_mixed_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - await r.hexpireat("test:hash", future_time, "field1") - result = await r.httl("test:hash", "field1", "field2", "field3") - assert 30 * 60 - 10 < result[0] <= 30 * 60 - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -async def test_pttl_multiple_fields_mixed_conditions(r): - await r.delete("test:hash") - await r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - await r.hexpireat("test:hash", future_time, "field1") - result = await r.hpttl("test:hash", "field1", "field2", "field3") - assert 30 * 60000 - 10000 < result[0] <= 30 * 60000 - assert result[1:] == [-1, -2] diff --git a/tests/test_hash.py b/tests/test_hash.py deleted file mode 100644 index 9519e0a6..00000000 --- a/tests/test_hash.py +++ /dev/null @@ -1,371 +0,0 @@ -import time -from datetime import datetime, timedelta - -import pytest -from tests.conftest import skip_if_server_version_lt - -pytestmark = pytest.mark.skip - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_basic(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert r.hexpire("test:hash", 1, "field1") == [1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_with_timedelta(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert r.hexpire("test:hash", timedelta(seconds=1), "field1") == [1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - assert r.hexpire("test:hash", 2, "field1", xx=True) == [0] - assert r.hexpire("test:hash", 2, "field1", nx=True) == [1] - assert r.hexpire("test:hash", 1, "field1", xx=True) == [1] - assert r.hexpire("test:hash", 2, "field1", nx=True) == [0] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - r.hset("test:hash", "field1", "value1") - r.hexpire("test:hash", 2, "field1") - assert r.hexpire("test:hash", 1, "field1", gt=True) == [0] - assert r.hexpire("test:hash", 1, "field1", lt=True) == [1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_nonexistent_key_or_field(r): - r.delete("test:hash") - assert r.hexpire("test:hash", 1, "field1") == [] - r.hset("test:hash", "field1", "value1") - assert r.hexpire("test:hash", 1, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_multiple_fields(r): - r.delete("test:hash") - r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - assert r.hexpire("test:hash", 1, "field1", "field2") == [1, 1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is False - assert r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpire_multiple_condition_flags_error(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - with pytest.raises(ValueError) as e: - r.hexpire("test:hash", 1, "field1", nx=True, xx=True) - assert "Only one of" in str(e) - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_basic(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert r.hpexpire("test:hash", 500, "field1") == [1] - time.sleep(0.6) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_with_timedelta(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - assert r.hpexpire("test:hash", timedelta(milliseconds=500), "field1") == [1] - time.sleep(0.6) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - assert r.hpexpire("test:hash", 1500, "field1", xx=True) == [0] - assert r.hpexpire("test:hash", 1500, "field1", nx=True) == [1] - assert r.hpexpire("test:hash", 500, "field1", xx=True) == [1] - assert r.hpexpire("test:hash", 1500, "field1", nx=True) == [0] - time.sleep(0.6) - assert r.hexists("test:hash", "field1") is False - r.hset("test:hash", "field1", "value1") - r.hpexpire("test:hash", 1000, "field1") - assert r.hpexpire("test:hash", 500, "field1", gt=True) == [0] - assert r.hpexpire("test:hash", 500, "field1", lt=True) == [1] - time.sleep(0.6) - assert r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_nonexistent_key_or_field(r): - r.delete("test:hash") - assert r.hpexpire("test:hash", 500, "field1") == [] - r.hset("test:hash", "field1", "value1") - assert r.hpexpire("test:hash", 500, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_multiple_fields(r): - r.delete("test:hash") - r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - assert r.hpexpire("test:hash", 500, "field1", "field2") == [1, 1] - time.sleep(0.6) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is False - assert r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpire_multiple_condition_flags_error(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - with pytest.raises(ValueError) as e: - r.hpexpire("test:hash", 500, "field1", nx=True, xx=True) - assert "Only one of" in str(e) - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_basic(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert r.hexpireat("test:hash", exp_time, "field1") == [1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_with_datetime(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = datetime.now() + timedelta(seconds=1) - assert r.hexpireat("test:hash", exp_time, "field1") == [1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - future_exp_time = int((datetime.now() + timedelta(seconds=2)).timestamp()) - past_exp_time = int((datetime.now() - timedelta(seconds=1)).timestamp()) - assert r.hexpireat("test:hash", future_exp_time, "field1", xx=True) == [0] - assert r.hexpireat("test:hash", future_exp_time, "field1", nx=True) == [1] - assert r.hexpireat("test:hash", past_exp_time, "field1", gt=True) == [0] - assert r.hexpireat("test:hash", past_exp_time, "field1", lt=True) == [2] - assert r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_nonexistent_key_or_field(r): - r.delete("test:hash") - future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert r.hexpireat("test:hash", future_exp_time, "field1") == [] - r.hset("test:hash", "field1", "value1") - assert r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_multiple_fields(r): - r.delete("test:hash") - r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - assert r.hexpireat("test:hash", exp_time, "field1", "field2") == [1, 1] - time.sleep(1.1) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is False - assert r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hexpireat_multiple_condition_flags_error(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp()) - with pytest.raises(ValueError) as e: - r.hexpireat("test:hash", exp_time, "field1", nx=True, xx=True) - assert "Only one of" in str(e) - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_basic(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = int((datetime.now() + timedelta(milliseconds=400)).timestamp() * 1000) - assert r.hpexpireat("test:hash", exp_time, "field1") == [1] - time.sleep(0.5) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_with_datetime(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - exp_time = datetime.now() + timedelta(milliseconds=400) - assert r.hpexpireat("test:hash", exp_time, "field1") == [1] - time.sleep(0.5) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - future_exp_time = int( - (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 - ) - past_exp_time = int( - (datetime.now() - timedelta(milliseconds=500)).timestamp() * 1000 - ) - assert r.hpexpireat("test:hash", future_exp_time, "field1", xx=True) == [0] - assert r.hpexpireat("test:hash", future_exp_time, "field1", nx=True) == [1] - assert r.hpexpireat("test:hash", past_exp_time, "field1", gt=True) == [0] - assert r.hpexpireat("test:hash", past_exp_time, "field1", lt=True) == [2] - assert r.hexists("test:hash", "field1") is False - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_nonexistent_key_or_field(r): - r.delete("test:hash") - future_exp_time = int( - (datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000 - ) - assert r.hpexpireat("test:hash", future_exp_time, "field1") == [] - r.hset("test:hash", "field1", "value1") - assert r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2] - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_multiple_fields(r): - r.delete("test:hash") - r.hset( - "test:hash", - mapping={"field1": "value1", "field2": "value2", "field3": "value3"}, - ) - exp_time = int((datetime.now() + timedelta(milliseconds=400)).timestamp() * 1000) - assert r.hpexpireat("test:hash", exp_time, "field1", "field2") == [1, 1] - time.sleep(0.5) - assert r.hexists("test:hash", "field1") is False - assert r.hexists("test:hash", "field2") is False - assert r.hexists("test:hash", "field3") is True - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpireat_multiple_condition_flags_error(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1"}) - exp_time = int((datetime.now() + timedelta(milliseconds=500)).timestamp()) - with pytest.raises(ValueError) as e: - r.hpexpireat("test:hash", exp_time, "field1", nx=True, xx=True) - assert "Only one of" in str(e) - - -@skip_if_server_version_lt("7.3.240") -def test_hpersist_multiple_fields(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - r.hexpire("test:hash", 5000, "field1") - assert r.hpersist("test:hash", "field1", "field2", "field3") == [1, -1, -2] - - -@skip_if_server_version_lt("7.3.240") -def test_hpersist_nonexistent_key(r): - r.delete("test:hash") - assert r.hpersist("test:hash", "field1", "field2", "field3") == [] - - -@skip_if_server_version_lt("7.3.240") -def test_hexpiretime_multiple_fields_mixed_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - r.hexpireat("test:hash", future_time, "field1") - result = r.hexpiretime("test:hash", "field1", "field2", "field3") - assert future_time - 10 < result[0] <= future_time - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -def test_hexpiretime_nonexistent_key(r): - r.delete("test:hash") - assert r.hexpiretime("test:hash", "field1", "field2", "field3") == [] - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpiretime_multiple_fields_mixed_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - r.hexpireat("test:hash", future_time, "field1") - result = r.hpexpiretime("test:hash", "field1", "field2", "field3") - assert future_time * 1000 - 10000 < result[0] <= future_time * 1000 - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -def test_hpexpiretime_nonexistent_key(r): - r.delete("test:hash") - assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == [] - - -@skip_if_server_version_lt("7.3.240") -def test_httl_multiple_fields_mixed_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - r.hexpireat("test:hash", future_time, "field1") - result = r.httl("test:hash", "field1", "field2", "field3") - assert 30 * 60 - 10 < result[0] <= 30 * 60 - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -def test_httl_nonexistent_key(r): - r.delete("test:hash") - assert r.httl("test:hash", "field1", "field2", "field3") == [] - - -@skip_if_server_version_lt("7.3.240") -def test_hpttl_multiple_fields_mixed_conditions(r): - r.delete("test:hash") - r.hset("test:hash", mapping={"field1": "value1", "field2": "value2"}) - future_time = int((datetime.now() + timedelta(minutes=30)).timestamp()) - r.hexpireat("test:hash", future_time, "field1") - result = r.hpttl("test:hash", "field1", "field2", "field3") - assert 30 * 60000 - 10000 < result[0] <= 30 * 60000 - assert result[1:] == [-1, -2] - - -@skip_if_server_version_lt("7.3.240") -def test_hpttl_nonexistent_key(r): - r.delete("test:hash") - assert r.hpttl("test:hash", "field1", "field2", "field3") == [] diff --git a/valkey/commands/core.py b/valkey/commands/core.py index 75f45eec..90dfb3b8 100644 --- a/valkey/commands/core.py +++ b/valkey/commands/core.py @@ -5092,410 +5092,6 @@ def hstrlen(self, name: str, key: str) -> Union[Awaitable[int], int]: """ return self.execute_command("HSTRLEN", name, key, keys=[name]) - def hexpire( - self, - name: KeyT, - seconds: ExpiryT, - *fields: str, - nx: bool = False, - xx: bool = False, - gt: bool = False, - lt: bool = False, - ) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Sets or updates the expiration time for fields within a hash key, using relative - time in seconds. - - If a field already has an expiration time, the behavior of the update can be - controlled using the `nx`, `xx`, `gt`, and `lt` parameters. - - The return value provides detailed information about the outcome for each field. - - For more information, see https://redis.io/commands/hexpire - - Args: - name: The name of the hash key. - seconds: Expiration time in seconds, relative. Can be an integer, or a - Python `timedelta` object. - fields: List of fields within the hash to apply the expiration time to. - nx: Set expiry only when the field has no expiry. - xx: Set expiry only when the field has an existing expiry. - gt: Set expiry only when the new expiry is greater than the current one. - lt: Set expiry only when the new expiry is less than the current one. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `0` if the specified NX | XX | GT | LT condition was not met. - - `1` if the expiration time was set or updated. - - `2` if the field was deleted because the specified expiration time is - in the past. - """ - conditions = [nx, xx, gt, lt] - if sum(conditions) > 1: - raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.") - - if isinstance(seconds, datetime.timedelta): - seconds = int(seconds.total_seconds()) - - options = [] - if nx: - options.append("NX") - if xx: - options.append("XX") - if gt: - options.append("GT") - if lt: - options.append("LT") - - return self.execute_command( - "HEXPIRE", name, seconds, *options, "FIELDS", len(fields), *fields - ) - - def hpexpire( - self, - name: KeyT, - milliseconds: ExpiryT, - *fields: str, - nx: bool = False, - xx: bool = False, - gt: bool = False, - lt: bool = False, - ) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Sets or updates the expiration time for fields within a hash key, using relative - time in milliseconds. - - If a field already has an expiration time, the behavior of the update can be - controlled using the `nx`, `xx`, `gt`, and `lt` parameters. - - The return value provides detailed information about the outcome for each field. - - For more information, see https://redis.io/commands/hpexpire - - Args: - name: The name of the hash key. - milliseconds: Expiration time in milliseconds, relative. Can be an integer, - or a Python `timedelta` object. - fields: List of fields within the hash to apply the expiration time to. - nx: Set expiry only when the field has no expiry. - xx: Set expiry only when the field has an existing expiry. - gt: Set expiry only when the new expiry is greater than the current one. - lt: Set expiry only when the new expiry is less than the current one. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `0` if the specified NX | XX | GT | LT condition was not met. - - `1` if the expiration time was set or updated. - - `2` if the field was deleted because the specified expiration time is - in the past. - """ - conditions = [nx, xx, gt, lt] - if sum(conditions) > 1: - raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.") - - if isinstance(milliseconds, datetime.timedelta): - milliseconds = int(milliseconds.total_seconds() * 1000) - - options = [] - if nx: - options.append("NX") - if xx: - options.append("XX") - if gt: - options.append("GT") - if lt: - options.append("LT") - - return self.execute_command( - "HPEXPIRE", name, milliseconds, *options, "FIELDS", len(fields), *fields - ) - - def hexpireat( - self, - name: KeyT, - unix_time_seconds: AbsExpiryT, - *fields: str, - nx: bool = False, - xx: bool = False, - gt: bool = False, - lt: bool = False, - ) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Sets or updates the expiration time for fields within a hash key, using an - absolute Unix timestamp in seconds. - - If a field already has an expiration time, the behavior of the update can be - controlled using the `nx`, `xx`, `gt`, and `lt` parameters. - - The return value provides detailed information about the outcome for each field. - - For more information, see https://redis.io/commands/hexpireat - - Args: - name: The name of the hash key. - unix_time_seconds: Expiration time as Unix timestamp in seconds. Can be an - integer or a Python `datetime` object. - fields: List of fields within the hash to apply the expiration time to. - nx: Set expiry only when the field has no expiry. - xx: Set expiry only when the field has an existing expiration time. - gt: Set expiry only when the new expiry is greater than the current one. - lt: Set expiry only when the new expiry is less than the current one. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `0` if the specified NX | XX | GT | LT condition was not met. - - `1` if the expiration time was set or updated. - - `2` if the field was deleted because the specified expiration time is - in the past. - """ - conditions = [nx, xx, gt, lt] - if sum(conditions) > 1: - raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.") - - if isinstance(unix_time_seconds, datetime.datetime): - unix_time_seconds = int(unix_time_seconds.timestamp()) - - options = [] - if nx: - options.append("NX") - if xx: - options.append("XX") - if gt: - options.append("GT") - if lt: - options.append("LT") - - return self.execute_command( - "HEXPIREAT", - name, - unix_time_seconds, - *options, - "FIELDS", - len(fields), - *fields, - ) - - def hpexpireat( - self, - name: KeyT, - unix_time_milliseconds: AbsExpiryT, - *fields: str, - nx: bool = False, - xx: bool = False, - gt: bool = False, - lt: bool = False, - ) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Sets or updates the expiration time for fields within a hash key, using an - absolute Unix timestamp in milliseconds. - - If a field already has an expiration time, the behavior of the update can be - controlled using the `nx`, `xx`, `gt`, and `lt` parameters. - - The return value provides detailed information about the outcome for each field. - - For more information, see https://redis.io/commands/hpexpireat - - Args: - name: The name of the hash key. - unix_time_milliseconds: Expiration time as Unix timestamp in milliseconds. - Can be an integer or a Python `datetime` object. - fields: List of fields within the hash to apply the expiry. - nx: Set expiry only when the field has no expiry. - xx: Set expiry only when the field has an existing expiry. - gt: Set expiry only when the new expiry is greater than the current one. - lt: Set expiry only when the new expiry is less than the current one. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `0` if the specified NX | XX | GT | LT condition was not met. - - `1` if the expiration time was set or updated. - - `2` if the field was deleted because the specified expiration time is - in the past. - """ - conditions = [nx, xx, gt, lt] - if sum(conditions) > 1: - raise ValueError("Only one of 'nx', 'xx', 'gt', 'lt' can be specified.") - - if isinstance(unix_time_milliseconds, datetime.datetime): - unix_time_milliseconds = int(unix_time_milliseconds.timestamp() * 1000) - - options = [] - if nx: - options.append("NX") - if xx: - options.append("XX") - if gt: - options.append("GT") - if lt: - options.append("LT") - - return self.execute_command( - "HPEXPIREAT", - name, - unix_time_milliseconds, - *options, - "FIELDS", - len(fields), - *fields, - ) - - def hpersist(self, name: KeyT, *fields: str) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Removes the expiration time for each specified field in a hash. - - For more information, see https://redis.io/commands/hpersist - - Args: - name: The name of the hash key. - fields: A list of fields within the hash from which to remove the - expiration time. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `-1` if the field exists but has no associated expiration time. - - `1` if the expiration time was successfully removed from the field. - """ - return self.execute_command("HPERSIST", name, "FIELDS", len(fields), *fields) - - def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Returns the expiration times of hash fields as Unix timestamps in seconds. - - For more information, see https://redis.io/commands/hexpiretime - - Args: - key: The hash key. - fields: A list of fields within the hash for which to get the expiration - time. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `-1` if the field exists but has no associated expire time. - - A positive integer representing the expiration Unix timestamp in - seconds, if the field has an associated expiration time. - """ - return self.execute_command( - "HEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key] - ) - - def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Returns the expiration times of hash fields as Unix timestamps in milliseconds. - - For more information, see https://redis.io/commands/hpexpiretime - - Args: - key: The hash key. - fields: A list of fields within the hash for which to get the expiration - time. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `-1` if the field exists but has no associated expire time. - - A positive integer representing the expiration Unix timestamp in - milliseconds, if the field has an associated expiration time. - """ - return self.execute_command( - "HPEXPIRETIME", key, "FIELDS", len(fields), *fields, keys=[key] - ) - - def httl(self, key: KeyT, *fields: str) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Returns the TTL (Time To Live) in seconds for each specified field within a hash - key. - - For more information, see https://redis.io/commands/httl - - Args: - key: The hash key. - fields: A list of fields within the hash for which to get the TTL. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `-1` if the field exists but has no associated expire time. - - A positive integer representing the TTL in seconds if the field has - an associated expiration time. - """ - return self.execute_command( - "HTTL", key, "FIELDS", len(fields), *fields, keys=[key] - ) - - def hpttl(self, key: KeyT, *fields: str) -> ResponseT: - """ - :meta private: - - Command not available in Valkey - - Returns the TTL (Time To Live) in milliseconds for each specified field within a - hash key. - - For more information, see https://redis.io/commands/hpttl - - Args: - key: The hash key. - fields: A list of fields within the hash for which to get the TTL. - - Returns: - If the key does not exist, returns an empty list. If the key exists, returns - a list which contains for each field in the request: - - `-2` if the field does not exist. - - `-1` if the field exists but has no associated expire time. - - A positive integer representing the TTL in milliseconds if the field - has an associated expiration time. - """ - return self.execute_command( - "HPTTL", key, "FIELDS", len(fields), *fields, keys=[key] - ) - AsyncHashCommands = HashCommands