Skip to content

Commit 79086f5

Browse files
authored
Merge branch 'master' into DOC-4199-tces-for-combined-query-page
2 parents ebbbfcb + 17db62e commit 79086f5

File tree

17 files changed

+234
-83
lines changed

17 files changed

+234
-83
lines changed

.github/workflows/integration.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,21 @@ jobs:
9090
invoke ${{matrix.test-type}}-tests
9191
ls -1
9292
93+
- name: Run tests against hiredis < 3.0.0
94+
if: ${{ matrix.connection-type == 'hiredis' && matrix.python-version == '3.12'}}
95+
run: |
96+
pip uninstall hiredis -y
97+
pip install -U setuptools wheel
98+
pip install -r requirements.txt
99+
pip install -r dev_requirements.txt
100+
if [ "${{matrix.connection-type}}" == "hiredis" ]; then
101+
pip install "hiredis<3.0.0"
102+
fi
103+
invoke devenv
104+
sleep 10 # time to settle
105+
invoke ${{matrix.test-type}}-tests
106+
ls -1
107+
93108
- name: Upload test results and profiling data
94109
uses: actions/upload-artifact@v4
95110
with:
@@ -145,6 +160,24 @@ jobs:
145160
invoke ${{matrix.test-type}}-tests --protocol=3
146161
fi
147162
163+
- name: Run tests against hiredis < 3.0.0
164+
if: ${{ matrix.connection-type == 'hiredis' && matrix.python-version == '3.12'}}
165+
run: |
166+
pip uninstall hiredis -y
167+
pip install -U setuptools wheel
168+
pip install -r requirements.txt
169+
pip install -r dev_requirements.txt
170+
if [ "${{matrix.connection-type}}" == "hiredis" ]; then
171+
pip install "hiredis<3.0.0"
172+
fi
173+
invoke devenv
174+
sleep 10 # time to settle
175+
if [ "${{matrix.event-loop}}" == "uvloop" ]; then
176+
invoke ${{matrix.test-type}}-tests --uvloop --protocol=3
177+
else
178+
invoke ${{matrix.test-type}}-tests --protocol=3
179+
fi
180+
148181
- name: Upload test results and profiling data
149182
uses: actions/upload-artifact@v4
150183
with:

doctests/dt_set.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@
5858
r.sadd("bikes:racing:usa", "bike:1", "bike:4")
5959
# HIDE_END
6060
res7 = r.sinter("bikes:racing:france", "bikes:racing:usa")
61-
print(res7) # >>> ['bike:1']
61+
print(res7) # >>> {'bike:1'}
6262
# STEP_END
6363

6464
# REMOVE_START
65-
assert res7 == ["bike:1"]
65+
assert res7 == {"bike:1"}
6666
# REMOVE_END
6767

6868
# STEP_START scard
@@ -83,12 +83,12 @@
8383
print(res9) # >>> 3
8484

8585
res10 = r.smembers("bikes:racing:france")
86-
print(res10) # >>> ['bike:1', 'bike:2', 'bike:3']
86+
print(res10) # >>> {'bike:1', 'bike:2', 'bike:3'}
8787
# STEP_END
8888

8989
# REMOVE_START
9090
assert res9 == 3
91-
assert res10 == ['bike:1', 'bike:2', 'bike:3']
91+
assert res10 == {'bike:1', 'bike:2', 'bike:3'}
9292
# REMOVE_END
9393

9494
# STEP_START smismember
@@ -109,11 +109,11 @@
109109
r.sadd("bikes:racing:usa", "bike:1", "bike:4")
110110

111111
res13 = r.sdiff("bikes:racing:france", "bikes:racing:usa")
112-
print(res13) # >>> ['bike:2', 'bike:3']
112+
print(res13) # >>> {'bike:2', 'bike:3'}
113113
# STEP_END
114114

115115
# REMOVE_START
116-
assert res13 == ['bike:2', 'bike:3']
116+
assert res13 == {'bike:2', 'bike:3'}
117117
r.delete("bikes:racing:france")
118118
r.delete("bikes:racing:usa")
119119
# REMOVE_END
@@ -124,27 +124,27 @@
124124
r.sadd("bikes:racing:italy", "bike:1", "bike:2", "bike:3", "bike:4")
125125

126126
res13 = r.sinter("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy")
127-
print(res13) # >>> ['bike:1']
127+
print(res13) # >>> {'bike:1'}
128128

129129
res14 = r.sunion("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy")
130-
print(res14) # >>> ['bike:1', 'bike:2', 'bike:3', 'bike:4']
130+
print(res14) # >>> {'bike:1', 'bike:2', 'bike:3', 'bike:4'}
131131

