redis 5.1.0b7__tar.gz → 5.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {redis-5.1.0b7/redis.egg-info → redis-5.2.0}/PKG-INFO +3 -2
- {redis-5.1.0b7 → redis-5.2.0}/README.md +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/helpers.py +8 -3
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/resp3.py +23 -37
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/client.py +25 -64
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/cluster.py +30 -96
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/connection.py +9 -152
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/sentinel.py +0 -1
- redis-5.2.0/redis/cache.py +401 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/client.py +35 -54
- {redis-5.1.0b7 → redis-5.2.0}/redis/cluster.py +64 -78
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/bf/commands.py +1 -5
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/core.py +92 -128
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/commands.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/query_result.py +16 -1
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/_util.py +2 -2
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/aggregation.py +27 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/commands.py +33 -22
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/query.py +19 -4
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/result.py +29 -15
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/timeseries/commands.py +55 -55
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/timeseries/info.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/redis/connection.py +371 -162
- {redis-5.1.0b7 → redis-5.2.0}/redis/retry.py +21 -5
- {redis-5.1.0b7 → redis-5.2.0}/redis/sentinel.py +9 -2
- {redis-5.1.0b7 → redis-5.2.0}/redis/typing.py +2 -4
- {redis-5.1.0b7 → redis-5.2.0}/redis/utils.py +55 -4
- {redis-5.1.0b7 → redis-5.2.0/redis.egg-info}/PKG-INFO +3 -2
- {redis-5.1.0b7 → redis-5.2.0}/redis.egg-info/SOURCES.txt +2 -2
- {redis-5.1.0b7 → redis-5.2.0}/redis.egg-info/requires.txt +3 -3
- {redis-5.1.0b7 → redis-5.2.0}/setup.py +4 -3
- {redis-5.1.0b7 → redis-5.2.0}/tests/conftest.py +81 -5
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/conftest.py +0 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_bloom.py +1 -6
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_cluster.py +13 -9
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_commands.py +7 -9
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_connect.py +11 -23
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_connection.py +56 -2
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_connection_pool.py +0 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_cwe_404.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_hash.py +5 -5
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_json.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_lock.py +4 -4
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_pipeline.py +10 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_pubsub.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_search.py +145 -7
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_timeseries.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_bloom.py +1 -6
- redis-5.2.0/tests/test_cache.py +1223 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_cluster.py +4 -7
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_command_parser.py +0 -4
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_commands.py +15 -28
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_connect.py +32 -23
- redis-5.2.0/tests/test_connection.py +565 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_encoding.py +0 -18
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_function.py +2 -2
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_graph.py +9 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_hash.py +9 -9
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_json.py +1 -2
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_lock.py +1 -1
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_pipeline.py +10 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_retry.py +2 -2
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_search.py +149 -18
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_timeseries.py +1 -1
- redis-5.2.0/tests/test_utils.py +27 -0
- redis-5.1.0b7/redis/_cache.py +0 -385
- redis-5.1.0b7/tests/test_asyncio/test_cache.py +0 -408
- redis-5.1.0b7/tests/test_cache.py +0 -587
- redis-5.1.0b7/tests/test_connection.py +0 -298
- {redis-5.1.0b7 → redis-5.2.0}/INSTALL +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/LICENSE +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/MANIFEST.in +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/base.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/commands.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/encoders.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/hiredis.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/resp2.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/_parsers/socket.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/lock.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/retry.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/asyncio/utils.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/backoff.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/bf/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/bf/info.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/cluster.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/edge.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/exceptions.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/execution_plan.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/node.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/graph/path.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/helpers.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/json/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/json/_util.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/json/commands.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/json/decoders.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/json/path.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/redismodules.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/document.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/field.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/indexDefinition.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/querystring.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/reducers.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/search/suggestion.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/sentinel.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/timeseries/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/commands/timeseries/utils.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/crc.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/credentials.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/exceptions.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/lock.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis/ocsp.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis.egg-info/dependency_links.txt +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/redis.egg-info/top_level.txt +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/setup.cfg +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/mocks.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/ssl_utils.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/compat.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/mocks.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_credentials.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_encoding.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_graph.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_monitor.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_retry.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_scripting.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_sentinel.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/testdata/titles.csv +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_connection_pool.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_credentials.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_graph_utils/__init__.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_graph_utils/test_edge.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_graph_utils/test_node.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_graph_utils/test_path.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_helpers.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_monitor.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_multiprocessing.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_parsers/test_helpers.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_pubsub.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_scripting.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_sentinel.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/test_ssl.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/testdata/jsontestdata.py +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/testdata/titles.csv +0 -0
- {redis-5.1.0b7 → redis-5.2.0}/tests/testdata/will_play_text.csv.bz2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: redis
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.2.0
|
|
4
4
|
Summary: Python client for Redis database and key-value store
|
|
5
5
|
Home-page: https://github.com/redis/redis-py
|
|
6
6
|
Author: Redis Inc.
|
|
@@ -24,6 +24,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
24
24
|
Classifier: Programming Language :: Python :: 3.9
|
|
25
25
|
Classifier: Programming Language :: Python :: 3.10
|
|
26
26
|
Classifier: Programming Language :: Python :: 3.11
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
28
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
28
29
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
29
30
|
Requires-Python: >=3.8
|
|
@@ -88,7 +89,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
|
|
|
88
89
|
|
|
89
90
|
## Supported Redis Versions
|
|
90
91
|
|
|
91
|
-
The most recent version of this library supports redis version [5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES), [6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), [6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES), [7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES)
|
|
92
|
+
The most recent version of this library supports redis version [5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES), [6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), [6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES), [7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES), [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES) and [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES).
|
|
92
93
|
|
|
93
94
|
The table below highlights version compatibility of the most-recent library versions and redis versions.
|
|
94
95
|
|
|
@@ -54,7 +54,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
|
|
|
54
54
|
|
|
55
55
|
## Supported Redis Versions
|
|
56
56
|
|
|
57
|
-
The most recent version of this library supports redis version [5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES), [6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), [6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES), [7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES)
|
|
57
|
+
The most recent version of this library supports redis version [5.0](https://github.com/redis/redis/blob/5.0/00-RELEASENOTES), [6.0](https://github.com/redis/redis/blob/6.0/00-RELEASENOTES), [6.2](https://github.com/redis/redis/blob/6.2/00-RELEASENOTES), [7.0](https://github.com/redis/redis/blob/7.0/00-RELEASENOTES), [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES) and [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES).
|
|
58
58
|
|
|
59
59
|
The table below highlights version compatibility of the most-recent library versions and redis versions.
|
|
60
60
|
|
|
@@ -445,9 +445,11 @@ def parse_cluster_info(response, **options):
|
|
|
445
445
|
def _parse_node_line(line):
|
|
446
446
|
line_items = line.split(" ")
|
|
447
447
|
node_id, addr, flags, master_id, ping, pong, epoch, connected = line.split(" ")[:8]
|
|
448
|
-
|
|
448
|
+
ip = addr.split("@")[0]
|
|
449
|
+
hostname = addr.split("@")[1].split(",")[1] if "@" in addr and "," in addr else ""
|
|
449
450
|
node_dict = {
|
|
450
451
|
"node_id": node_id,
|
|
452
|
+
"hostname": hostname,
|
|
451
453
|
"flags": flags,
|
|
452
454
|
"master_id": master_id,
|
|
453
455
|
"last_ping_sent": ping,
|
|
@@ -460,7 +462,7 @@ def _parse_node_line(line):
|
|
|
460
462
|
if len(line_items) >= 9:
|
|
461
463
|
slots, migrations = _parse_slots(line_items[8:])
|
|
462
464
|
node_dict["slots"], node_dict["migrations"] = slots, migrations
|
|
463
|
-
return
|
|
465
|
+
return ip, node_dict
|
|
464
466
|
|
|
465
467
|
|
|
466
468
|
def _parse_slots(slot_ranges):
|
|
@@ -507,7 +509,7 @@ def parse_geosearch_generic(response, **options):
|
|
|
507
509
|
except KeyError: # it means the command was sent via execute_command
|
|
508
510
|
return response
|
|
509
511
|
|
|
510
|
-
if
|
|
512
|
+
if not isinstance(response, list):
|
|
511
513
|
response_list = [response]
|
|
512
514
|
else:
|
|
513
515
|
response_list = response
|
|
@@ -830,6 +832,9 @@ _RedisCallbacksRESP2 = {
|
|
|
830
832
|
|
|
831
833
|
|
|
832
834
|
_RedisCallbacksRESP3 = {
|
|
835
|
+
**string_keys_to_dict(
|
|
836
|
+
"SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
|
|
837
|
+
),
|
|
833
838
|
**string_keys_to_dict(
|
|
834
839
|
"ZRANGE ZINTER ZPOPMAX ZPOPMIN ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE "
|
|
835
840
|
"ZUNION HGETALL XREADGROUP",
|
|
@@ -88,15 +88,11 @@ class _RESP3Parser(_RESPBase):
|
|
|
88
88
|
# set response
|
|
89
89
|
elif byte == b"~":
|
|
90
90
|
# redis can return unhashable types (like dict) in a set,
|
|
91
|
-
# so we
|
|
91
|
+
# so we return sets as list, all the time, for predictability
|
|
92
92
|
response = [
|
|
93
93
|
self._read_response(disable_decoding=disable_decoding)
|
|
94
94
|
for _ in range(int(response))
|
|
95
95
|
]
|
|
96
|
-
try:
|
|
97
|
-
response = set(response)
|
|
98
|
-
except TypeError:
|
|
99
|
-
pass
|
|
100
96
|
# map response
|
|
101
97
|
elif byte == b"%":
|
|
102
98
|
# We cannot use a dict-comprehension to parse stream.
|
|
@@ -120,6 +116,12 @@ class _RESP3Parser(_RESPBase):
|
|
|
120
116
|
response = self.handle_push_response(
|
|
121
117
|
response, disable_decoding, push_request
|
|
122
118
|
)
|
|
119
|
+
if not push_request:
|
|
120
|
+
return self._read_response(
|
|
121
|
+
disable_decoding=disable_decoding, push_request=push_request
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
return response
|
|
123
125
|
else:
|
|
124
126
|
raise InvalidResponse(f"Protocol Error: {raw!r}")
|
|
125
127
|
|
|
@@ -128,19 +130,10 @@ class _RESP3Parser(_RESPBase):
|
|
|
128
130
|
return response
|
|
129
131
|
|
|
130
132
|
def handle_push_response(self, response, disable_decoding, push_request):
|
|
131
|
-
if response[0] in _INVALIDATION_MESSAGE:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
res = None
|
|
136
|
-
else:
|
|
137
|
-
res = self.pubsub_push_handler_func(response)
|
|
138
|
-
if not push_request:
|
|
139
|
-
return self._read_response(
|
|
140
|
-
disable_decoding=disable_decoding, push_request=push_request
|
|
141
|
-
)
|
|
142
|
-
else:
|
|
143
|
-
return res
|
|
133
|
+
if response[0] not in _INVALIDATION_MESSAGE:
|
|
134
|
+
return self.pubsub_push_handler_func(response)
|
|
135
|
+
if self.invalidation_push_handler_func:
|
|
136
|
+
return self.invalidation_push_handler_func(response)
|
|
144
137
|
|
|
145
138
|
def set_pubsub_push_handler(self, pubsub_push_handler_func):
|
|
146
139
|
self.pubsub_push_handler_func = pubsub_push_handler_func
|
|
@@ -155,7 +148,7 @@ class _AsyncRESP3Parser(_AsyncRESPBase):
|
|
|
155
148
|
self.pubsub_push_handler_func = self.handle_pubsub_push_response
|
|
156
149
|
self.invalidation_push_handler_func = None
|
|
157
150
|
|
|
158
|
-
def handle_pubsub_push_response(self, response):
|
|
151
|
+
async def handle_pubsub_push_response(self, response):
|
|
159
152
|
logger = getLogger("push_response")
|
|
160
153
|
logger.info("Push response: " + str(response))
|
|
161
154
|
return response
|
|
@@ -233,15 +226,11 @@ class _AsyncRESP3Parser(_AsyncRESPBase):
|
|
|
233
226
|
# set response
|
|
234
227
|
elif byte == b"~":
|
|
235
228
|
# redis can return unhashable types (like dict) in a set,
|
|
236
|
-
# so we
|
|
229
|
+
# so we always convert to a list, to have predictable return types
|
|
237
230
|
response = [
|
|
238
231
|
(await self._read_response(disable_decoding=disable_decoding))
|
|
239
232
|
for _ in range(int(response))
|
|
240
233
|
]
|
|
241
|
-
try:
|
|
242
|
-
response = set(response)
|
|
243
|
-
except TypeError:
|
|
244
|
-
pass
|
|
245
234
|
# map response
|
|
246
235
|
elif byte == b"%":
|
|
247
236
|
# We cannot use a dict-comprehension to parse stream.
|
|
@@ -267,6 +256,12 @@ class _AsyncRESP3Parser(_AsyncRESPBase):
|
|
|
267
256
|
response = await self.handle_push_response(
|
|
268
257
|
response, disable_decoding, push_request
|
|
269
258
|
)
|
|
259
|
+
if not push_request:
|
|
260
|
+
return await self._read_response(
|
|
261
|
+
disable_decoding=disable_decoding, push_request=push_request
|
|
262
|
+
)
|
|
263
|
+
else:
|
|
264
|
+
return response
|
|
270
265
|
else:
|
|
271
266
|
raise InvalidResponse(f"Protocol Error: {raw!r}")
|
|
272
267
|
|
|
@@ -275,19 +270,10 @@ class _AsyncRESP3Parser(_AsyncRESPBase):
|
|
|
275
270
|
return response
|
|
276
271
|
|
|
277
272
|
async def handle_push_response(self, response, disable_decoding, push_request):
|
|
278
|
-
if response[0] in _INVALIDATION_MESSAGE:
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
res = None
|
|
283
|
-
else:
|
|
284
|
-
res = self.pubsub_push_handler_func(response)
|
|
285
|
-
if not push_request:
|
|
286
|
-
return await self._read_response(
|
|
287
|
-
disable_decoding=disable_decoding, push_request=push_request
|
|
288
|
-
)
|
|
289
|
-
else:
|
|
290
|
-
return res
|
|
273
|
+
if response[0] not in _INVALIDATION_MESSAGE:
|
|
274
|
+
return await self.pubsub_push_handler_func(response)
|
|
275
|
+
if self.invalidation_push_handler_func:
|
|
276
|
+
return await self.invalidation_push_handler_func(response)
|
|
291
277
|
|
|
292
278
|
def set_pubsub_push_handler(self, pubsub_push_handler_func):
|
|
293
279
|
self.pubsub_push_handler_func = pubsub_push_handler_func
|
|
@@ -26,12 +26,6 @@ from typing import (
|
|
|
26
26
|
cast,
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
from redis._cache import (
|
|
30
|
-
DEFAULT_ALLOW_LIST,
|
|
31
|
-
DEFAULT_DENY_LIST,
|
|
32
|
-
DEFAULT_EVICTION_POLICY,
|
|
33
|
-
AbstractCache,
|
|
34
|
-
)
|
|
35
29
|
from redis._parsers.helpers import (
|
|
36
30
|
_RedisCallbacks,
|
|
37
31
|
_RedisCallbacksRESP2,
|
|
@@ -239,13 +233,6 @@ class Redis(
|
|
|
239
233
|
redis_connect_func=None,
|
|
240
234
|
credential_provider: Optional[CredentialProvider] = None,
|
|
241
235
|
protocol: Optional[int] = 2,
|
|
242
|
-
cache_enabled: bool = False,
|
|
243
|
-
client_cache: Optional[AbstractCache] = None,
|
|
244
|
-
cache_max_size: int = 100,
|
|
245
|
-
cache_ttl: int = 0,
|
|
246
|
-
cache_policy: str = DEFAULT_EVICTION_POLICY,
|
|
247
|
-
cache_deny_list: List[str] = DEFAULT_DENY_LIST,
|
|
248
|
-
cache_allow_list: List[str] = DEFAULT_ALLOW_LIST,
|
|
249
236
|
):
|
|
250
237
|
"""
|
|
251
238
|
Initialize a new Redis client.
|
|
@@ -295,13 +282,6 @@ class Redis(
|
|
|
295
282
|
"lib_version": lib_version,
|
|
296
283
|
"redis_connect_func": redis_connect_func,
|
|
297
284
|
"protocol": protocol,
|
|
298
|
-
"cache_enabled": cache_enabled,
|
|
299
|
-
"client_cache": client_cache,
|
|
300
|
-
"cache_max_size": cache_max_size,
|
|
301
|
-
"cache_ttl": cache_ttl,
|
|
302
|
-
"cache_policy": cache_policy,
|
|
303
|
-
"cache_deny_list": cache_deny_list,
|
|
304
|
-
"cache_allow_list": cache_allow_list,
|
|
305
285
|
}
|
|
306
286
|
# based on input, setup appropriate connection args
|
|
307
287
|
if unix_socket_path is not None:
|
|
@@ -579,10 +559,12 @@ class Redis(
|
|
|
579
559
|
"""
|
|
580
560
|
Closes Redis client connection
|
|
581
561
|
|
|
582
|
-
:
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
562
|
+
Args:
|
|
563
|
+
close_connection_pool:
|
|
564
|
+
decides whether to close the connection pool used by this Redis client,
|
|
565
|
+
overriding Redis.auto_close_connection_pool.
|
|
566
|
+
By default, let Redis.auto_close_connection_pool decide
|
|
567
|
+
whether to close the connection pool.
|
|
586
568
|
"""
|
|
587
569
|
conn = self.connection
|
|
588
570
|
if conn:
|
|
@@ -624,31 +606,22 @@ class Redis(
|
|
|
624
606
|
async def execute_command(self, *args, **options):
|
|
625
607
|
"""Execute a command and return a parsed response"""
|
|
626
608
|
await self.initialize()
|
|
627
|
-
command_name = args[0]
|
|
628
|
-
keys = options.pop("keys", None) # keys are used only for client side caching
|
|
629
609
|
pool = self.connection_pool
|
|
610
|
+
command_name = args[0]
|
|
630
611
|
conn = self.connection or await pool.get_connection(command_name, **options)
|
|
631
|
-
|
|
612
|
+
|
|
613
|
+
if self.single_connection_client:
|
|
614
|
+
await self._single_conn_lock.acquire()
|
|
632
615
|
try:
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
response = await conn.retry.call_with_retry(
|
|
640
|
-
lambda: self._send_command_parse_response(
|
|
641
|
-
conn, command_name, *args, **options
|
|
642
|
-
),
|
|
643
|
-
lambda error: self._disconnect_raise(conn, error),
|
|
644
|
-
)
|
|
645
|
-
if keys:
|
|
646
|
-
conn._add_to_local_cache(args, response, keys)
|
|
647
|
-
return response
|
|
648
|
-
finally:
|
|
649
|
-
if self.single_connection_client:
|
|
650
|
-
self._single_conn_lock.release()
|
|
616
|
+
return await conn.retry.call_with_retry(
|
|
617
|
+
lambda: self._send_command_parse_response(
|
|
618
|
+
conn, command_name, *args, **options
|
|
619
|
+
),
|
|
620
|
+
lambda error: self._disconnect_raise(conn, error),
|
|
621
|
+
)
|
|
651
622
|
finally:
|
|
623
|
+
if self.single_connection_client:
|
|
624
|
+
self._single_conn_lock.release()
|
|
652
625
|
if not self.connection:
|
|
653
626
|
await pool.release(conn)
|
|
654
627
|
|
|
@@ -670,6 +643,9 @@ class Redis(
|
|
|
670
643
|
if EMPTY_RESPONSE in options:
|
|
671
644
|
options.pop(EMPTY_RESPONSE)
|
|
672
645
|
|
|
646
|
+
# Remove keys entry, it needs only for cache.
|
|
647
|
+
options.pop("keys", None)
|
|
648
|
+
|
|
673
649
|
if command_name in self.response_callbacks:
|
|
674
650
|
# Mypy bug: https://github.com/python/mypy/issues/10977
|
|
675
651
|
command_name = cast(str, command_name)
|
|
@@ -677,24 +653,6 @@ class Redis(
|
|
|
677
653
|
return await retval if inspect.isawaitable(retval) else retval
|
|
678
654
|
return response
|
|
679
655
|
|
|
680
|
-
def flush_cache(self):
|
|
681
|
-
if self.connection:
|
|
682
|
-
self.connection.flush_cache()
|
|
683
|
-
else:
|
|
684
|
-
self.connection_pool.flush_cache()
|
|
685
|
-
|
|
686
|
-
def delete_command_from_cache(self, command):
|
|
687
|
-
if self.connection:
|
|
688
|
-
self.connection.delete_command_from_cache(command)
|
|
689
|
-
else:
|
|
690
|
-
self.connection_pool.delete_command_from_cache(command)
|
|
691
|
-
|
|
692
|
-
def invalidate_key_from_cache(self, key):
|
|
693
|
-
if self.connection:
|
|
694
|
-
self.connection.invalidate_key_from_cache(key)
|
|
695
|
-
else:
|
|
696
|
-
self.connection_pool.invalidate_key_from_cache(key)
|
|
697
|
-
|
|
698
656
|
|
|
699
657
|
StrictRedis = Redis
|
|
700
658
|
|
|
@@ -1331,7 +1289,6 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
|
|
|
1331
1289
|
def execute_command(
|
|
1332
1290
|
self, *args, **kwargs
|
|
1333
1291
|
) -> Union["Pipeline", Awaitable["Pipeline"]]:
|
|
1334
|
-
kwargs.pop("keys", None) # the keys are used only for client side caching
|
|
1335
1292
|
if (self.watching or args[0] == "WATCH") and not self.explicit_transaction:
|
|
1336
1293
|
return self.immediate_execute_command(*args, **kwargs)
|
|
1337
1294
|
return self.pipeline_execute_command(*args, **kwargs)
|
|
@@ -1466,6 +1423,10 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
|
|
|
1466
1423
|
if not isinstance(r, Exception):
|
|
1467
1424
|
args, options = cmd
|
|
1468
1425
|
command_name = args[0]
|
|
1426
|
+
|
|
1427
|
+
# Remove keys entry, it needs only for cache.
|
|
1428
|
+
options.pop("keys", None)
|
|
1429
|
+
|
|
1469
1430
|
if command_name in self.response_callbacks:
|
|
1470
1431
|
r = self.response_callbacks[command_name](r, **options)
|
|
1471
1432
|
if inspect.isawaitable(r):
|
|
@@ -19,12 +19,6 @@ from typing import (
|
|
|
19
19
|
Union,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
from redis._cache import (
|
|
23
|
-
DEFAULT_ALLOW_LIST,
|
|
24
|
-
DEFAULT_DENY_LIST,
|
|
25
|
-
DEFAULT_EVICTION_POLICY,
|
|
26
|
-
AbstractCache,
|
|
27
|
-
)
|
|
28
22
|
from redis._parsers import AsyncCommandsParser, Encoder
|
|
29
23
|
from redis._parsers.helpers import (
|
|
30
24
|
_RedisCallbacks,
|
|
@@ -276,13 +270,6 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
|
|
|
276
270
|
ssl_ciphers: Optional[str] = None,
|
|
277
271
|
protocol: Optional[int] = 2,
|
|
278
272
|
address_remap: Optional[Callable[[Tuple[str, int]], Tuple[str, int]]] = None,
|
|
279
|
-
cache_enabled: bool = False,
|
|
280
|
-
client_cache: Optional[AbstractCache] = None,
|
|
281
|
-
cache_max_size: int = 100,
|
|
282
|
-
cache_ttl: int = 0,
|
|
283
|
-
cache_policy: str = DEFAULT_EVICTION_POLICY,
|
|
284
|
-
cache_deny_list: List[str] = DEFAULT_DENY_LIST,
|
|
285
|
-
cache_allow_list: List[str] = DEFAULT_ALLOW_LIST,
|
|
286
273
|
) -> None:
|
|
287
274
|
if db:
|
|
288
275
|
raise RedisClusterException(
|
|
@@ -326,14 +313,6 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
|
|
|
326
313
|
"socket_timeout": socket_timeout,
|
|
327
314
|
"retry": retry,
|
|
328
315
|
"protocol": protocol,
|
|
329
|
-
# Client cache related kwargs
|
|
330
|
-
"cache_enabled": cache_enabled,
|
|
331
|
-
"client_cache": client_cache,
|
|
332
|
-
"cache_max_size": cache_max_size,
|
|
333
|
-
"cache_ttl": cache_ttl,
|
|
334
|
-
"cache_policy": cache_policy,
|
|
335
|
-
"cache_deny_list": cache_deny_list,
|
|
336
|
-
"cache_allow_list": cache_allow_list,
|
|
337
316
|
}
|
|
338
317
|
|
|
339
318
|
if ssl:
|
|
@@ -938,18 +917,6 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
|
|
|
938
917
|
thread_local=thread_local,
|
|
939
918
|
)
|
|
940
919
|
|
|
941
|
-
def flush_cache(self):
|
|
942
|
-
if self.nodes_manager:
|
|
943
|
-
self.nodes_manager.flush_cache()
|
|
944
|
-
|
|
945
|
-
def delete_command_from_cache(self, command):
|
|
946
|
-
if self.nodes_manager:
|
|
947
|
-
self.nodes_manager.delete_command_from_cache(command)
|
|
948
|
-
|
|
949
|
-
def invalidate_key_from_cache(self, key):
|
|
950
|
-
if self.nodes_manager:
|
|
951
|
-
self.nodes_manager.invalidate_key_from_cache(key)
|
|
952
|
-
|
|
953
920
|
|
|
954
921
|
class ClusterNode:
|
|
955
922
|
"""
|
|
@@ -1067,6 +1034,9 @@ class ClusterNode:
|
|
|
1067
1034
|
if EMPTY_RESPONSE in kwargs:
|
|
1068
1035
|
kwargs.pop(EMPTY_RESPONSE)
|
|
1069
1036
|
|
|
1037
|
+
# Remove keys entry, it needs only for cache.
|
|
1038
|
+
kwargs.pop("keys", None)
|
|
1039
|
+
|
|
1070
1040
|
# Return response
|
|
1071
1041
|
if command in self.response_callbacks:
|
|
1072
1042
|
return self.response_callbacks[command](response, **kwargs)
|
|
@@ -1076,25 +1046,16 @@ class ClusterNode:
|
|
|
1076
1046
|
async def execute_command(self, *args: Any, **kwargs: Any) -> Any:
|
|
1077
1047
|
# Acquire connection
|
|
1078
1048
|
connection = self.acquire_connection()
|
|
1079
|
-
keys = kwargs.pop("keys", None)
|
|
1080
1049
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
self._free.append(connection)
|
|
1084
|
-
return response_from_cache
|
|
1085
|
-
else:
|
|
1086
|
-
# Execute command
|
|
1087
|
-
await connection.send_packed_command(connection.pack_command(*args), False)
|
|
1050
|
+
# Execute command
|
|
1051
|
+
await connection.send_packed_command(connection.pack_command(*args), False)
|
|
1088
1052
|
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
finally:
|
|
1096
|
-
# Release connection
|
|
1097
|
-
self._free.append(connection)
|
|
1053
|
+
# Read response
|
|
1054
|
+
try:
|
|
1055
|
+
return await self.parse_response(connection, args[0], **kwargs)
|
|
1056
|
+
finally:
|
|
1057
|
+
# Release connection
|
|
1058
|
+
self._free.append(connection)
|
|
1098
1059
|
|
|
1099
1060
|
async def execute_pipeline(self, commands: List["PipelineCommand"]) -> bool:
|
|
1100
1061
|
# Acquire connection
|
|
@@ -1121,18 +1082,6 @@ class ClusterNode:
|
|
|
1121
1082
|
|
|
1122
1083
|
return ret
|
|
1123
1084
|
|
|
1124
|
-
def flush_cache(self):
|
|
1125
|
-
for connection in self._connections:
|
|
1126
|
-
connection.flush_cache()
|
|
1127
|
-
|
|
1128
|
-
def delete_command_from_cache(self, command):
|
|
1129
|
-
for connection in self._connections:
|
|
1130
|
-
connection.delete_command_from_cache(command)
|
|
1131
|
-
|
|
1132
|
-
def invalidate_key_from_cache(self, key):
|
|
1133
|
-
for connection in self._connections:
|
|
1134
|
-
connection.invalidate_key_from_cache(key)
|
|
1135
|
-
|
|
1136
1085
|
|
|
1137
1086
|
class NodesManager:
|
|
1138
1087
|
__slots__ = (
|
|
@@ -1317,6 +1266,8 @@ class NodesManager:
|
|
|
1317
1266
|
port = int(primary_node[1])
|
|
1318
1267
|
host, port = self.remap_host_port(host, port)
|
|
1319
1268
|
|
|
1269
|
+
nodes_for_slot = []
|
|
1270
|
+
|
|
1320
1271
|
target_node = tmp_nodes_cache.get(get_node_name(host, port))
|
|
1321
1272
|
if not target_node:
|
|
1322
1273
|
target_node = ClusterNode(
|
|
@@ -1324,30 +1275,26 @@ class NodesManager:
|
|
|
1324
1275
|
)
|
|
1325
1276
|
# add this node to the nodes cache
|
|
1326
1277
|
tmp_nodes_cache[target_node.name] = target_node
|
|
1278
|
+
nodes_for_slot.append(target_node)
|
|
1279
|
+
|
|
1280
|
+
replica_nodes = slot[3:]
|
|
1281
|
+
for replica_node in replica_nodes:
|
|
1282
|
+
host = replica_node[0]
|
|
1283
|
+
port = replica_node[1]
|
|
1284
|
+
host, port = self.remap_host_port(host, port)
|
|
1285
|
+
|
|
1286
|
+
target_replica_node = tmp_nodes_cache.get(get_node_name(host, port))
|
|
1287
|
+
if not target_replica_node:
|
|
1288
|
+
target_replica_node = ClusterNode(
|
|
1289
|
+
host, port, REPLICA, **self.connection_kwargs
|
|
1290
|
+
)
|
|
1291
|
+
# add this node to the nodes cache
|
|
1292
|
+
tmp_nodes_cache[target_replica_node.name] = target_replica_node
|
|
1293
|
+
nodes_for_slot.append(target_replica_node)
|
|
1327
1294
|
|
|
1328
1295
|
for i in range(int(slot[0]), int(slot[1]) + 1):
|
|
1329
1296
|
if i not in tmp_slots:
|
|
1330
|
-
tmp_slots[i] =
|
|
1331
|
-
tmp_slots[i].append(target_node)
|
|
1332
|
-
replica_nodes = [slot[j] for j in range(3, len(slot))]
|
|
1333
|
-
|
|
1334
|
-
for replica_node in replica_nodes:
|
|
1335
|
-
host = replica_node[0]
|
|
1336
|
-
port = replica_node[1]
|
|
1337
|
-
host, port = self.remap_host_port(host, port)
|
|
1338
|
-
|
|
1339
|
-
target_replica_node = tmp_nodes_cache.get(
|
|
1340
|
-
get_node_name(host, port)
|
|
1341
|
-
)
|
|
1342
|
-
if not target_replica_node:
|
|
1343
|
-
target_replica_node = ClusterNode(
|
|
1344
|
-
host, port, REPLICA, **self.connection_kwargs
|
|
1345
|
-
)
|
|
1346
|
-
tmp_slots[i].append(target_replica_node)
|
|
1347
|
-
# add this node to the nodes cache
|
|
1348
|
-
tmp_nodes_cache[target_replica_node.name] = (
|
|
1349
|
-
target_replica_node
|
|
1350
|
-
)
|
|
1297
|
+
tmp_slots[i] = nodes_for_slot
|
|
1351
1298
|
else:
|
|
1352
1299
|
# Validate that 2 nodes want to use the same slot cache
|
|
1353
1300
|
# setup
|
|
@@ -1418,18 +1365,6 @@ class NodesManager:
|
|
|
1418
1365
|
return self.address_remap((host, port))
|
|
1419
1366
|
return host, port
|
|
1420
1367
|
|
|
1421
|
-
def flush_cache(self):
|
|
1422
|
-
for node in self.nodes_cache.values():
|
|
1423
|
-
node.flush_cache()
|
|
1424
|
-
|
|
1425
|
-
def delete_command_from_cache(self, command):
|
|
1426
|
-
for node in self.nodes_cache.values():
|
|
1427
|
-
node.delete_command_from_cache(command)
|
|
1428
|
-
|
|
1429
|
-
def invalidate_key_from_cache(self, key):
|
|
1430
|
-
for node in self.nodes_cache.values():
|
|
1431
|
-
node.invalidate_key_from_cache(key)
|
|
1432
|
-
|
|
1433
1368
|
|
|
1434
1369
|
class ClusterPipeline(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommands):
|
|
1435
1370
|
"""
|
|
@@ -1518,7 +1453,6 @@ class ClusterPipeline(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterComm
|
|
|
1518
1453
|
or List[:class:`~.ClusterNode`] or Dict[Any, :class:`~.ClusterNode`]
|
|
1519
1454
|
- Rest of the kwargs are passed to the Redis connection
|
|
1520
1455
|
"""
|
|
1521
|
-
kwargs.pop("keys", None) # the keys are used only for client side caching
|
|
1522
1456
|
self._command_stack.append(
|
|
1523
1457
|
PipelineCommand(len(self._command_stack), *args, **kwargs)
|
|
1524
1458
|
)
|