redis 6.3.0__tar.gz → 7.0.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-6.3.0 → redis-7.0.0}/PKG-INFO +7 -4
- {redis-6.3.0 → redis-7.0.0}/README.md +4 -3
- redis-7.0.0/dev_requirements.txt +30 -0
- {redis-6.3.0 → redis-7.0.0}/pyproject.toml +9 -0
- {redis-6.3.0 → redis-7.0.0}/redis/__init__.py +1 -2
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/base.py +193 -8
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/helpers.py +64 -6
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/hiredis.py +16 -10
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/resp3.py +11 -5
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/client.py +65 -8
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/cluster.py +57 -14
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/connection.py +62 -2
- redis-7.0.0/redis/asyncio/http/http_client.py +265 -0
- redis-7.0.0/redis/asyncio/multidb/client.py +530 -0
- redis-7.0.0/redis/asyncio/multidb/command_executor.py +339 -0
- redis-7.0.0/redis/asyncio/multidb/config.py +210 -0
- redis-7.0.0/redis/asyncio/multidb/database.py +69 -0
- redis-7.0.0/redis/asyncio/multidb/event.py +84 -0
- redis-7.0.0/redis/asyncio/multidb/failover.py +125 -0
- redis-7.0.0/redis/asyncio/multidb/failure_detector.py +38 -0
- redis-7.0.0/redis/asyncio/multidb/healthcheck.py +285 -0
- redis-7.0.0/redis/background.py +204 -0
- {redis-6.3.0 → redis-7.0.0}/redis/cache.py +1 -0
- {redis-6.3.0 → redis-7.0.0}/redis/client.py +99 -22
- {redis-6.3.0 → redis-7.0.0}/redis/cluster.py +14 -3
- {redis-6.3.0 → redis-7.0.0}/redis/commands/core.py +348 -313
- {redis-6.3.0 → redis-7.0.0}/redis/commands/helpers.py +0 -20
- redis-7.0.0/redis/commands/json/_util.py +5 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/json/commands.py +2 -2
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/__init__.py +2 -2
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/aggregation.py +28 -30
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/commands.py +13 -13
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/query.py +23 -23
- {redis-6.3.0 → redis-7.0.0}/redis/commands/vectorset/commands.py +50 -25
- {redis-6.3.0 → redis-7.0.0}/redis/commands/vectorset/utils.py +40 -4
- {redis-6.3.0 → redis-7.0.0}/redis/connection.py +1258 -90
- redis-7.0.0/redis/data_structure.py +81 -0
- {redis-6.3.0 → redis-7.0.0}/redis/event.py +88 -14
- {redis-6.3.0 → redis-7.0.0}/redis/exceptions.py +8 -0
- redis-7.0.0/redis/http/http_client.py +425 -0
- redis-7.0.0/redis/maint_notifications.py +810 -0
- redis-7.0.0/redis/multidb/circuit.py +144 -0
- redis-7.0.0/redis/multidb/client.py +526 -0
- redis-7.0.0/redis/multidb/command_executor.py +350 -0
- redis-7.0.0/redis/multidb/config.py +207 -0
- redis-7.0.0/redis/multidb/database.py +130 -0
- redis-7.0.0/redis/multidb/event.py +89 -0
- redis-7.0.0/redis/multidb/exception.py +17 -0
- redis-7.0.0/redis/multidb/failover.py +125 -0
- redis-7.0.0/redis/multidb/failure_detector.py +104 -0
- redis-7.0.0/redis/multidb/healthcheck.py +282 -0
- redis-7.0.0/redis/py.typed +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/retry.py +14 -1
- {redis-6.3.0 → redis-7.0.0}/redis/utils.py +34 -0
- redis-7.0.0/tests/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/conftest.py +16 -0
- redis-7.0.0/tests/test_asyncio/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_commands.py +60 -7
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_connection_pool.py +31 -5
- redis-7.0.0/tests/test_asyncio/test_multidb/__init__.py +0 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/conftest.py +131 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_client.py +628 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_command_executor.py +181 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_config.py +166 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_failover.py +169 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_failure_detector.py +128 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_healthcheck.py +401 -0
- redis-7.0.0/tests/test_asyncio/test_multidb/test_pipeline.py +488 -0
- redis-7.0.0/tests/test_asyncio/test_scenario/__init__.py +0 -0
- redis-7.0.0/tests/test_asyncio/test_scenario/conftest.py +116 -0
- redis-7.0.0/tests/test_asyncio/test_scenario/test_active_active.py +420 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_search.py +17 -0
- redis-7.0.0/tests/test_asyncio/test_ssl.py +143 -0
- redis-7.0.0/tests/test_asyncio/test_usage_counter.py +16 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_vsets.py +128 -1
- redis-7.0.0/tests/test_auth/__init__.py +0 -0
- redis-7.0.0/tests/test_background.py +94 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_cluster.py +16 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_cluster_transaction.py +2 -2
- {redis-6.3.0 → redis-7.0.0}/tests/test_commands.py +108 -13
- {redis-6.3.0 → redis-7.0.0}/tests/test_connection.py +3 -3
- {redis-6.3.0 → redis-7.0.0}/tests/test_connection_pool.py +108 -4
- {redis-6.3.0 → redis-7.0.0}/tests/test_credentials.py +11 -6
- redis-7.0.0/tests/test_data_structure.py +94 -0
- redis-7.0.0/tests/test_event.py +67 -0
- redis-7.0.0/tests/test_http/__init__.py +0 -0
- redis-7.0.0/tests/test_http/test_http_client.py +371 -0
- redis-7.0.0/tests/test_maint_notifications.py +896 -0
- redis-7.0.0/tests/test_maint_notifications_handling.py +2242 -0
- redis-7.0.0/tests/test_multidb/__init__.py +0 -0
- redis-7.0.0/tests/test_multidb/conftest.py +131 -0
- redis-7.0.0/tests/test_multidb/test_circuit.py +57 -0
- redis-7.0.0/tests/test_multidb/test_client.py +615 -0
- redis-7.0.0/tests/test_multidb/test_command_executor.py +173 -0
- redis-7.0.0/tests/test_multidb/test_config.py +161 -0
- redis-7.0.0/tests/test_multidb/test_failover.py +161 -0
- redis-7.0.0/tests/test_multidb/test_failure_detector.py +117 -0
- redis-7.0.0/tests/test_multidb/test_healthcheck.py +385 -0
- redis-7.0.0/tests/test_multidb/test_pipeline.py +489 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_multiprocessing.py +8 -0
- redis-7.0.0/tests/test_parsers/test_errors.py +167 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_parsers/test_helpers.py +21 -1
- {redis-6.3.0 → redis-7.0.0}/tests/test_pubsub.py +10 -2
- redis-7.0.0/tests/test_scenario/__init__.py +0 -0
- redis-7.0.0/tests/test_scenario/conftest.py +237 -0
- redis-7.0.0/tests/test_scenario/fault_injector_client.py +150 -0
- redis-7.0.0/tests/test_scenario/maint_notifications_helpers.py +341 -0
- redis-7.0.0/tests/test_scenario/test_active_active.py +460 -0
- redis-7.0.0/tests/test_scenario/test_maint_notifications.py +1096 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_search.py +64 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_ssl.py +97 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_vsets.py +126 -1
- redis-6.3.0/dev_requirements.txt +0 -16
- redis-6.3.0/redis/commands/json/_util.py +0 -3
- redis-6.3.0/tests/test_asyncio/test_ssl.py +0 -56
- {redis-6.3.0 → redis-7.0.0}/.gitignore +0 -0
- {redis-6.3.0 → redis-7.0.0}/LICENSE +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/commands.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/encoders.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/resp2.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/_parsers/socket.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/__init__.py +0 -0
- {redis-6.3.0/redis/auth → redis-7.0.0/redis/asyncio/http}/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/lock.py +0 -0
- {redis-6.3.0/tests → redis-7.0.0/redis/asyncio/multidb}/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/retry.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/sentinel.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/asyncio/utils.py +0 -0
- {redis-6.3.0/tests/test_asyncio → redis-7.0.0/redis/auth}/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/auth/err.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/auth/idp.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/auth/token.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/auth/token_manager.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/backoff.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/bf/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/bf/commands.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/bf/info.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/cluster.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/json/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/json/decoders.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/json/path.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/redismodules.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/_util.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/dialect.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/document.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/field.py +2 -2
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/index_definition.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/profile_information.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/querystring.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/reducers.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/result.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/search/suggestion.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/sentinel.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/timeseries/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/timeseries/commands.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/timeseries/info.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/timeseries/utils.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/commands/vectorset/__init__.py +1 -1
- {redis-6.3.0 → redis-7.0.0}/redis/crc.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/credentials.py +0 -0
- {redis-6.3.0/tests/test_auth → redis-7.0.0/redis/http}/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/lock.py +0 -0
- /redis-6.3.0/redis/py.typed → /redis-7.0.0/redis/multidb/__init__.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/ocsp.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/sentinel.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/redis/typing.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/entraid_utils.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/mocks.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/ssl_utils.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/compat.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/conftest.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/mocks.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_bloom.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_cluster.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_cluster_transaction.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_connect.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_connection.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_credentials.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_cwe_404.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_encoding.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_hash.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_json.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_lock.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_monitor.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_pipeline.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_pubsub.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_retry.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_scripting.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_sentinel.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_timeseries.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/test_utils.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/testdata/titles.csv +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_auth/test_token.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_auth/test_token_manager.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_backoff.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_bloom.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_cache.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_command_parser.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_connect.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_encoding.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_function.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_hash.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_helpers.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_json.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_lock.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_max_connections_error.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_monitor.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_pipeline.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_retry.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_scripting.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_sentinel.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_sentinel_managed_connection.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_timeseries.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/test_utils.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/testdata/jsontestdata.py +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/testdata/titles.csv +0 -0
- {redis-6.3.0 → redis-7.0.0}/tests/testdata/will_play_text.csv.bz2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: redis
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0
|
|
4
4
|
Summary: Python client for Redis database and key-value store
|
|
5
5
|
Project-URL: Changes, https://github.com/redis/redis-py/releases
|
|
6
6
|
Project-URL: Code, https://github.com/redis/redis-py
|
|
@@ -28,6 +28,8 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
28
28
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
29
29
|
Requires-Python: >=3.9
|
|
30
30
|
Requires-Dist: async-timeout>=4.0.3; python_full_version < '3.11.3'
|
|
31
|
+
Provides-Extra: circuit-breaker
|
|
32
|
+
Requires-Dist: pybreaker>=1.4.0; extra == 'circuit-breaker'
|
|
31
33
|
Provides-Extra: hiredis
|
|
32
34
|
Requires-Dist: hiredis>=3.2.0; extra == 'hiredis'
|
|
33
35
|
Provides-Extra: jwt
|
|
@@ -53,8 +55,9 @@ The Python interface to the Redis key-value store.
|
|
|
53
55
|
|
|
54
56
|
---------------------------------------------
|
|
55
57
|
|
|
56
|
-
**Note:** redis-py 5.0
|
|
57
|
-
**Note:** redis-py 6.1.0
|
|
58
|
+
**Note:** redis-py 5.0 is the last version of redis-py that supports Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 supports Python 3.8+.<br>
|
|
59
|
+
**Note:** redis-py 6.1.0 is the last version of redis-py that supports Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 supports Python 3.9+.
|
|
60
|
+
|
|
58
61
|
---------------------------------------------
|
|
59
62
|
|
|
60
63
|
## How do I Redis?
|
|
@@ -99,7 +102,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
|
|
|
99
102
|
|
|
100
103
|
## Supported Redis Versions
|
|
101
104
|
|
|
102
|
-
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES)
|
|
105
|
+
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) and [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES).
|
|
103
106
|
|
|
104
107
|
The table below highlights version compatibility of the most-recent library versions and redis versions.
|
|
105
108
|
|
|
@@ -13,8 +13,9 @@ The Python interface to the Redis key-value store.
|
|
|
13
13
|
|
|
14
14
|
---------------------------------------------
|
|
15
15
|
|
|
16
|
-
**Note:** redis-py 5.0
|
|
17
|
-
**Note:** redis-py 6.1.0
|
|
16
|
+
**Note:** redis-py 5.0 is the last version of redis-py that supports Python 3.7, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 5.1 supports Python 3.8+.<br>
|
|
17
|
+
**Note:** redis-py 6.1.0 is the last version of redis-py that supports Python 3.8, as it has reached [end of life](https://devguide.python.org/versions/). redis-py 6.2.0 supports Python 3.9+.
|
|
18
|
+
|
|
18
19
|
---------------------------------------------
|
|
19
20
|
|
|
20
21
|
## How do I Redis?
|
|
@@ -59,7 +60,7 @@ Looking for a high-level library to handle object mapping? See [redis-om-python]
|
|
|
59
60
|
|
|
60
61
|
## Supported Redis Versions
|
|
61
62
|
|
|
62
|
-
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES)
|
|
63
|
+
The most recent version of this library supports Redis version [7.2](https://github.com/redis/redis/blob/7.2/00-RELEASENOTES), [7.4](https://github.com/redis/redis/blob/7.4/00-RELEASENOTES), [8.0](https://github.com/redis/redis/blob/8.0/00-RELEASENOTES) and [8.2](https://github.com/redis/redis/blob/8.2/00-RELEASENOTES).
|
|
63
64
|
|
|
64
65
|
The table below highlights version compatibility of the most-recent library versions and redis versions.
|
|
65
66
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
build
|
|
2
|
+
build==1.2.2.post1 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
3
|
+
click==8.0.4
|
|
4
|
+
invoke==2.2.0
|
|
5
|
+
mock
|
|
6
|
+
mock==5.1.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
7
|
+
packaging>=20.4
|
|
8
|
+
packaging==24.2 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
9
|
+
|
|
10
|
+
pytest
|
|
11
|
+
pytest==8.3.4 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
12
|
+
pytest-asyncio>=0.23.0
|
|
13
|
+
pytest-asyncio==1.1.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
14
|
+
pytest-cov
|
|
15
|
+
pytest-cov==6.0.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
16
|
+
coverage==7.6.12 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
17
|
+
pytest-profiling==1.8.1
|
|
18
|
+
pytest-timeout
|
|
19
|
+
pytest-timeout==2.3.1 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
20
|
+
|
|
21
|
+
ruff==0.9.6
|
|
22
|
+
ujson>=4.2.0
|
|
23
|
+
uvloop<=0.21.0; platform_python_implementation == "CPython"
|
|
24
|
+
vulture>=2.3.0
|
|
25
|
+
|
|
26
|
+
numpy>=1.24.0 ; platform_python_implementation == "CPython"
|
|
27
|
+
numpy>=1.24.0,<2.0 ; platform_python_implementation == "PyPy" or python_version < "3.10"
|
|
28
|
+
|
|
29
|
+
redis-entraid==1.0.0
|
|
30
|
+
pybreaker>=1.4.0
|
|
@@ -42,6 +42,9 @@ ocsp = [
|
|
|
42
42
|
jwt = [
|
|
43
43
|
"PyJWT>=2.9.0",
|
|
44
44
|
]
|
|
45
|
+
circuit_breaker = [
|
|
46
|
+
"pybreaker>=1.4.0"
|
|
47
|
+
]
|
|
45
48
|
|
|
46
49
|
[project.urls]
|
|
47
50
|
Changes = "https://github.com/redis/redis-py/releases"
|
|
@@ -80,6 +83,12 @@ filterwarnings = [
|
|
|
80
83
|
# Ignore a coverage warning when COVERAGE_CORE=sysmon for Pythons < 3.12.
|
|
81
84
|
"ignore:sys.monitoring isn't available:coverage.exceptions.CoverageWarning",
|
|
82
85
|
]
|
|
86
|
+
log_cli_level = "INFO"
|
|
87
|
+
log_cli_date_format = "%H:%M:%S:%f"
|
|
88
|
+
log_cli = false
|
|
89
|
+
log_cli_format = "%(asctime)s %(levelname)s %(threadName)s: %(message)s"
|
|
90
|
+
log_level = "INFO"
|
|
91
|
+
capture = "yes"
|
|
83
92
|
|
|
84
93
|
[tool.ruff]
|
|
85
94
|
target-version = "py39"
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import sys
|
|
2
3
|
from abc import ABC
|
|
3
4
|
from asyncio import IncompleteReadError, StreamReader, TimeoutError
|
|
4
|
-
from typing import Callable, List, Optional, Protocol, Union
|
|
5
|
+
from typing import Awaitable, Callable, List, Optional, Protocol, Union
|
|
6
|
+
|
|
7
|
+
from redis.maint_notifications import (
|
|
8
|
+
MaintenanceNotification,
|
|
9
|
+
NodeFailedOverNotification,
|
|
10
|
+
NodeFailingOverNotification,
|
|
11
|
+
NodeMigratedNotification,
|
|
12
|
+
NodeMigratingNotification,
|
|
13
|
+
NodeMovingNotification,
|
|
14
|
+
)
|
|
5
15
|
|
|
6
16
|
if sys.version_info.major >= 3 and sys.version_info.minor >= 11:
|
|
7
17
|
from asyncio import timeout as async_timeout
|
|
@@ -17,6 +27,7 @@ from ..exceptions import (
|
|
|
17
27
|
ClusterDownError,
|
|
18
28
|
ConnectionError,
|
|
19
29
|
ExecAbortError,
|
|
30
|
+
ExternalAuthProviderError,
|
|
20
31
|
MasterDownError,
|
|
21
32
|
ModuleError,
|
|
22
33
|
MovedError,
|
|
@@ -50,6 +61,12 @@ NO_AUTH_SET_ERROR = {
|
|
|
50
61
|
"Client sent AUTH, but no password is set": AuthenticationError,
|
|
51
62
|
}
|
|
52
63
|
|
|
64
|
+
EXTERNAL_AUTH_PROVIDER_ERROR = {
|
|
65
|
+
"problem with LDAP service": ExternalAuthProviderError,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
logger = logging.getLogger(__name__)
|
|
69
|
+
|
|
53
70
|
|
|
54
71
|
class BaseParser(ABC):
|
|
55
72
|
EXCEPTION_CLASSES = {
|
|
@@ -69,6 +86,7 @@ class BaseParser(ABC):
|
|
|
69
86
|
NO_SUCH_MODULE_ERROR: ModuleError,
|
|
70
87
|
MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError,
|
|
71
88
|
**NO_AUTH_SET_ERROR,
|
|
89
|
+
**EXTERNAL_AUTH_PROVIDER_ERROR,
|
|
72
90
|
},
|
|
73
91
|
"OOM": OutOfMemoryError,
|
|
74
92
|
"WRONGPASS": AuthenticationError,
|
|
@@ -158,7 +176,77 @@ class AsyncBaseParser(BaseParser):
|
|
|
158
176
|
raise NotImplementedError()
|
|
159
177
|
|
|
160
178
|
|
|
161
|
-
|
|
179
|
+
class MaintenanceNotificationsParser:
|
|
180
|
+
"""Protocol defining maintenance push notification parsing functionality"""
|
|
181
|
+
|
|
182
|
+
@staticmethod
|
|
183
|
+
def parse_maintenance_start_msg(response, notification_type):
|
|
184
|
+
# Expected message format is: <notification_type> <seq_number> <time>
|
|
185
|
+
id = response[1]
|
|
186
|
+
ttl = response[2]
|
|
187
|
+
return notification_type(id, ttl)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def parse_maintenance_completed_msg(response, notification_type):
|
|
191
|
+
# Expected message format is: <notification_type> <seq_number>
|
|
192
|
+
id = response[1]
|
|
193
|
+
return notification_type(id)
|
|
194
|
+
|
|
195
|
+
@staticmethod
|
|
196
|
+
def parse_moving_msg(response):
|
|
197
|
+
# Expected message format is: MOVING <seq_number> <time> <endpoint>
|
|
198
|
+
id = response[1]
|
|
199
|
+
ttl = response[2]
|
|
200
|
+
if response[3] is None:
|
|
201
|
+
host, port = None, None
|
|
202
|
+
else:
|
|
203
|
+
value = response[3]
|
|
204
|
+
if isinstance(value, bytes):
|
|
205
|
+
value = value.decode()
|
|
206
|
+
host, port = value.split(":")
|
|
207
|
+
port = int(port) if port is not None else None
|
|
208
|
+
|
|
209
|
+
return NodeMovingNotification(id, host, port, ttl)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
_INVALIDATION_MESSAGE = "invalidate"
|
|
213
|
+
_MOVING_MESSAGE = "MOVING"
|
|
214
|
+
_MIGRATING_MESSAGE = "MIGRATING"
|
|
215
|
+
_MIGRATED_MESSAGE = "MIGRATED"
|
|
216
|
+
_FAILING_OVER_MESSAGE = "FAILING_OVER"
|
|
217
|
+
_FAILED_OVER_MESSAGE = "FAILED_OVER"
|
|
218
|
+
|
|
219
|
+
_MAINTENANCE_MESSAGES = (
|
|
220
|
+
_MIGRATING_MESSAGE,
|
|
221
|
+
_MIGRATED_MESSAGE,
|
|
222
|
+
_FAILING_OVER_MESSAGE,
|
|
223
|
+
_FAILED_OVER_MESSAGE,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING: dict[
|
|
227
|
+
str, tuple[type[MaintenanceNotification], Callable]
|
|
228
|
+
] = {
|
|
229
|
+
_MIGRATING_MESSAGE: (
|
|
230
|
+
NodeMigratingNotification,
|
|
231
|
+
MaintenanceNotificationsParser.parse_maintenance_start_msg,
|
|
232
|
+
),
|
|
233
|
+
_MIGRATED_MESSAGE: (
|
|
234
|
+
NodeMigratedNotification,
|
|
235
|
+
MaintenanceNotificationsParser.parse_maintenance_completed_msg,
|
|
236
|
+
),
|
|
237
|
+
_FAILING_OVER_MESSAGE: (
|
|
238
|
+
NodeFailingOverNotification,
|
|
239
|
+
MaintenanceNotificationsParser.parse_maintenance_start_msg,
|
|
240
|
+
),
|
|
241
|
+
_FAILED_OVER_MESSAGE: (
|
|
242
|
+
NodeFailedOverNotification,
|
|
243
|
+
MaintenanceNotificationsParser.parse_maintenance_completed_msg,
|
|
244
|
+
),
|
|
245
|
+
_MOVING_MESSAGE: (
|
|
246
|
+
NodeMovingNotification,
|
|
247
|
+
MaintenanceNotificationsParser.parse_moving_msg,
|
|
248
|
+
),
|
|
249
|
+
}
|
|
162
250
|
|
|
163
251
|
|
|
164
252
|
class PushNotificationsParser(Protocol):
|
|
@@ -166,16 +254,57 @@ class PushNotificationsParser(Protocol):
|
|
|
166
254
|
|
|
167
255
|
pubsub_push_handler_func: Callable
|
|
168
256
|
invalidation_push_handler_func: Optional[Callable] = None
|
|
257
|
+
node_moving_push_handler_func: Optional[Callable] = None
|
|
258
|
+
maintenance_push_handler_func: Optional[Callable] = None
|
|
169
259
|
|
|
170
260
|
def handle_pubsub_push_response(self, response):
|
|
171
261
|
"""Handle pubsub push responses"""
|
|
172
262
|
raise NotImplementedError()
|
|
173
263
|
|
|
174
264
|
def handle_push_response(self, response, **kwargs):
|
|
175
|
-
|
|
265
|
+
msg_type = response[0]
|
|
266
|
+
if isinstance(msg_type, bytes):
|
|
267
|
+
msg_type = msg_type.decode()
|
|
268
|
+
|
|
269
|
+
if msg_type not in (
|
|
270
|
+
_INVALIDATION_MESSAGE,
|
|
271
|
+
*_MAINTENANCE_MESSAGES,
|
|
272
|
+
_MOVING_MESSAGE,
|
|
273
|
+
):
|
|
176
274
|
return self.pubsub_push_handler_func(response)
|
|
177
|
-
|
|
178
|
-
|
|
275
|
+
|
|
276
|
+
try:
|
|
277
|
+
if (
|
|
278
|
+
msg_type == _INVALIDATION_MESSAGE
|
|
279
|
+
and self.invalidation_push_handler_func
|
|
280
|
+
):
|
|
281
|
+
return self.invalidation_push_handler_func(response)
|
|
282
|
+
|
|
283
|
+
if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
|
|
284
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
285
|
+
msg_type
|
|
286
|
+
][1]
|
|
287
|
+
|
|
288
|
+
notification = parser_function(response)
|
|
289
|
+
return self.node_moving_push_handler_func(notification)
|
|
290
|
+
|
|
291
|
+
if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
|
|
292
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
293
|
+
msg_type
|
|
294
|
+
][1]
|
|
295
|
+
notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
296
|
+
msg_type
|
|
297
|
+
][0]
|
|
298
|
+
notification = parser_function(response, notification_type)
|
|
299
|
+
|
|
300
|
+
if notification is not None:
|
|
301
|
+
return self.maintenance_push_handler_func(notification)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
logger.error(
|
|
304
|
+
"Error handling {} message ({}): {}".format(msg_type, response, e)
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
return None
|
|
179
308
|
|
|
180
309
|
def set_pubsub_push_handler(self, pubsub_push_handler_func):
|
|
181
310
|
self.pubsub_push_handler_func = pubsub_push_handler_func
|
|
@@ -183,12 +312,20 @@ class PushNotificationsParser(Protocol):
|
|
|
183
312
|
def set_invalidation_push_handler(self, invalidation_push_handler_func):
|
|
184
313
|
self.invalidation_push_handler_func = invalidation_push_handler_func
|
|
185
314
|
|
|
315
|
+
def set_node_moving_push_handler(self, node_moving_push_handler_func):
|
|
316
|
+
self.node_moving_push_handler_func = node_moving_push_handler_func
|
|
317
|
+
|
|
318
|
+
def set_maintenance_push_handler(self, maintenance_push_handler_func):
|
|
319
|
+
self.maintenance_push_handler_func = maintenance_push_handler_func
|
|
320
|
+
|
|
186
321
|
|
|
187
322
|
class AsyncPushNotificationsParser(Protocol):
|
|
188
323
|
"""Protocol defining async RESP3-specific parsing functionality"""
|
|
189
324
|
|
|
190
325
|
pubsub_push_handler_func: Callable
|
|
191
326
|
invalidation_push_handler_func: Optional[Callable] = None
|
|
327
|
+
node_moving_push_handler_func: Optional[Callable[..., Awaitable[None]]] = None
|
|
328
|
+
maintenance_push_handler_func: Optional[Callable[..., Awaitable[None]]] = None
|
|
192
329
|
|
|
193
330
|
async def handle_pubsub_push_response(self, response):
|
|
194
331
|
"""Handle pubsub push responses asynchronously"""
|
|
@@ -196,10 +333,52 @@ class AsyncPushNotificationsParser(Protocol):
|
|
|
196
333
|
|
|
197
334
|
async def handle_push_response(self, response, **kwargs):
|
|
198
335
|
"""Handle push responses asynchronously"""
|
|
199
|
-
|
|
336
|
+
|
|
337
|
+
msg_type = response[0]
|
|
338
|
+
if isinstance(msg_type, bytes):
|
|
339
|
+
msg_type = msg_type.decode()
|
|
340
|
+
|
|
341
|
+
if msg_type not in (
|
|
342
|
+
_INVALIDATION_MESSAGE,
|
|
343
|
+
*_MAINTENANCE_MESSAGES,
|
|
344
|
+
_MOVING_MESSAGE,
|
|
345
|
+
):
|
|
200
346
|
return await self.pubsub_push_handler_func(response)
|
|
201
|
-
|
|
202
|
-
|
|
347
|
+
|
|
348
|
+
try:
|
|
349
|
+
if (
|
|
350
|
+
msg_type == _INVALIDATION_MESSAGE
|
|
351
|
+
and self.invalidation_push_handler_func
|
|
352
|
+
):
|
|
353
|
+
return await self.invalidation_push_handler_func(response)
|
|
354
|
+
|
|
355
|
+
if isinstance(msg_type, bytes):
|
|
356
|
+
msg_type = msg_type.decode()
|
|
357
|
+
|
|
358
|
+
if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
|
|
359
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
360
|
+
msg_type
|
|
361
|
+
][1]
|
|
362
|
+
notification = parser_function(response)
|
|
363
|
+
return await self.node_moving_push_handler_func(notification)
|
|
364
|
+
|
|
365
|
+
if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
|
|
366
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
367
|
+
msg_type
|
|
368
|
+
][1]
|
|
369
|
+
notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
370
|
+
msg_type
|
|
371
|
+
][0]
|
|
372
|
+
notification = parser_function(response, notification_type)
|
|
373
|
+
|
|
374
|
+
if notification is not None:
|
|
375
|
+
return await self.maintenance_push_handler_func(notification)
|
|
376
|
+
except Exception as e:
|
|
377
|
+
logger.error(
|
|
378
|
+
"Error handling {} message ({}): {}".format(msg_type, response, e)
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
return None
|
|
203
382
|
|
|
204
383
|
def set_pubsub_push_handler(self, pubsub_push_handler_func):
|
|
205
384
|
"""Set the pubsub push handler function"""
|
|
@@ -209,6 +388,12 @@ class AsyncPushNotificationsParser(Protocol):
|
|
|
209
388
|
"""Set the invalidation push handler function"""
|
|
210
389
|
self.invalidation_push_handler_func = invalidation_push_handler_func
|
|
211
390
|
|
|
391
|
+
def set_node_moving_push_handler(self, node_moving_push_handler_func):
|
|
392
|
+
self.node_moving_push_handler_func = node_moving_push_handler_func
|
|
393
|
+
|
|
394
|
+
def set_maintenance_push_handler(self, maintenance_push_handler_func):
|
|
395
|
+
self.maintenance_push_handler_func = maintenance_push_handler_func
|
|
396
|
+
|
|
212
397
|
|
|
213
398
|
class _AsyncRESPBase(AsyncBaseParser):
|
|
214
399
|
"""Base class for async resp parsing"""
|
|
@@ -224,6 +224,39 @@ def zset_score_pairs(response, **options):
|
|
|
224
224
|
return list(zip(it, map(score_cast_func, it)))
|
|
225
225
|
|
|
226
226
|
|
|
227
|
+
def zset_score_for_rank(response, **options):
|
|
228
|
+
"""
|
|
229
|
+
If ``withscores`` is specified in the options, return the response as
|
|
230
|
+
a [value, score] pair
|
|
231
|
+
"""
|
|
232
|
+
if not response or not options.get("withscore"):
|
|
233
|
+
return response
|
|
234
|
+
score_cast_func = options.get("score_cast_func", float)
|
|
235
|
+
return [response[0], score_cast_func(response[1])]
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def zset_score_pairs_resp3(response, **options):
|
|
239
|
+
"""
|
|
240
|
+
If ``withscores`` is specified in the options, return the response as
|
|
241
|
+
a list of [value, score] pairs
|
|
242
|
+
"""
|
|
243
|
+
if not response or not options.get("withscores"):
|
|
244
|
+
return response
|
|
245
|
+
score_cast_func = options.get("score_cast_func", float)
|
|
246
|
+
return [[name, score_cast_func(val)] for name, val in response]
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def zset_score_for_rank_resp3(response, **options):
|
|
250
|
+
"""
|
|
251
|
+
If ``withscores`` is specified in the options, return the response as
|
|
252
|
+
a [value, score] pair
|
|
253
|
+
"""
|
|
254
|
+
if not response or not options.get("withscore"):
|
|
255
|
+
return response
|
|
256
|
+
score_cast_func = options.get("score_cast_func", float)
|
|
257
|
+
return [response[0], score_cast_func(response[1])]
|
|
258
|
+
|
|
259
|
+
|
|
227
260
|
def sort_return_tuples(response, **options):
|
|
228
261
|
"""
|
|
229
262
|
If ``groups`` is specified, return the response as a list of
|
|
@@ -349,8 +382,22 @@ def parse_zadd(response, **options):
|
|
|
349
382
|
def parse_client_list(response, **options):
|
|
350
383
|
clients = []
|
|
351
384
|
for c in str_if_bytes(response).splitlines():
|
|
352
|
-
|
|
353
|
-
|
|
385
|
+
client_dict = {}
|
|
386
|
+
tokens = c.split(" ")
|
|
387
|
+
last_key = None
|
|
388
|
+
for token in tokens:
|
|
389
|
+
if "=" in token:
|
|
390
|
+
# Values might contain '='
|
|
391
|
+
key, value = token.split("=", 1)
|
|
392
|
+
client_dict[key] = value
|
|
393
|
+
last_key = key
|
|
394
|
+
else:
|
|
395
|
+
# Values may include spaces. For instance, when running Redis via a Unix socket — such as
|
|
396
|
+
# "/tmp/redis sock/redis.sock" — the addr or laddr field will include a space.
|
|
397
|
+
client_dict[last_key] += " " + token
|
|
398
|
+
|
|
399
|
+
if client_dict:
|
|
400
|
+
clients.append(client_dict)
|
|
354
401
|
return clients
|
|
355
402
|
|
|
356
403
|
|
|
@@ -797,10 +844,14 @@ _RedisCallbacksRESP2 = {
|
|
|
797
844
|
"SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
|
|
798
845
|
),
|
|
799
846
|
**string_keys_to_dict(
|
|
800
|
-
"ZDIFF ZINTER ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE
|
|
801
|
-
"ZREVRANGEBYSCORE
|
|
847
|
+
"ZDIFF ZINTER ZPOPMAX ZPOPMIN ZRANGE ZRANGEBYSCORE ZREVRANGE "
|
|
848
|
+
"ZREVRANGEBYSCORE ZUNION",
|
|
802
849
|
zset_score_pairs,
|
|
803
850
|
),
|
|
851
|
+
**string_keys_to_dict(
|
|
852
|
+
"ZREVRANK ZRANK",
|
|
853
|
+
zset_score_for_rank,
|
|
854
|
+
),
|
|
804
855
|
**string_keys_to_dict("ZINCRBY ZSCORE", float_or_none),
|
|
805
856
|
**string_keys_to_dict("BGREWRITEAOF BGSAVE", lambda r: True),
|
|
806
857
|
**string_keys_to_dict("BLPOP BRPOP", lambda r: r and tuple(r) or None),
|
|
@@ -844,10 +895,17 @@ _RedisCallbacksRESP3 = {
|
|
|
844
895
|
"SDIFF SINTER SMEMBERS SUNION", lambda r: r and set(r) or set()
|
|
845
896
|
),
|
|
846
897
|
**string_keys_to_dict(
|
|
847
|
-
"ZRANGE ZINTER ZPOPMAX ZPOPMIN
|
|
848
|
-
"ZUNION HGETALL XREADGROUP",
|
|
898
|
+
"ZRANGE ZINTER ZPOPMAX ZPOPMIN HGETALL XREADGROUP",
|
|
849
899
|
lambda r, **kwargs: r,
|
|
850
900
|
),
|
|
901
|
+
**string_keys_to_dict(
|
|
902
|
+
"ZRANGE ZRANGEBYSCORE ZREVRANGE ZREVRANGEBYSCORE ZUNION",
|
|
903
|
+
zset_score_pairs_resp3,
|
|
904
|
+
),
|
|
905
|
+
**string_keys_to_dict(
|
|
906
|
+
"ZREVRANK ZRANK",
|
|
907
|
+
zset_score_for_rank_resp3,
|
|
908
|
+
),
|
|
851
909
|
**string_keys_to_dict("XREAD XREADGROUP", parse_xread_resp3),
|
|
852
910
|
"ACL LOG": lambda r: (
|
|
853
911
|
[
|
|
@@ -47,6 +47,8 @@ class _HiredisParser(BaseParser, PushNotificationsParser):
|
|
|
47
47
|
self.socket_read_size = socket_read_size
|
|
48
48
|
self._buffer = bytearray(socket_read_size)
|
|
49
49
|
self.pubsub_push_handler_func = self.handle_pubsub_push_response
|
|
50
|
+
self.node_moving_push_handler_func = None
|
|
51
|
+
self.maintenance_push_handler_func = None
|
|
50
52
|
self.invalidation_push_handler_func = None
|
|
51
53
|
self._hiredis_PushNotificationType = None
|
|
52
54
|
|
|
@@ -141,12 +143,15 @@ class _HiredisParser(BaseParser, PushNotificationsParser):
|
|
|
141
143
|
response, self._hiredis_PushNotificationType
|
|
142
144
|
):
|
|
143
145
|
response = self.handle_push_response(response)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
148
|
-
else:
|
|
146
|
+
|
|
147
|
+
# if this is a push request return the push response
|
|
148
|
+
if push_request:
|
|
149
149
|
return response
|
|
150
|
+
|
|
151
|
+
return self.read_response(
|
|
152
|
+
disable_decoding=disable_decoding,
|
|
153
|
+
push_request=push_request,
|
|
154
|
+
)
|
|
150
155
|
return response
|
|
151
156
|
|
|
152
157
|
if disable_decoding:
|
|
@@ -169,12 +174,13 @@ class _HiredisParser(BaseParser, PushNotificationsParser):
|
|
|
169
174
|
response, self._hiredis_PushNotificationType
|
|
170
175
|
):
|
|
171
176
|
response = self.handle_push_response(response)
|
|
172
|
-
if
|
|
173
|
-
return self.read_response(
|
|
174
|
-
disable_decoding=disable_decoding, push_request=push_request
|
|
175
|
-
)
|
|
176
|
-
else:
|
|
177
|
+
if push_request:
|
|
177
178
|
return response
|
|
179
|
+
return self.read_response(
|
|
180
|
+
disable_decoding=disable_decoding,
|
|
181
|
+
push_request=push_request,
|
|
182
|
+
)
|
|
183
|
+
|
|
178
184
|
elif (
|
|
179
185
|
isinstance(response, list)
|
|
180
186
|
and response
|
|
@@ -18,6 +18,8 @@ class _RESP3Parser(_RESPBase, PushNotificationsParser):
|
|
|
18
18
|
def __init__(self, socket_read_size):
|
|
19
19
|
super().__init__(socket_read_size)
|
|
20
20
|
self.pubsub_push_handler_func = self.handle_pubsub_push_response
|
|
21
|
+
self.node_moving_push_handler_func = None
|
|
22
|
+
self.maintenance_push_handler_func = None
|
|
21
23
|
self.invalidation_push_handler_func = None
|
|
22
24
|
|
|
23
25
|
def handle_pubsub_push_response(self, response):
|
|
@@ -117,17 +119,21 @@ class _RESP3Parser(_RESPBase, PushNotificationsParser):
|
|
|
117
119
|
for _ in range(int(response))
|
|
118
120
|
]
|
|
119
121
|
response = self.handle_push_response(response)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
)
|
|
124
|
-
else:
|
|
122
|
+
|
|
123
|
+
# if this is a push request return the push response
|
|
124
|
+
if push_request:
|
|
125
125
|
return response
|
|
126
|
+
|
|
127
|
+
return self._read_response(
|
|
128
|
+
disable_decoding=disable_decoding,
|
|
129
|
+
push_request=push_request,
|
|
130
|
+
)
|
|
126
131
|
else:
|
|
127
132
|
raise InvalidResponse(f"Protocol Error: {raw!r}")
|
|
128
133
|
|
|
129
134
|
if isinstance(response, bytes) and disable_decoding is False:
|
|
130
135
|
response = self.encoder.decode(response)
|
|
136
|
+
|
|
131
137
|
return response
|
|
132
138
|
|
|
133
139
|
|