132132
res15 = r.sdiff("bikes:racing:france", "bikes:racing:usa", "bikes:racing:italy")
133-
print(res15) # >>> []
133+
print(res15) # >>> {}
134134

135135
res16 = r.sdiff("bikes:racing:usa", "bikes:racing:france")
136-
print(res16) # >>> ['bike:4']
136+
print(res16) # >>> {'bike:4'}
137137

138138
res17 = r.sdiff("bikes:racing:france", "bikes:racing:usa")
139-
print(res17) # >>> ['bike:2', 'bike:3']
139+
print(res17) # >>> {'bike:2', 'bike:3'}
140140
# STEP_END
141141

142142
# REMOVE_START
143-
assert res13 == ['bike:1']
144-
assert res14 == ['bike:1', 'bike:2', 'bike:3', 'bike:4']
145-
assert res15 == []
146-
assert res16 == ['bike:4']
147-
assert res17 == ['bike:2', 'bike:3']
143+
assert res13 == {'bike:1'}
144+
assert res14 == {'bike:1', 'bike:2', 'bike:3', 'bike:4'}
145+
assert res15 == {}
146+
assert res16 == {'bike:4'}
147+
assert res17 == {'bike:2', 'bike:3'}
148148
r.delete("bikes:racing:france")
149149
r.delete("bikes:racing:usa")
150150
r.delete("bikes:racing:italy")
@@ -160,7 +160,7 @@
160160
print(res19) # >>> bike:3
161161

162162
res20 = r.smembers("bikes:racing:france")
163-
print(res20) # >>> ['bike:2', 'bike:4', 'bike:5']
163+
print(res20) # >>> {'bike:2', 'bike:4', 'bike:5'}
164164

165165
res21 = r.srandmember("bikes:racing:france")
166166
print(res21) # >>> bike:4

doctests/query_agg.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# EXAMPLE: query_agg
2+
# HIDE_START
3+
import json
4+
import redis
5+
from redis.commands.json.path import Path
6+
from redis.commands.search import Search
7+
from redis.commands.search.aggregation import AggregateRequest
8+
from redis.commands.search.field import NumericField, TagField
9+
from redis.commands.search.indexDefinition import IndexDefinition, IndexType
10+
import redis.commands.search.reducers as reducers
11+
12+
r = redis.Redis(decode_responses=True)
13+
14+
# create index
15+
schema = (
16+
TagField("$.condition", as_name="condition"),
17+
NumericField("$.price", as_name="price"),
18+
)
19+
20+
index = r.ft("idx:bicycle")
21+
index.create_index(
22+
schema,
23+
definition=IndexDefinition(prefix=["bicycle:"], index_type=IndexType.JSON),
24+
)
25+
26+
# load data
27+
with open("data/query_em.json") as f:
28+
bicycles = json.load(f)
29+
30+
pipeline = r.pipeline(transaction=False)
31+
for bid, bicycle in enumerate(bicycles):
32+
pipeline.json().set(f'bicycle:{bid}', Path.root_path(), bicycle)
33+
pipeline.execute()
34+
# HIDE_END
35+
36+
# STEP_START agg1
37+
search = Search(r, index_name="idx:bicycle")
38+
aggregate_request = AggregateRequest(query='@condition:{new}') \
39+
.load('__key', 'price') \
40+
.apply(discounted='@price - (@price * 0.1)')
41+
res = search.aggregate(aggregate_request)
42+
print(len(res.rows)) # >>> 5
43+
print(res.rows) # >>> [['__key', 'bicycle:0', ...
44+
#[['__key', 'bicycle:0', 'price', '270', 'discounted', '243'],
45+
# ['__key', 'bicycle:5', 'price', '810', 'discounted', '729'],
46+
# ['__key', 'bicycle:6', 'price', '2300', 'discounted', '2070'],
47+
# ['__key', 'bicycle:7', 'price', '430', 'discounted', '387'],
48+
# ['__key', 'bicycle:8', 'price', '1200', 'discounted', '1080']]
49+
# REMOVE_START
50+
assert len(res.rows) == 5
51+
# REMOVE_END
52+
# STEP_END
53+
54+
# STEP_START agg2
55+
search = Search(r, index_name="idx:bicycle")
56+
aggregate_request = AggregateRequest(query='*') \
57+
.load('price') \
58+
.apply(price_category='@price<1000') \
59+
.group_by('@condition', reducers.sum('@price_category').alias('num_affordable'))
60+
res = search.aggregate(aggregate_request)
61+
print(len(res.rows)) # >>> 3
62+
print(res.rows) # >>>
63+
#[['condition', 'refurbished', 'num_affordable', '1'],
64+
# ['condition', 'used', 'num_affordable', '1'],
65+
# ['condition', 'new', 'num_affordable', '3']]
66+
# REMOVE_START
67+
assert len(res.rows) == 3
68+
# REMOVE_END
69+
# STEP_END
70+
71+
# STEP_START agg3
72+
search = Search(r, index_name="idx:bicycle")
73+
aggregate_request = AggregateRequest(query='*') \
74+
.apply(type="'bicycle'") \
75+
.group_by('@type', reducers.count().alias('num_total'))
76+
res = search.aggregate(aggregate_request)
77+
print(len(res.rows)) # >>> 1
78+
print(res.rows) # >>> [['type', 'bicycle', 'num_total', '10']]
79+
# REMOVE_START
80+
assert len(res.rows) == 1
81+
# REMOVE_END
82+
# STEP_END
83+
84+
# STEP_START agg4
85+
search = Search(r, index_name="idx:bicycle")
86+
aggregate_request = AggregateRequest(query='*') \
87+
.load('__key') \
88+
.group_by('@condition', reducers.tolist('__key').alias('bicycles'))
89+
res = search.aggregate(aggregate_request)
90+
print(len(res.rows)) # >>> 3
91+
print(res.rows) # >>>
92+
#[['condition', 'refurbished', 'bicycles', ['bicycle:9']],
93+
# ['condition', 'used', 'bicycles', ['bicycle:1', 'bicycle:2', 'bicycle:3', 'bicycle:4']],
94+
# ['condition', 'new', 'bicycles', ['bicycle:5', 'bicycle:6', 'bicycle:7', 'bicycle:0', 'bicycle:8']]]
95+
# REMOVE_START
96+
assert len(res.rows) == 3
97+
# REMOVE_END
98+
# STEP_END
99+
100+
# REMOVE_START
101+
# destroy index and data
102+
r.ft("idx:bicycle").dropindex(delete_documents=True)
103+
# REMOVE_END

