Skip to content

Commit

Permalink
Prepare for Redis 7.4 RC2 (redis#3303)
Browse files Browse the repository at this point in the history
Adapt the code and some of the tests to match the changes done in the
Redis 7.4 RC2 release.
  • Loading branch information
gerzse authored Jul 4, 2024
1 parent 57af285 commit 04962e0
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 60 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/integration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ permissions:

env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
REDIS_IMAGE: redis/redis-stack-server:7.4.0-rc1
REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc1
REDIS_IMAGE: redis:7.4-rc2
REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc2

jobs:
dependency-audit:
Expand Down
45 changes: 18 additions & 27 deletions redis/commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5126,9 +5126,8 @@ def hexpire(
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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
Expand Down Expand Up @@ -5187,9 +5186,8 @@ def hpexpire(
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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
Expand Down Expand Up @@ -5248,9 +5246,8 @@ def hexpireat(
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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
Expand Down Expand Up @@ -5315,9 +5312,8 @@ def hpexpireat(
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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
Expand Down Expand Up @@ -5362,9 +5358,8 @@ def hpersist(self, name: KeyT, *fields: str) -> ResponseT:
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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.
"""
Expand All @@ -5382,9 +5377,8 @@ def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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.
Expand All @@ -5405,9 +5399,8 @@ def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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.
Expand All @@ -5428,9 +5421,8 @@ def httl(self, key: KeyT, *fields: str) -> ResponseT:
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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.
Expand All @@ -5451,9 +5443,8 @@ def hpttl(self, key: KeyT, *fields: str) -> ResponseT:
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.
Returns a list which contains for each field in the request:
- `-2` if the field does not exist, or if the key 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.
Expand Down
8 changes: 4 additions & 4 deletions tests/test_asyncio/test_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async def test_hexpire_conditions(r):
@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") == []
assert await r.hexpire("test:hash", 1, "field1") == [-2]
await r.hset("test:hash", "field1", "value1")
assert await r.hexpire("test:hash", 1, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -105,7 +105,7 @@ async def test_hpexpire_conditions(r):
@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") == []
assert await r.hpexpire("test:hash", 500, "field1") == [-2]
await r.hset("test:hash", "field1", "value1")
assert await r.hpexpire("test:hash", 500, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -163,7 +163,7 @@ async def test_hexpireat_conditions(r):
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") == []
assert await r.hexpireat("test:hash", future_exp_time, "field1") == [-2]
await r.hset("test:hash", "field1", "value1")
assert await r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -228,7 +228,7 @@ async def test_hpexpireat_nonexistent_key_or_field(r):
future_exp_time = int(
(datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000
)
assert await r.hpexpireat("test:hash", future_exp_time, "field1") == []
assert await r.hpexpireat("test:hash", future_exp_time, "field1") == [-2]
await r.hset("test:hash", "field1", "value1")
assert await r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]

Expand Down
2 changes: 1 addition & 1 deletion tests/test_asyncio/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ async def test_mset(decoded_r: redis.Redis):
async def test_clear(decoded_r: redis.Redis):
await decoded_r.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4])
assert 1 == await decoded_r.json().clear("arr", Path.root_path())
assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], [[[]]])
assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], [])


@pytest.mark.redismod
Expand Down
18 changes: 9 additions & 9 deletions tests/test_hash.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_hexpire_conditions(r):
@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") == []
assert r.hexpire("test:hash", 1, "field1") == [-2]
r.hset("test:hash", "field1", "value1")
assert r.hexpire("test:hash", 1, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -115,7 +115,7 @@ def test_hpexpire_conditions(r):
@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") == []
assert r.hpexpire("test:hash", 500, "field1") == [-2]
r.hset("test:hash", "field1", "value1")
assert r.hpexpire("test:hash", 500, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -182,7 +182,7 @@ def test_hexpireat_conditions(r):
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") == []
assert r.hexpireat("test:hash", future_exp_time, "field1") == [-2]
r.hset("test:hash", "field1", "value1")
assert r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -257,7 +257,7 @@ def test_hpexpireat_nonexistent_key_or_field(r):
future_exp_time = int(
(datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000
)
assert r.hpexpireat("test:hash", future_exp_time, "field1") == []
assert r.hpexpireat("test:hash", future_exp_time, "field1") == [-2]
r.hset("test:hash", "field1", "value1")
assert r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]

Expand Down Expand Up @@ -298,7 +298,7 @@ def test_hpersist_multiple_fields(r):
@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") == []
assert r.hpersist("test:hash", "field1", "field2", "field3") == [-2, -2, -2]


@skip_if_server_version_lt("7.3.240")
Expand All @@ -315,7 +315,7 @@ def test_hexpiretime_multiple_fields_mixed_conditions(r):
@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") == []
assert r.hexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2]


@skip_if_server_version_lt("7.3.240")
Expand All @@ -332,7 +332,7 @@ def test_hpexpiretime_multiple_fields_mixed_conditions(r):
@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") == []
assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2]


@skip_if_server_version_lt("7.3.240")
Expand All @@ -349,7 +349,7 @@ def test_httl_multiple_fields_mixed_conditions(r):
@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") == []
assert r.httl("test:hash", "field1", "field2", "field3") == [-2, -2, -2]


@skip_if_server_version_lt("7.3.240")
Expand All @@ -366,4 +366,4 @@ def test_hpttl_multiple_fields_mixed_conditions(r):
@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") == []
assert r.hpttl("test:hash", "field1", "field2", "field3") == [-2, -2, -2]
2 changes: 1 addition & 1 deletion tests/test_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def test_mset(client):
def test_clear(client):
client.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4])
assert 1 == client.json().clear("arr", Path.root_path())
assert_resp_response(client, client.json().get("arr"), [], [[[]]])
assert_resp_response(client, client.json().get("arr"), [], [])


@pytest.mark.redismod
Expand Down
33 changes: 17 additions & 16 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2369,27 +2369,27 @@ def test_search_missing_fields(client):

with pytest.raises(redis.exceptions.ResponseError) as e:
client.ft().search(
Query("ismissing(@title)").dialect(5).return_field("id").no_content()
Query("ismissing(@title)").dialect(2).return_field("id").no_content()
)
assert "to be defined with 'INDEXMISSING'" in e.value.args[0]

res = client.ft().search(
Query("ismissing(@features)").dialect(5).return_field("id").no_content()
Query("ismissing(@features)").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:2"])

res = client.ft().search(
Query("-ismissing(@features)").dialect(5).return_field("id").no_content()
Query("-ismissing(@features)").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:1", "property:3"])

res = client.ft().search(
Query("ismissing(@description)").dialect(5).return_field("id").no_content()
Query("ismissing(@description)").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:3"])

res = client.ft().search(
Query("-ismissing(@description)").dialect(5).return_field("id").no_content()
Query("-ismissing(@description)").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:1", "property:2"])

Expand Down Expand Up @@ -2438,27 +2438,29 @@ def test_search_empty_fields(client):

with pytest.raises(redis.exceptions.ResponseError) as e:
client.ft().search(
Query("@title:''").dialect(5).return_field("id").no_content()
Query("@title:''").dialect(2).return_field("id").no_content()
)
assert "to be defined with `INDEXEMPTY`" in e.value.args[0]
assert "Use `INDEXEMPTY` in field creation" in e.value.args[0]

res = client.ft().search(
Query("@features:{ }").dialect(5).return_field("id").no_content()
Query("@features:{$empty}").dialect(2).return_field("id").no_content(),
query_params={"empty": ""},
)
_assert_search_result(client, res, ["property:2"])

res = client.ft().search(
Query("-@features:{ }").dialect(5).return_field("id").no_content()
Query("-@features:{$empty}").dialect(2).return_field("id").no_content(),
query_params={"empty": ""},
)
_assert_search_result(client, res, ["property:1", "property:3"])

res = client.ft().search(
Query("@description:''").dialect(5).return_field("id").no_content()
Query("@description:''").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:3"])

res = client.ft().search(
Query("-@description:''").dialect(5).return_field("id").no_content()
Query("-@description:''").dialect(2).return_field("id").no_content()
)
_assert_search_result(client, res, ["property:1", "property:2"])

Expand Down Expand Up @@ -2503,22 +2505,21 @@ def test_special_characters_in_fields(client):
)
_assert_search_result(client, res, ["resource:1"])

# with dialect 5 no need to escape the - even without params
# with double quotes exact match no need to escape the - even without params
res = client.ft().search(
Query("@uuid:{123e4567-e89b-12d3-a456-426614174000}").dialect(5)
Query('@uuid:{"123e4567-e89b-12d3-a456-426614174000"}').dialect(2)
)
_assert_search_result(client, res, ["resource:1"])

# also no need to escape ' with dialect 5
res = client.ft().search(Query("@tags:{new-year's-resolutions}").dialect(5))
res = client.ft().search(Query('@tags:{"new-year\'s-resolutions"}').dialect(2))
_assert_search_result(client, res, ["resource:2"])

# possible to search numeric fields by single value
res = client.ft().search(Query("@rating:[4]").dialect(2))
_assert_search_result(client, res, ["resource:2"])

# some chars still need escaping
res = client.ft().search(Query(r"@tags:{\$btc}").dialect(5))
res = client.ft().search(Query(r"@tags:{\$btc}").dialect(2))
_assert_search_result(client, res, ["resource:1"])


Expand Down

0 comments on commit 04962e0

Please sign in to comment.