redis 7.0.0b1__tar.gz → 7.0.0b2__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-7.0.0b1 → redis-7.0.0b2}/PKG-INFO +1 -1
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/__init__.py +1 -1
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/base.py +36 -22
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/client.py +6 -1
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/cluster.py +6 -1
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/connection.py +43 -1
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/client.py +19 -15
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/cluster.py +2 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/connection.py +206 -151
- redis-7.0.0b1/redis/maintenance_events.py → redis-7.0.0b2/redis/maint_notifications.py +154 -140
- redis-7.0.0b2/tests/test_asyncio/test_ssl.py +143 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_connection_pool.py +62 -2
- redis-7.0.0b2/tests/test_maint_notifications.py +893 -0
- redis-7.0.0b1/tests/test_maintenance_events_handling.py → redis-7.0.0b2/tests/test_maint_notifications_handling.py +376 -325
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_scenario/conftest.py +27 -22
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_scenario/fault_injector_client.py +3 -2
- redis-7.0.0b1/tests/test_scenario/hitless_upgrade_helpers.py → redis-7.0.0b2/tests/test_scenario/maint_notifications_helpers.py +39 -0
- redis-7.0.0b1/tests/test_scenario/test_hitless_upgrade.py → redis-7.0.0b2/tests/test_scenario/test_maint_notifications.py +389 -73
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_ssl.py +97 -0
- redis-7.0.0b1/tests/test_asyncio/test_ssl.py +0 -56
- redis-7.0.0b1/tests/test_maintenance_events.py +0 -869
- {redis-7.0.0b1 → redis-7.0.0b2}/.gitignore +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/LICENSE +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/README.md +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/dev_requirements.txt +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/pyproject.toml +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/encoders.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/helpers.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/hiredis.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/resp2.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/resp3.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/_parsers/socket.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/lock.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/retry.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/sentinel.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/asyncio/utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/auth/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/auth/err.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/auth/idp.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/auth/token.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/auth/token_manager.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/backoff.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/cache.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/bf/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/bf/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/bf/info.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/cluster.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/core.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/helpers.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/json/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/json/_util.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/json/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/json/decoders.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/json/path.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/redismodules.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/_util.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/aggregation.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/dialect.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/document.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/field.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/index_definition.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/profile_information.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/query.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/querystring.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/reducers.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/result.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/search/suggestion.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/sentinel.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/timeseries/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/timeseries/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/timeseries/info.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/timeseries/utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/vectorset/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/vectorset/commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/commands/vectorset/utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/crc.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/credentials.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/event.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/exceptions.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/lock.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/ocsp.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/py.typed +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/retry.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/sentinel.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/typing.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/redis/utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/conftest.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/entraid_utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/mocks.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/ssl_utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/compat.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/conftest.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/mocks.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_bloom.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_cluster.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_cluster_transaction.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_connect.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_connection.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_connection_pool.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_credentials.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_cwe_404.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_encoding.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_hash.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_json.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_lock.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_monitor.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_pipeline.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_pubsub.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_retry.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_scripting.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_search.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_sentinel.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_sentinel_managed_connection.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_timeseries.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_usage_counter.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/test_vsets.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/testdata/jsontestdata.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/testdata/titles.csv +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_asyncio/testdata/will_play_text.csv.bz2 +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_auth/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_auth/test_token.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_auth/test_token_manager.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_backoff.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_bloom.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_cache.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_cluster.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_cluster_transaction.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_command_parser.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_commands.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_connect.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_connection.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_credentials.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_encoding.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_function.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_hash.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_helpers.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_json.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_lock.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_max_connections_error.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_monitor.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_multiprocessing.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_parsers/test_helpers.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_pipeline.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_pubsub.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_retry.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_scenario/__init__.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_scripting.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_search.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_sentinel.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_sentinel_managed_connection.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_timeseries.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_utils.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/test_vsets.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/testdata/jsontestdata.py +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/testdata/titles.csv +0 -0
- {redis-7.0.0b1 → redis-7.0.0b2}/tests/testdata/will_play_text.csv.bz2 +0 -0
|
@@ -4,13 +4,13 @@ from abc import ABC
|
|
|
4
4
|
from asyncio import IncompleteReadError, StreamReader, TimeoutError
|
|
5
5
|
from typing import Awaitable, Callable, List, Optional, Protocol, Union
|
|
6
6
|
|
|
7
|
-
from redis.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
from redis.maint_notifications import (
|
|
8
|
+
MaintenanceNotification,
|
|
9
|
+
NodeFailedOverNotification,
|
|
10
|
+
NodeFailingOverNotification,
|
|
11
|
+
NodeMigratedNotification,
|
|
12
|
+
NodeMigratingNotification,
|
|
13
|
+
NodeMovingNotification,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
if sys.version_info.major >= 3 and sys.version_info.minor >= 11:
|
|
@@ -175,14 +175,14 @@ class MaintenanceNotificationsParser:
|
|
|
175
175
|
|
|
176
176
|
@staticmethod
|
|
177
177
|
def parse_maintenance_start_msg(response, notification_type):
|
|
178
|
-
# Expected message format is: <
|
|
178
|
+
# Expected message format is: <notification_type> <seq_number> <time>
|
|
179
179
|
id = response[1]
|
|
180
180
|
ttl = response[2]
|
|
181
181
|
return notification_type(id, ttl)
|
|
182
182
|
|
|
183
183
|
@staticmethod
|
|
184
184
|
def parse_maintenance_completed_msg(response, notification_type):
|
|
185
|
-
# Expected message format is: <
|
|
185
|
+
# Expected message format is: <notification_type> <seq_number>
|
|
186
186
|
id = response[1]
|
|
187
187
|
return notification_type(id)
|
|
188
188
|
|
|
@@ -200,7 +200,7 @@ class MaintenanceNotificationsParser:
|
|
|
200
200
|
host, port = value.split(":")
|
|
201
201
|
port = int(port) if port is not None else None
|
|
202
202
|
|
|
203
|
-
return
|
|
203
|
+
return NodeMovingNotification(id, host, port, ttl)
|
|
204
204
|
|
|
205
205
|
|
|
206
206
|
_INVALIDATION_MESSAGE = "invalidate"
|
|
@@ -217,25 +217,27 @@ _MAINTENANCE_MESSAGES = (
|
|
|
217
217
|
_FAILED_OVER_MESSAGE,
|
|
218
218
|
)
|
|
219
219
|
|
|
220
|
-
|
|
220
|
+
MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING: dict[
|
|
221
|
+
str, tuple[type[MaintenanceNotification], Callable]
|
|
222
|
+
] = {
|
|
221
223
|
_MIGRATING_MESSAGE: (
|
|
222
|
-
|
|
224
|
+
NodeMigratingNotification,
|
|
223
225
|
MaintenanceNotificationsParser.parse_maintenance_start_msg,
|
|
224
226
|
),
|
|
225
227
|
_MIGRATED_MESSAGE: (
|
|
226
|
-
|
|
228
|
+
NodeMigratedNotification,
|
|
227
229
|
MaintenanceNotificationsParser.parse_maintenance_completed_msg,
|
|
228
230
|
),
|
|
229
231
|
_FAILING_OVER_MESSAGE: (
|
|
230
|
-
|
|
232
|
+
NodeFailingOverNotification,
|
|
231
233
|
MaintenanceNotificationsParser.parse_maintenance_start_msg,
|
|
232
234
|
),
|
|
233
235
|
_FAILED_OVER_MESSAGE: (
|
|
234
|
-
|
|
236
|
+
NodeFailedOverNotification,
|
|
235
237
|
MaintenanceNotificationsParser.parse_maintenance_completed_msg,
|
|
236
238
|
),
|
|
237
239
|
_MOVING_MESSAGE: (
|
|
238
|
-
|
|
240
|
+
NodeMovingNotification,
|
|
239
241
|
MaintenanceNotificationsParser.parse_moving_msg,
|
|
240
242
|
),
|
|
241
243
|
}
|
|
@@ -273,14 +275,20 @@ class PushNotificationsParser(Protocol):
|
|
|
273
275
|
return self.invalidation_push_handler_func(response)
|
|
274
276
|
|
|
275
277
|
if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
|
|
276
|
-
parser_function =
|
|
278
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
279
|
+
msg_type
|
|
280
|
+
][1]
|
|
277
281
|
|
|
278
282
|
notification = parser_function(response)
|
|
279
283
|
return self.node_moving_push_handler_func(notification)
|
|
280
284
|
|
|
281
285
|
if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
|
|
282
|
-
parser_function =
|
|
283
|
-
|
|
286
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
287
|
+
msg_type
|
|
288
|
+
][1]
|
|
289
|
+
notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
290
|
+
msg_type
|
|
291
|
+
][0]
|
|
284
292
|
notification = parser_function(response, notification_type)
|
|
285
293
|
|
|
286
294
|
if notification is not None:
|
|
@@ -342,13 +350,19 @@ class AsyncPushNotificationsParser(Protocol):
|
|
|
342
350
|
msg_type = msg_type.decode()
|
|
343
351
|
|
|
344
352
|
if msg_type == _MOVING_MESSAGE and self.node_moving_push_handler_func:
|
|
345
|
-
parser_function =
|
|
353
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
354
|
+
msg_type
|
|
355
|
+
][1]
|
|
346
356
|
notification = parser_function(response)
|
|
347
357
|
return await self.node_moving_push_handler_func(notification)
|
|
348
358
|
|
|
349
359
|
if msg_type in _MAINTENANCE_MESSAGES and self.maintenance_push_handler_func:
|
|
350
|
-
parser_function =
|
|
351
|
-
|
|
360
|
+
parser_function = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
361
|
+
msg_type
|
|
362
|
+
][1]
|
|
363
|
+
notification_type = MSG_TYPE_TO_MAINT_NOTIFICATION_PARSER_MAPPING[
|
|
364
|
+
msg_type
|
|
365
|
+
][0]
|
|
352
366
|
notification = parser_function(response, notification_type)
|
|
353
367
|
|
|
354
368
|
if notification is not None:
|
|
@@ -81,10 +81,11 @@ from redis.utils import (
|
|
|
81
81
|
)
|
|
82
82
|
|
|
83
83
|
if TYPE_CHECKING and SSL_AVAILABLE:
|
|
84
|
-
from ssl import TLSVersion, VerifyMode
|
|
84
|
+
from ssl import TLSVersion, VerifyFlags, VerifyMode
|
|
85
85
|
else:
|
|
86
86
|
TLSVersion = None
|
|
87
87
|
VerifyMode = None
|
|
88
|
+
VerifyFlags = None
|
|
88
89
|
|
|
89
90
|
PubSubHandler = Callable[[Dict[str, str]], Awaitable[None]]
|
|
90
91
|
_KeyT = TypeVar("_KeyT", bound=KeyT)
|
|
@@ -238,6 +239,8 @@ class Redis(
|
|
|
238
239
|
ssl_keyfile: Optional[str] = None,
|
|
239
240
|
ssl_certfile: Optional[str] = None,
|
|
240
241
|
ssl_cert_reqs: Union[str, VerifyMode] = "required",
|
|
242
|
+
ssl_include_verify_flags: Optional[List[VerifyFlags]] = None,
|
|
243
|
+
ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
|
|
241
244
|
ssl_ca_certs: Optional[str] = None,
|
|
242
245
|
ssl_ca_data: Optional[str] = None,
|
|
243
246
|
ssl_check_hostname: bool = True,
|
|
@@ -347,6 +350,8 @@ class Redis(
|
|
|
347
350
|
"ssl_keyfile": ssl_keyfile,
|
|
348
351
|
"ssl_certfile": ssl_certfile,
|
|
349
352
|
"ssl_cert_reqs": ssl_cert_reqs,
|
|
353
|
+
"ssl_include_verify_flags": ssl_include_verify_flags,
|
|
354
|
+
"ssl_exclude_verify_flags": ssl_exclude_verify_flags,
|
|
350
355
|
"ssl_ca_certs": ssl_ca_certs,
|
|
351
356
|
"ssl_ca_data": ssl_ca_data,
|
|
352
357
|
"ssl_check_hostname": ssl_check_hostname,
|
|
@@ -86,10 +86,11 @@ from redis.utils import (
|
|
|
86
86
|
)
|
|
87
87
|
|
|
88
88
|
if SSL_AVAILABLE:
|
|
89
|
-
from ssl import TLSVersion, VerifyMode
|
|
89
|
+
from ssl import TLSVersion, VerifyFlags, VerifyMode
|
|
90
90
|
else:
|
|
91
91
|
TLSVersion = None
|
|
92
92
|
VerifyMode = None
|
|
93
|
+
VerifyFlags = None
|
|
93
94
|
|
|
94
95
|
TargetNodesT = TypeVar(
|
|
95
96
|
"TargetNodesT", str, "ClusterNode", List["ClusterNode"], Dict[Any, "ClusterNode"]
|
|
@@ -299,6 +300,8 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
|
|
|
299
300
|
ssl_ca_certs: Optional[str] = None,
|
|
300
301
|
ssl_ca_data: Optional[str] = None,
|
|
301
302
|
ssl_cert_reqs: Union[str, VerifyMode] = "required",
|
|
303
|
+
ssl_include_verify_flags: Optional[List[VerifyFlags]] = None,
|
|
304
|
+
ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
|
|
302
305
|
ssl_certfile: Optional[str] = None,
|
|
303
306
|
ssl_check_hostname: bool = True,
|
|
304
307
|
ssl_keyfile: Optional[str] = None,
|
|
@@ -358,6 +361,8 @@ class RedisCluster(AbstractRedis, AbstractRedisCluster, AsyncRedisClusterCommand
|
|
|
358
361
|
"ssl_ca_certs": ssl_ca_certs,
|
|
359
362
|
"ssl_ca_data": ssl_ca_data,
|
|
360
363
|
"ssl_cert_reqs": ssl_cert_reqs,
|
|
364
|
+
"ssl_include_verify_flags": ssl_include_verify_flags,
|
|
365
|
+
"ssl_exclude_verify_flags": ssl_exclude_verify_flags,
|
|
361
366
|
"ssl_certfile": ssl_certfile,
|
|
362
367
|
"ssl_check_hostname": ssl_check_hostname,
|
|
363
368
|
"ssl_keyfile": ssl_keyfile,
|
|
@@ -30,11 +30,12 @@ from ..utils import SSL_AVAILABLE
|
|
|
30
30
|
|
|
31
31
|
if SSL_AVAILABLE:
|
|
32
32
|
import ssl
|
|
33
|
-
from ssl import SSLContext, TLSVersion
|
|
33
|
+
from ssl import SSLContext, TLSVersion, VerifyFlags
|
|
34
34
|
else:
|
|
35
35
|
ssl = None
|
|
36
36
|
TLSVersion = None
|
|
37
37
|
SSLContext = None
|
|
38
|
+
VerifyFlags = None
|
|
38
39
|
|
|
39
40
|
from ..auth.token import TokenInterface
|
|
40
41
|
from ..event import AsyncAfterConnectionReleasedEvent, EventDispatcher
|
|
@@ -793,6 +794,8 @@ class SSLConnection(Connection):
|
|
|
793
794
|
ssl_keyfile: Optional[str] = None,
|
|
794
795
|
ssl_certfile: Optional[str] = None,
|
|
795
796
|
ssl_cert_reqs: Union[str, ssl.VerifyMode] = "required",
|
|
797
|
+
ssl_include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
798
|
+
ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
796
799
|
ssl_ca_certs: Optional[str] = None,
|
|
797
800
|
ssl_ca_data: Optional[str] = None,
|
|
798
801
|
ssl_check_hostname: bool = True,
|
|
@@ -807,6 +810,8 @@ class SSLConnection(Connection):
|
|
|
807
810
|
keyfile=ssl_keyfile,
|
|
808
811
|
certfile=ssl_certfile,
|
|
809
812
|
cert_reqs=ssl_cert_reqs,
|
|
813
|
+
include_verify_flags=ssl_include_verify_flags,
|
|
814
|
+
exclude_verify_flags=ssl_exclude_verify_flags,
|
|
810
815
|
ca_certs=ssl_ca_certs,
|
|
811
816
|
ca_data=ssl_ca_data,
|
|
812
817
|
check_hostname=ssl_check_hostname,
|
|
@@ -832,6 +837,14 @@ class SSLConnection(Connection):
|
|
|
832
837
|
def cert_reqs(self):
|
|
833
838
|
return self.ssl_context.cert_reqs
|
|
834
839
|
|
|
840
|
+
@property
|
|
841
|
+
def include_verify_flags(self):
|
|
842
|
+
return self.ssl_context.include_verify_flags
|
|
843
|
+
|
|
844
|
+
@property
|
|
845
|
+
def exclude_verify_flags(self):
|
|
846
|
+
return self.ssl_context.exclude_verify_flags
|
|
847
|
+
|
|
835
848
|
@property
|
|
836
849
|
def ca_certs(self):
|
|
837
850
|
return self.ssl_context.ca_certs
|
|
@@ -854,6 +867,8 @@ class RedisSSLContext:
|
|
|
854
867
|
"keyfile",
|
|
855
868
|
"certfile",
|
|
856
869
|
"cert_reqs",
|
|
870
|
+
"include_verify_flags",
|
|
871
|
+
"exclude_verify_flags",
|
|
857
872
|
"ca_certs",
|
|
858
873
|
"ca_data",
|
|
859
874
|
"context",
|
|
@@ -867,6 +882,8 @@ class RedisSSLContext:
|
|
|
867
882
|
keyfile: Optional[str] = None,
|
|
868
883
|
certfile: Optional[str] = None,
|
|
869
884
|
cert_reqs: Optional[Union[str, ssl.VerifyMode]] = None,
|
|
885
|
+
include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
886
|
+
exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
870
887
|
ca_certs: Optional[str] = None,
|
|
871
888
|
ca_data: Optional[str] = None,
|
|
872
889
|
check_hostname: bool = False,
|
|
@@ -892,6 +909,8 @@ class RedisSSLContext:
|
|
|
892
909
|
)
|
|
893
910
|
cert_reqs = CERT_REQS[cert_reqs]
|
|
894
911
|
self.cert_reqs = cert_reqs
|
|
912
|
+
self.include_verify_flags = include_verify_flags
|
|
913
|
+
self.exclude_verify_flags = exclude_verify_flags
|
|
895
914
|
self.ca_certs = ca_certs
|
|
896
915
|
self.ca_data = ca_data
|
|
897
916
|
self.check_hostname = (
|
|
@@ -906,6 +925,12 @@ class RedisSSLContext:
|
|
|
906
925
|
context = ssl.create_default_context()
|
|
907
926
|
context.check_hostname = self.check_hostname
|
|
908
927
|
context.verify_mode = self.cert_reqs
|
|
928
|
+
if self.include_verify_flags:
|
|
929
|
+
for flag in self.include_verify_flags:
|
|
930
|
+
context.verify_flags |= flag
|
|
931
|
+
if self.exclude_verify_flags:
|
|
932
|
+
for flag in self.exclude_verify_flags:
|
|
933
|
+
context.verify_flags &= ~flag
|
|
909
934
|
if self.certfile and self.keyfile:
|
|
910
935
|
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
|
|
911
936
|
if self.ca_certs or self.ca_data:
|
|
@@ -953,6 +978,20 @@ def to_bool(value) -> Optional[bool]:
|
|
|
953
978
|
return bool(value)
|
|
954
979
|
|
|
955
980
|
|
|
981
|
+
def parse_ssl_verify_flags(value):
|
|
982
|
+
# flags are passed in as a string representation of a list,
|
|
983
|
+
# e.g. VERIFY_X509_STRICT, VERIFY_X509_PARTIAL_CHAIN
|
|
984
|
+
verify_flags_str = value.replace("[", "").replace("]", "")
|
|
985
|
+
|
|
986
|
+
verify_flags = []
|
|
987
|
+
for flag in verify_flags_str.split(","):
|
|
988
|
+
flag = flag.strip()
|
|
989
|
+
if not hasattr(VerifyFlags, flag):
|
|
990
|
+
raise ValueError(f"Invalid ssl verify flag: {flag}")
|
|
991
|
+
verify_flags.append(getattr(VerifyFlags, flag))
|
|
992
|
+
return verify_flags
|
|
993
|
+
|
|
994
|
+
|
|
956
995
|
URL_QUERY_ARGUMENT_PARSERS: Mapping[str, Callable[..., object]] = MappingProxyType(
|
|
957
996
|
{
|
|
958
997
|
"db": int,
|
|
@@ -963,6 +1002,8 @@ URL_QUERY_ARGUMENT_PARSERS: Mapping[str, Callable[..., object]] = MappingProxyTy
|
|
|
963
1002
|
"max_connections": int,
|
|
964
1003
|
"health_check_interval": int,
|
|
965
1004
|
"ssl_check_hostname": to_bool,
|
|
1005
|
+
"ssl_include_verify_flags": parse_ssl_verify_flags,
|
|
1006
|
+
"ssl_exclude_verify_flags": parse_ssl_verify_flags,
|
|
966
1007
|
"timeout": float,
|
|
967
1008
|
}
|
|
968
1009
|
)
|
|
@@ -1021,6 +1062,7 @@ def parse_url(url: str) -> ConnectKwargs:
|
|
|
1021
1062
|
|
|
1022
1063
|
if parsed.scheme == "rediss":
|
|
1023
1064
|
kwargs["connection_class"] = SSLConnection
|
|
1065
|
+
|
|
1024
1066
|
else:
|
|
1025
1067
|
valid_schemes = "redis://, rediss://, unix://"
|
|
1026
1068
|
raise ValueError(
|
|
@@ -56,9 +56,9 @@ from redis.exceptions import (
|
|
|
56
56
|
WatchError,
|
|
57
57
|
)
|
|
58
58
|
from redis.lock import Lock
|
|
59
|
-
from redis.
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
from redis.maint_notifications import (
|
|
60
|
+
MaintNotificationsConfig,
|
|
61
|
+
MaintNotificationsPoolHandler,
|
|
62
62
|
)
|
|
63
63
|
from redis.retry import Retry
|
|
64
64
|
from redis.utils import (
|
|
@@ -224,6 +224,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
224
224
|
ssl_keyfile: Optional[str] = None,
|
|
225
225
|
ssl_certfile: Optional[str] = None,
|
|
226
226
|
ssl_cert_reqs: Union[str, "ssl.VerifyMode"] = "required",
|
|
227
|
+
ssl_include_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
228
|
+
ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
|
|
227
229
|
ssl_ca_certs: Optional[str] = None,
|
|
228
230
|
ssl_ca_path: Optional[str] = None,
|
|
229
231
|
ssl_ca_data: Optional[str] = None,
|
|
@@ -248,7 +250,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
248
250
|
cache: Optional[CacheInterface] = None,
|
|
249
251
|
cache_config: Optional[CacheConfig] = None,
|
|
250
252
|
event_dispatcher: Optional[EventDispatcher] = None,
|
|
251
|
-
|
|
253
|
+
maint_notifications_config: Optional[MaintNotificationsConfig] = None,
|
|
252
254
|
) -> None:
|
|
253
255
|
"""
|
|
254
256
|
Initialize a new Redis client.
|
|
@@ -330,6 +332,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
330
332
|
"ssl_keyfile": ssl_keyfile,
|
|
331
333
|
"ssl_certfile": ssl_certfile,
|
|
332
334
|
"ssl_cert_reqs": ssl_cert_reqs,
|
|
335
|
+
"ssl_include_verify_flags": ssl_include_verify_flags,
|
|
336
|
+
"ssl_exclude_verify_flags": ssl_exclude_verify_flags,
|
|
333
337
|
"ssl_ca_certs": ssl_ca_certs,
|
|
334
338
|
"ssl_ca_data": ssl_ca_data,
|
|
335
339
|
"ssl_check_hostname": ssl_check_hostname,
|
|
@@ -373,22 +377,22 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
373
377
|
]:
|
|
374
378
|
raise RedisError("Client caching is only supported with RESP version 3")
|
|
375
379
|
|
|
376
|
-
if
|
|
380
|
+
if maint_notifications_config and self.connection_pool.get_protocol() not in [
|
|
377
381
|
3,
|
|
378
382
|
"3",
|
|
379
383
|
]:
|
|
380
384
|
raise RedisError(
|
|
381
385
|
"Push handlers on connection are only supported with RESP version 3"
|
|
382
386
|
)
|
|
383
|
-
if
|
|
384
|
-
self.
|
|
385
|
-
self.connection_pool,
|
|
387
|
+
if maint_notifications_config and maint_notifications_config.enabled:
|
|
388
|
+
self.maint_notifications_pool_handler = MaintNotificationsPoolHandler(
|
|
389
|
+
self.connection_pool, maint_notifications_config
|
|
386
390
|
)
|
|
387
|
-
self.connection_pool.
|
|
388
|
-
self.
|
|
391
|
+
self.connection_pool.set_maint_notifications_pool_handler(
|
|
392
|
+
self.maint_notifications_pool_handler
|
|
389
393
|
)
|
|
390
394
|
else:
|
|
391
|
-
self.
|
|
395
|
+
self.maint_notifications_pool_handler = None
|
|
392
396
|
|
|
393
397
|
self.single_connection_lock = threading.RLock()
|
|
394
398
|
self.connection = None
|
|
@@ -587,15 +591,15 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
587
591
|
return Monitor(self.connection_pool)
|
|
588
592
|
|
|
589
593
|
def client(self):
|
|
590
|
-
|
|
594
|
+
maint_notifications_config = (
|
|
591
595
|
None
|
|
592
|
-
if self.
|
|
593
|
-
else self.
|
|
596
|
+
if self.maint_notifications_pool_handler is None
|
|
597
|
+
else self.maint_notifications_pool_handler.config
|
|
594
598
|
)
|
|
595
599
|
return self.__class__(
|
|
596
600
|
connection_pool=self.connection_pool,
|
|
597
601
|
single_connection_client=True,
|
|
598
|
-
|
|
602
|
+
maint_notifications_config=maint_notifications_config,
|
|
599
603
|
)
|
|
600
604
|
|
|
601
605
|
def __enter__(self):
|