redis/_parsers/helpers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -785,6 +785,9 @@ def string_keys_to_dict(key_string, callback):
785785

786786

787787
_RedisCallbacksRESP2 = {
788+
**string_keys_to_dict(
789+
"SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
790+
),
788791
**string_keys_to_dict(
789792
"ZDIFF ZINTER ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZRANK ZREVRANGE "
790793
"ZREVRANGEBYSCORE ZREVRANK ZUNION",
@@ -829,6 +832,9 @@ def string_keys_to_dict(key_string, callback):
829832

830833

831834
_RedisCallbacksRESP3 = {
835+
**string_keys_to_dict(
836+
"SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
837+
),
832838
**string_keys_to_dict(
833839
"ZRANGE ZINTER ZPOPMAX ZPOPMIN ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE "
834840
"ZUNION HGETALL XREADGROUP",

redis/asyncio/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,6 +1423,10 @@ async def _execute_transaction( # noqa: C901
14231423
if not isinstance(r, Exception):
14241424
args, options = cmd
14251425
command_name = args[0]
1426+
1427+
# Remove keys entry, it needs only for cache.
1428+
options.pop("keys", None)
1429+
14261430
if command_name in self.response_callbacks:
14271431
r = self.response_callbacks[command_name](r, **options)
14281432
if inspect.isawaitable(r):

redis/cluster.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,10 @@ def _execute_command(self, target_node, *args, **kwargs):
11631163
asking = False
11641164
connection.send_command(*args, **kwargs)
11651165
response = redis_node.parse_response(connection, command, **kwargs)
1166+
1167+
# Remove keys entry, it needs only for cache.
1168+
kwargs.pop("keys", None)
1169+
11661170
if command in self.cluster_response_callbacks:
11671171
response = self.cluster_response_callbacks[command](
11681172
response, **kwargs

redis/connection.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
from .utils import (
3939
CRYPTOGRAPHY_AVAILABLE,
4040
HIREDIS_AVAILABLE,
41-
HIREDIS_PACK_AVAILABLE,
4241
SSL_AVAILABLE,
4342
compare_versions,
4443
ensure_string,
@@ -314,7 +313,7 @@ def __del__(self):
314313
def _construct_command_packer(self, packer):
315314
if packer is not None:
316315
return packer
317-
elif HIREDIS_PACK_AVAILABLE:
316+
elif HIREDIS_AVAILABLE:
318317
return HiredisRespSerializer()
319318
else:
320319
return PythonRespSerializer(self._buffer_cutoff, self.encoder.encode)

redis/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
# Only support Hiredis >= 3.0:
1010
HIREDIS_AVAILABLE = int(hiredis.__version__.split(".")[0]) >= 3
11-
HIREDIS_PACK_AVAILABLE = hasattr(hiredis, "pack_command")
11+
if not HIREDIS_AVAILABLE:
12+
raise ImportError("hiredis package should be >= 3.0.0")
1213
except ImportError:
1314
HIREDIS_AVAILABLE = False
14-
HIREDIS_PACK_AVAILABLE = False
1515

1616
try:
1717
import ssl # noqa

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
long_description_content_type="text/markdown",
99
keywords=["Redis", "key-value store", "database"],
1010
license="MIT",
11-
version="5.1.0b7",
11+
version="5.1.1",
1212
packages=find_packages(
1313
include=[
1414
"redis",

tests/test_asyncio/test_cluster.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,38 +1752,38 @@ async def test_cluster_rpoplpush(self, r: RedisCluster) -> None:
17521752

17531753
async def test_cluster_sdiff(self, r: RedisCluster) -> None:
17541754
await r.sadd("{foo}a", "1", "2", "3")
1755-
assert set(await r.sdiff("{foo}a", "{foo}b")) == {b"1", b"2", b"3"}
1755+
assert await r.sdiff("{foo}a", "{foo}b") == {b"1", b"2", b"3"}
17561756
await r.sadd("{foo}b", "2", "3")
1757-
assert await r.sdiff("{foo}a", "{foo}b") == [b"1"]
1757+
assert await r.sdiff("{foo}a", "{foo}b") == {b"1"}
17581758

17591759
async def test_cluster_sdiffstore(self, r: RedisCluster) -> None:
17601760
await r.sadd("{foo}a", "1", "2", "3")
17611761
assert await r.sdiffstore("{foo}c", "{foo}a", "{foo}b") == 3
1762-
assert set(await r.smembers("{foo}c")) == {b"1", b"2", b"3"}
1762+
assert await r.smembers("{foo}c") == {b"1", b"2", b"3"}
17631763
await r.sadd("{foo}b", "2", "3")
17641764
assert await r.sdiffstore("{foo}c", "{foo}a", "{foo}b") == 1
1765-
assert await r.smembers("{foo}c") == [b"1"]
1765+
assert await r.smembers("{foo}c") == {b"1"}
17661766

17671767
async def test_cluster_sinter(self, r: RedisCluster) -> None:
17681768
await r.sadd("{foo}a", "1", "2", "3")
1769-
assert await r.sinter("{foo}a", "{foo}b") == []
1769+
assert await r.sinter("{foo}a", "{foo}b") == set()
17701770
await r.sadd("{foo}b", "2", "3")
1771-
assert set(await r.sinter("{foo}a", "{foo}b")) == {b"2", b"3"}
1771+
assert await r.sinter("{foo}a", "{foo}b") == {b"2", b"3"}
17721772

17731773
async def test_cluster_sinterstore(self, r: RedisCluster) -> None:
17741774
await r.sadd("{foo}a", "1", "2", "3")
17751775
assert await r.sinterstore("{foo}c", "{foo}a", "{foo}b") == 0
1776-
assert await r.smembers("{foo}c") == []
1776+
assert await r.smembers("{foo}c") == set()
17771777
await r.sadd("{foo}b", "2", "3")
17781778
assert await r.sinterstore("{foo}c", "{foo}a", "{foo}b") == 2
1779-
assert set(await r.smembers("{foo}c")) == {b"2", b"3"}
1779+
assert await r.smembers("{foo}c") == {b"2", b"3"}
17801780

17811781
async def test_cluster_smove(self, r: RedisCluster) -> None:
17821782
await r.sadd("{foo}a", "a1", "a2")
17831783
await r.sadd("{foo}b", "b1", "b2")
17841784
assert await r.smove("{foo}a", "{foo}b", "a1")
1785-
assert await r.smembers("{foo}a") == [b"a2"]
1786-
assert set(await r.smembers("{foo}b")) == {b"b1", b"b2", b"a1"}
1785+
assert await r.smembers("{foo}a") == {b"a2"}
1786+
assert await r.smembers("{foo}b") == {b"b1", b"b2", b"a1"}
17871787

17881788
async def test_cluster_sunion(self, r: RedisCluster) -> None:
17891789
await r.sadd("{foo}a", "1", "2")

0 commit comments

Comments
 (0)