Skip to content

Commit

Permalink
Merge pull request #20 from valkey-io/aiven-sal/changes_for_b6
Browse files Browse the repository at this point in the history
Version 5.1.0b6
  • Loading branch information
aiven-sal authored Jun 14, 2024
2 parents 16192e5 + 9e29e5e commit a1053d7
Show file tree
Hide file tree
Showing 27 changed files with 1,142 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* Update `ResponseT` type hint
* Allow to control the minimum SSL version
* Add an optional lock_name attribute to LockError.
* Fix return types for `get`, `set_path` and `strappend` in JSONCommands
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
long_description_content_type="text/markdown",
keywords=["Valkey", "key-value store", "database"],
license="MIT",
version="5.1.0b5",
version="5.1.0b6",
packages=find_packages(
include=[
"valkey",
Expand All @@ -33,7 +33,7 @@
"Issue tracker": "https://github.com/valkey-io/valkey-py/issues",
},
author="valkey-py authors",
author_email="placeholder@valkey.io",
author_email="valkey-py@lists.valkey.io",
python_requires=">=3.8",
install_requires=[
'async-timeout>=4.0.3; python_full_version<"3.11.3"',
Expand Down
2 changes: 1 addition & 1 deletion tests/ssl_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ def get_ssl_filename(name):
os.path.join(root, "..", "dockers", "stunnel", "keys")
)
if not os.path.isdir(cert_dir):
raise IOError(f"No SSL certificates found. They should be in {cert_dir}")
raise OSError(f"No SSL certificates found. They should be in {cert_dir}")

return os.path.join(cert_dir, name)
2 changes: 1 addition & 1 deletion tests/test_asyncio/compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
try:
mock.AsyncMock
except AttributeError:
import mock
from unittest import mock

try:
from contextlib import aclosing
Expand Down
2 changes: 1 addition & 1 deletion tests/test_asyncio/test_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -1430,7 +1430,7 @@ async def test_memory_stats(self, r: ValkeyCluster) -> None:
assert isinstance(stats, dict)
for key, value in stats.items():
if key.startswith("db."):
assert isinstance(value, dict)
assert not isinstance(value, list)

@skip_if_server_version_lt("4.0.0")
async def test_memory_help(self, r: ValkeyCluster) -> None:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_asyncio/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ async def test_hscan(self, r: valkey.Valkey):
_, dic = await r.hscan("a_notset", match="a")
assert dic == {}

@skip_if_server_version_lt("7.4.0")
@skip_if_server_version_lt("7.3.240")
async def test_hscan_novalues(self, r: valkey.Valkey):
await r.hset("a", mapping={"a": 1, "b": 2, "c": 3})
cursor, keys = await r.hscan("a", no_values=True)
Expand All @@ -1373,7 +1373,7 @@ async def test_hscan_iter(self, r: valkey.Valkey):
dic = {k: v async for k, v in r.hscan_iter("a_notset", match="a")}
assert dic == {}

@skip_if_server_version_lt("7.4.0")
@skip_if_server_version_lt("7.3.240")
async def test_hscan_iter_novalues(self, r: valkey.Valkey):
await r.hset("a", mapping={"a": 1, "b": 2, "c": 3})
keys = list([k async for k in r.hscan_iter("a", no_values=True)])
Expand Down Expand Up @@ -3235,7 +3235,7 @@ async def test_memory_stats(self, r: valkey.Valkey):
assert isinstance(stats, dict)
for key, value in stats.items():
if key.startswith("db."):
assert isinstance(value, dict)
assert not isinstance(value, list)

@skip_if_server_version_lt("4.0.0")
async def test_memory_usage(self, r: valkey.Valkey):
Expand Down
300 changes: 300 additions & 0 deletions tests/test_asyncio/test_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
import asyncio
from datetime import datetime, timedelta

from tests.conftest import skip_if_server_version_lt


@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]
Loading

0 comments on commit a1053d7

Please sign in to comment.