Skip to content

Commit 6fedfef

Browse files
committed
Prepare for Redis 7.4 RC2 (#3303)
Adapt the code and some of the tests to match the changes done in the Redis 7.4 RC2 release.
1 parent ed1680f commit 6fedfef

File tree

7 files changed

+52
-60
lines changed

7 files changed

+52
-60
lines changed

.github/workflows/integration.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ permissions:
2525

2626
env:
2727
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
28-
REDIS_IMAGE: redis/redis-stack-server:7.4.0-rc1
29-
REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc1
28+
REDIS_IMAGE: redis:7.4-rc2
29+
REDIS_STACK_IMAGE: redis/redis-stack-server:7.4.0-rc2
3030

3131
jobs:
3232
dependency-audit:

redis/commands/core.py

+18-27
Original file line numberDiff line numberDiff line change
@@ -5119,9 +5119,8 @@ def hexpire(
51195119
lt: Set expiry only when the new expiry is less than the current one.
51205120
51215121
Returns:
5122-
If the key does not exist, returns an empty list. If the key exists, returns
5123-
a list which contains for each field in the request:
5124-
- `-2` if the field does not exist.
5122+
Returns a list which contains for each field in the request:
5123+
- `-2` if the field does not exist, or if the key does not exist.
51255124
- `0` if the specified NX | XX | GT | LT condition was not met.
51265125
- `1` if the expiration time was set or updated.
51275126
- `2` if the field was deleted because the specified expiration time is
@@ -5180,9 +5179,8 @@ def hpexpire(
51805179
lt: Set expiry only when the new expiry is less than the current one.
51815180
51825181
Returns:
5183-
If the key does not exist, returns an empty list. If the key exists, returns
5184-
a list which contains for each field in the request:
5185-
- `-2` if the field does not exist.
5182+
Returns a list which contains for each field in the request:
5183+
- `-2` if the field does not exist, or if the key does not exist.
51865184
- `0` if the specified NX | XX | GT | LT condition was not met.
51875185
- `1` if the expiration time was set or updated.
51885186
- `2` if the field was deleted because the specified expiration time is
@@ -5241,9 +5239,8 @@ def hexpireat(
52415239
lt: Set expiry only when the new expiry is less than the current one.
52425240
52435241
Returns:
5244-
If the key does not exist, returns an empty list. If the key exists, returns
5245-
a list which contains for each field in the request:
5246-
- `-2` if the field does not exist.
5242+
Returns a list which contains for each field in the request:
5243+
- `-2` if the field does not exist, or if the key does not exist.
52475244
- `0` if the specified NX | XX | GT | LT condition was not met.
52485245
- `1` if the expiration time was set or updated.
52495246
- `2` if the field was deleted because the specified expiration time is
@@ -5308,9 +5305,8 @@ def hpexpireat(
53085305
lt: Set expiry only when the new expiry is less than the current one.
53095306
53105307
Returns:
5311-
If the key does not exist, returns an empty list. If the key exists, returns
5312-
a list which contains for each field in the request:
5313-
- `-2` if the field does not exist.
5308+
Returns a list which contains for each field in the request:
5309+
- `-2` if the field does not exist, or if the key does not exist.
53145310
- `0` if the specified NX | XX | GT | LT condition was not met.
53155311
- `1` if the expiration time was set or updated.
53165312
- `2` if the field was deleted because the specified expiration time is
@@ -5355,9 +5351,8 @@ def hpersist(self, name: KeyT, *fields: str) -> ResponseT:
53555351
expiration time.
53565352
53575353
Returns:
5358-
If the key does not exist, returns an empty list. If the key exists, returns
5359-
a list which contains for each field in the request:
5360-
- `-2` if the field does not exist.
5354+
Returns a list which contains for each field in the request:
5355+
- `-2` if the field does not exist, or if the key does not exist.
53615356
- `-1` if the field exists but has no associated expiration time.
53625357
- `1` if the expiration time was successfully removed from the field.
53635358
"""
@@ -5375,9 +5370,8 @@ def hexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
53755370
time.
53765371
53775372
Returns:
5378-
If the key does not exist, returns an empty list. If the key exists, returns
5379-
a list which contains for each field in the request:
5380-
- `-2` if the field does not exist.
5373+
Returns a list which contains for each field in the request:
5374+
- `-2` if the field does not exist, or if the key does not exist.
53815375
- `-1` if the field exists but has no associated expire time.
53825376
- A positive integer representing the expiration Unix timestamp in
53835377
seconds, if the field has an associated expiration time.
@@ -5396,9 +5390,8 @@ def hpexpiretime(self, key: KeyT, *fields: str) -> ResponseT:
53965390
time.
53975391
53985392
Returns:
5399-
If the key does not exist, returns an empty list. If the key exists, returns
5400-
a list which contains for each field in the request:
5401-
- `-2` if the field does not exist.
5393+
Returns a list which contains for each field in the request:
5394+
- `-2` if the field does not exist, or if the key does not exist.
54025395
- `-1` if the field exists but has no associated expire time.
54035396
- A positive integer representing the expiration Unix timestamp in
54045397
milliseconds, if the field has an associated expiration time.
@@ -5417,9 +5410,8 @@ def httl(self, key: KeyT, *fields: str) -> ResponseT:
54175410
fields: A list of fields within the hash for which to get the TTL.
54185411
54195412
Returns:
5420-
If the key does not exist, returns an empty list. If the key exists, returns
5421-
a list which contains for each field in the request:
5422-
- `-2` if the field does not exist.
5413+
Returns a list which contains for each field in the request:
5414+
- `-2` if the field does not exist, or if the key does not exist.
54235415
- `-1` if the field exists but has no associated expire time.
54245416
- A positive integer representing the TTL in seconds if the field has
54255417
an associated expiration time.
@@ -5438,9 +5430,8 @@ def hpttl(self, key: KeyT, *fields: str) -> ResponseT:
54385430
fields: A list of fields within the hash for which to get the TTL.
54395431
54405432
Returns:
5441-
If the key does not exist, returns an empty list. If the key exists, returns
5442-
a list which contains for each field in the request:
5443-
- `-2` if the field does not exist.
5433+
Returns a list which contains for each field in the request:
5434+
- `-2` if the field does not exist, or if the key does not exist.
54445435
- `-1` if the field exists but has no associated expire time.
54455436
- A positive integer representing the TTL in milliseconds if the field
54465437
has an associated expiration time.

tests/test_asyncio/test_hash.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ async def test_hexpire_conditions(r):
4545
@skip_if_server_version_lt("7.3.240")
4646
async def test_hexpire_nonexistent_key_or_field(r):
4747
await r.delete("test:hash")
48-
assert await r.hexpire("test:hash", 1, "field1") == []
48+
assert await r.hexpire("test:hash", 1, "field1") == [-2]
4949
await r.hset("test:hash", "field1", "value1")
5050
assert await r.hexpire("test:hash", 1, "nonexistent_field") == [-2]
5151

@@ -105,7 +105,7 @@ async def test_hpexpire_conditions(r):
105105
@skip_if_server_version_lt("7.3.240")
106106
async def test_hpexpire_nonexistent_key_or_field(r):
107107
await r.delete("test:hash")
108-
assert await r.hpexpire("test:hash", 500, "field1") == []
108+
assert await r.hpexpire("test:hash", 500, "field1") == [-2]
109109
await r.hset("test:hash", "field1", "value1")
110110
assert await r.hpexpire("test:hash", 500, "nonexistent_field") == [-2]
111111

@@ -163,7 +163,7 @@ async def test_hexpireat_conditions(r):
163163
async def test_hexpireat_nonexistent_key_or_field(r):
164164
await r.delete("test:hash")
165165
future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp())
166-
assert await r.hexpireat("test:hash", future_exp_time, "field1") == []
166+
assert await r.hexpireat("test:hash", future_exp_time, "field1") == [-2]
167167
await r.hset("test:hash", "field1", "value1")
168168
assert await r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]
169169

@@ -228,7 +228,7 @@ async def test_hpexpireat_nonexistent_key_or_field(r):
228228
future_exp_time = int(
229229
(datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000
230230
)
231-
assert await r.hpexpireat("test:hash", future_exp_time, "field1") == []
231+
assert await r.hpexpireat("test:hash", future_exp_time, "field1") == [-2]
232232
await r.hset("test:hash", "field1", "value1")
233233
assert await r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]
234234

tests/test_asyncio/test_json.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ async def test_mset(decoded_r: redis.Redis):
131131
async def test_clear(decoded_r: redis.Redis):
132132
await decoded_r.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4])
133133
assert 1 == await decoded_r.json().clear("arr", Path.root_path())
134-
assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], [[[]]])
134+
assert_resp_response(decoded_r, await decoded_r.json().get("arr"), [], [])
135135

136136

137137
@pytest.mark.redismod

tests/test_hash.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_hexpire_conditions(r):
4646
@skip_if_server_version_lt("7.3.240")
4747
def test_hexpire_nonexistent_key_or_field(r):
4848
r.delete("test:hash")
49-
assert r.hexpire("test:hash", 1, "field1") == []
49+
assert r.hexpire("test:hash", 1, "field1") == [-2]
5050
r.hset("test:hash", "field1", "value1")
5151
assert r.hexpire("test:hash", 1, "nonexistent_field") == [-2]
5252

@@ -115,7 +115,7 @@ def test_hpexpire_conditions(r):
115115
@skip_if_server_version_lt("7.3.240")
116116
def test_hpexpire_nonexistent_key_or_field(r):
117117
r.delete("test:hash")
118-
assert r.hpexpire("test:hash", 500, "field1") == []
118+
assert r.hpexpire("test:hash", 500, "field1") == [-2]
119119
r.hset("test:hash", "field1", "value1")
120120
assert r.hpexpire("test:hash", 500, "nonexistent_field") == [-2]
121121

@@ -182,7 +182,7 @@ def test_hexpireat_conditions(r):
182182
def test_hexpireat_nonexistent_key_or_field(r):
183183
r.delete("test:hash")
184184
future_exp_time = int((datetime.now() + timedelta(seconds=1)).timestamp())
185-
assert r.hexpireat("test:hash", future_exp_time, "field1") == []
185+
assert r.hexpireat("test:hash", future_exp_time, "field1") == [-2]
186186
r.hset("test:hash", "field1", "value1")
187187
assert r.hexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]
188188

@@ -257,7 +257,7 @@ def test_hpexpireat_nonexistent_key_or_field(r):
257257
future_exp_time = int(
258258
(datetime.now() + timedelta(milliseconds=500)).timestamp() * 1000
259259
)
260-
assert r.hpexpireat("test:hash", future_exp_time, "field1") == []
260+
assert r.hpexpireat("test:hash", future_exp_time, "field1") == [-2]
261261
r.hset("test:hash", "field1", "value1")
262262
assert r.hpexpireat("test:hash", future_exp_time, "nonexistent_field") == [-2]
263263

@@ -298,7 +298,7 @@ def test_hpersist_multiple_fields(r):
298298
@skip_if_server_version_lt("7.3.240")
299299
def test_hpersist_nonexistent_key(r):
300300
r.delete("test:hash")
301-
assert r.hpersist("test:hash", "field1", "field2", "field3") == []
301+
assert r.hpersist("test:hash", "field1", "field2", "field3") == [-2, -2, -2]
302302

303303

304304
@skip_if_server_version_lt("7.3.240")
@@ -315,7 +315,7 @@ def test_hexpiretime_multiple_fields_mixed_conditions(r):
315315
@skip_if_server_version_lt("7.3.240")
316316
def test_hexpiretime_nonexistent_key(r):
317317
r.delete("test:hash")
318-
assert r.hexpiretime("test:hash", "field1", "field2", "field3") == []
318+
assert r.hexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2]
319319

320320

321321
@skip_if_server_version_lt("7.3.240")
@@ -332,7 +332,7 @@ def test_hpexpiretime_multiple_fields_mixed_conditions(r):
332332
@skip_if_server_version_lt("7.3.240")
333333
def test_hpexpiretime_nonexistent_key(r):
334334
r.delete("test:hash")
335-
assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == []
335+
assert r.hpexpiretime("test:hash", "field1", "field2", "field3") == [-2, -2, -2]
336336

337337

338338
@skip_if_server_version_lt("7.3.240")
@@ -349,7 +349,7 @@ def test_httl_multiple_fields_mixed_conditions(r):
349349
@skip_if_server_version_lt("7.3.240")
350350
def test_httl_nonexistent_key(r):
351351
r.delete("test:hash")
352-
assert r.httl("test:hash", "field1", "field2", "field3") == []
352+
assert r.httl("test:hash", "field1", "field2", "field3") == [-2, -2, -2]
353353

354354

355355
@skip_if_server_version_lt("7.3.240")
@@ -366,4 +366,4 @@ def test_hpttl_multiple_fields_mixed_conditions(r):
366366
@skip_if_server_version_lt("7.3.240")
367367
def test_hpttl_nonexistent_key(r):
368368
r.delete("test:hash")
369-
assert r.hpttl("test:hash", "field1", "field2", "field3") == []
369+
assert r.hpttl("test:hash", "field1", "field2", "field3") == [-2, -2, -2]

tests/test_json.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def test_mset(client):
130130
def test_clear(client):
131131
client.json().set("arr", Path.root_path(), [0, 1, 2, 3, 4])
132132
assert 1 == client.json().clear("arr", Path.root_path())
133-
assert_resp_response(client, client.json().get("arr"), [], [[[]]])
133+
assert_resp_response(client, client.json().get("arr"), [], [])
134134

135135

136136
@pytest.mark.redismod

tests/test_search.py

+17-16
Original file line numberDiff line numberDiff line change
@@ -2371,27 +2371,27 @@ def test_search_missing_fields(client):
23712371

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

23782378
res = client.ft().search(
2379-
Query("ismissing(@features)").dialect(5).return_field("id").no_content()
2379+
Query("ismissing(@features)").dialect(2).return_field("id").no_content()
23802380
)
23812381
_assert_search_result(client, res, ["property:2"])
23822382

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

23882388
res = client.ft().search(
2389-
Query("ismissing(@description)").dialect(5).return_field("id").no_content()
2389+
Query("ismissing(@description)").dialect(2).return_field("id").no_content()
23902390
)
23912391
_assert_search_result(client, res, ["property:3"])
23922392

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

@@ -2440,27 +2440,29 @@ def test_search_empty_fields(client):
24402440

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

24472447
res = client.ft().search(
2448-
Query("@features:{ }").dialect(5).return_field("id").no_content()
2448+
Query("@features:{$empty}").dialect(2).return_field("id").no_content(),
2449+
query_params={"empty": ""},
24492450
)
24502451
_assert_search_result(client, res, ["property:2"])
24512452

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

24572459
res = client.ft().search(
2458-
Query("@description:''").dialect(5).return_field("id").no_content()
2460+
Query("@description:''").dialect(2).return_field("id").no_content()
24592461
)
24602462
_assert_search_result(client, res, ["property:3"])
24612463

24622464
res = client.ft().search(
2463-
Query("-@description:''").dialect(5).return_field("id").no_content()
2465+
Query("-@description:''").dialect(2).return_field("id").no_content()
24642466
)
24652467
_assert_search_result(client, res, ["property:1", "property:2"])
24662468

@@ -2505,22 +2507,21 @@ def test_special_characters_in_fields(client):
25052507
)
25062508
_assert_search_result(client, res, ["resource:1"])
25072509

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

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

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

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

25262527

0 commit comments

Comments
 (0)