redis 6.4.0__py3-none-any.whl → 7.0.0b2__py3-none-any.whl
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/__init__.py +1 -1
- redis/_parsers/base.py +187 -8
- redis/_parsers/hiredis.py +16 -10
- redis/_parsers/resp3.py +11 -5
- redis/asyncio/client.py +51 -3
- redis/asyncio/cluster.py +52 -4
- redis/asyncio/connection.py +43 -1
- redis/cache.py +1 -0
- redis/client.py +72 -13
- redis/cluster.py +5 -2
- redis/commands/core.py +285 -285
- redis/commands/helpers.py +0 -20
- redis/commands/search/query.py +12 -12
- redis/commands/vectorset/__init__.py +1 -1
- redis/commands/vectorset/commands.py +43 -25
- redis/commands/vectorset/utils.py +40 -4
- redis/connection.py +884 -60
- redis/maint_notifications.py +799 -0
- {redis-6.4.0.dist-info → redis-7.0.0b2.dist-info}/METADATA +1 -1
- {redis-6.4.0.dist-info → redis-7.0.0b2.dist-info}/RECORD +22 -21
- {redis-6.4.0.dist-info → redis-7.0.0b2.dist-info}/WHEEL +0 -0
- {redis-6.4.0.dist-info → redis-7.0.0b2.dist-info}/licenses/LICENSE +0 -0
redis/client.py
CHANGED
|
@@ -56,6 +56,10 @@ from redis.exceptions import (
|
|
|
56
56
|
WatchError,
|
|
57
57
|
)
|
|
58
58
|
from redis.lock import Lock
|
|
59
|
+
from redis.maint_notifications import (
|
|
60
|
+
MaintNotificationsConfig,
|
|
61
|
+
MaintNotificationsPoolHandler,
|
|
62
|
+
)
|
|
59
63
|
from redis.retry import Retry
|
|
60
64
|
from redis.utils import (
|
|
61
65
|
_set_info_logger,
|
|
@@ -220,6 +224,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
220
224
|
ssl_keyfile: Optional[str] = None,
|
|
221
225
|
ssl_certfile: Optional[str] = None,
|
|
222
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,
|
|
223
229
|
ssl_ca_certs: Optional[str] = None,
|
|
224
230
|
ssl_ca_path: Optional[str] = None,
|
|
225
231
|
ssl_ca_data: Optional[str] = None,
|
|
@@ -244,6 +250,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
244
250
|
cache: Optional[CacheInterface] = None,
|
|
245
251
|
cache_config: Optional[CacheConfig] = None,
|
|
246
252
|
event_dispatcher: Optional[EventDispatcher] = None,
|
|
253
|
+
maint_notifications_config: Optional[MaintNotificationsConfig] = None,
|
|
247
254
|
) -> None:
|
|
248
255
|
"""
|
|
249
256
|
Initialize a new Redis client.
|
|
@@ -325,6 +332,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
325
332
|
"ssl_keyfile": ssl_keyfile,
|
|
326
333
|
"ssl_certfile": ssl_certfile,
|
|
327
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,
|
|
328
337
|
"ssl_ca_certs": ssl_ca_certs,
|
|
329
338
|
"ssl_ca_data": ssl_ca_data,
|
|
330
339
|
"ssl_check_hostname": ssl_check_hostname,
|
|
@@ -368,6 +377,23 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
368
377
|
]:
|
|
369
378
|
raise RedisError("Client caching is only supported with RESP version 3")
|
|
370
379
|
|
|
380
|
+
if maint_notifications_config and self.connection_pool.get_protocol() not in [
|
|
381
|
+
3,
|
|
382
|
+
"3",
|
|
383
|
+
]:
|
|
384
|
+
raise RedisError(
|
|
385
|
+
"Push handlers on connection are only supported with RESP version 3"
|
|
386
|
+
)
|
|
387
|
+
if maint_notifications_config and maint_notifications_config.enabled:
|
|
388
|
+
self.maint_notifications_pool_handler = MaintNotificationsPoolHandler(
|
|
389
|
+
self.connection_pool, maint_notifications_config
|
|
390
|
+
)
|
|
391
|
+
self.connection_pool.set_maint_notifications_pool_handler(
|
|
392
|
+
self.maint_notifications_pool_handler
|
|
393
|
+
)
|
|
394
|
+
else:
|
|
395
|
+
self.maint_notifications_pool_handler = None
|
|
396
|
+
|
|
371
397
|
self.single_connection_lock = threading.RLock()
|
|
372
398
|
self.connection = None
|
|
373
399
|
self._single_connection_client = single_connection_client
|
|
@@ -565,8 +591,15 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
565
591
|
return Monitor(self.connection_pool)
|
|
566
592
|
|
|
567
593
|
def client(self):
|
|
594
|
+
maint_notifications_config = (
|
|
595
|
+
None
|
|
596
|
+
if self.maint_notifications_pool_handler is None
|
|
597
|
+
else self.maint_notifications_pool_handler.config
|
|
598
|
+
)
|
|
568
599
|
return self.__class__(
|
|
569
|
-
connection_pool=self.connection_pool,
|
|
600
|
+
connection_pool=self.connection_pool,
|
|
601
|
+
single_connection_client=True,
|
|
602
|
+
maint_notifications_config=maint_notifications_config,
|
|
570
603
|
)
|
|
571
604
|
|
|
572
605
|
def __enter__(self):
|
|
@@ -635,7 +668,11 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
|
|
|
635
668
|
),
|
|
636
669
|
lambda _: self._close_connection(conn),
|
|
637
670
|
)
|
|
671
|
+
|
|
638
672
|
finally:
|
|
673
|
+
if conn and conn.should_reconnect():
|
|
674
|
+
self._close_connection(conn)
|
|
675
|
+
conn.connect()
|
|
639
676
|
if self._single_connection_client:
|
|
640
677
|
self.single_connection_lock.release()
|
|
641
678
|
if not self.connection:
|
|
@@ -686,11 +723,7 @@ class Monitor:
|
|
|
686
723
|
self.connection = self.connection_pool.get_connection()
|
|
687
724
|
|
|
688
725
|
def __enter__(self):
|
|
689
|
-
self.
|
|
690
|
-
# check that monitor returns 'OK', but don't return it to user
|
|
691
|
-
response = self.connection.read_response()
|
|
692
|
-
if not bool_ok(response):
|
|
693
|
-
raise RedisError(f"MONITOR failed: {response}")
|
|
726
|
+
self._start_monitor()
|
|
694
727
|
return self
|
|
695
728
|
|
|
696
729
|
def __exit__(self, *args):
|
|
@@ -700,8 +733,13 @@ class Monitor:
|
|
|
700
733
|
def next_command(self):
|
|
701
734
|
"""Parse the response from a monitor command"""
|
|
702
735
|
response = self.connection.read_response()
|
|
736
|
+
|
|
737
|
+
if response is None:
|
|
738
|
+
return None
|
|
739
|
+
|
|
703
740
|
if isinstance(response, bytes):
|
|
704
741
|
response = self.connection.encoder.decode(response, force=True)
|
|
742
|
+
|
|
705
743
|
command_time, command_data = response.split(" ", 1)
|
|
706
744
|
m = self.monitor_re.match(command_data)
|
|
707
745
|
db_id, client_info, command = m.groups()
|
|
@@ -737,6 +775,14 @@ class Monitor:
|
|
|
737
775
|
while True:
|
|
738
776
|
yield self.next_command()
|
|
739
777
|
|
|
778
|
+
def _start_monitor(self):
|
|
779
|
+
self.connection.send_command("MONITOR")
|
|
780
|
+
# check that monitor returns 'OK', but don't return it to user
|
|
781
|
+
response = self.connection.read_response()
|
|
782
|
+
|
|
783
|
+
if not bool_ok(response):
|
|
784
|
+
raise RedisError(f"MONITOR failed: {response}")
|
|
785
|
+
|
|
740
786
|
|
|
741
787
|
class PubSub:
|
|
742
788
|
"""
|
|
@@ -881,7 +927,7 @@ class PubSub:
|
|
|
881
927
|
"""
|
|
882
928
|
ttl = 10
|
|
883
929
|
conn = self.connection
|
|
884
|
-
while self.health_check_response_counter > 0 and ttl > 0:
|
|
930
|
+
while conn and self.health_check_response_counter > 0 and ttl > 0:
|
|
885
931
|
if self._execute(conn, conn.can_read, timeout=conn.socket_timeout):
|
|
886
932
|
response = self._execute(conn, conn.read_response)
|
|
887
933
|
if self.is_health_check_response(response):
|
|
@@ -911,11 +957,17 @@ class PubSub:
|
|
|
911
957
|
called by the # connection to resubscribe us to any channels and
|
|
912
958
|
patterns we were previously listening to
|
|
913
959
|
"""
|
|
914
|
-
|
|
960
|
+
|
|
961
|
+
if conn.should_reconnect():
|
|
962
|
+
self._reconnect(conn)
|
|
963
|
+
|
|
964
|
+
response = conn.retry.call_with_retry(
|
|
915
965
|
lambda: command(*args, **kwargs),
|
|
916
966
|
lambda _: self._reconnect(conn),
|
|
917
967
|
)
|
|
918
968
|
|
|
969
|
+
return response
|
|
970
|
+
|
|
919
971
|
def parse_response(self, block=True, timeout=0):
|
|
920
972
|
"""Parse the response from a publish/subscribe command"""
|
|
921
973
|
conn = self.connection
|
|
@@ -1125,6 +1177,7 @@ class PubSub:
|
|
|
1125
1177
|
return None
|
|
1126
1178
|
|
|
1127
1179
|
response = self.parse_response(block=(timeout is None), timeout=timeout)
|
|
1180
|
+
|
|
1128
1181
|
if response:
|
|
1129
1182
|
return self.handle_message(response, ignore_subscribe_messages)
|
|
1130
1183
|
return None
|
|
@@ -1148,6 +1201,7 @@ class PubSub:
|
|
|
1148
1201
|
return None
|
|
1149
1202
|
if isinstance(response, bytes):
|
|
1150
1203
|
response = [b"pong", response] if response != b"PONG" else [b"pong", b""]
|
|
1204
|
+
|
|
1151
1205
|
message_type = str_if_bytes(response[0])
|
|
1152
1206
|
if message_type == "pmessage":
|
|
1153
1207
|
message = {
|
|
@@ -1351,6 +1405,7 @@ class Pipeline(Redis):
|
|
|
1351
1405
|
# clean up the other instance attributes
|
|
1352
1406
|
self.watching = False
|
|
1353
1407
|
self.explicit_transaction = False
|
|
1408
|
+
|
|
1354
1409
|
# we can safely return the connection to the pool here since we're
|
|
1355
1410
|
# sure we're no longer WATCHing anything
|
|
1356
1411
|
if self.connection:
|
|
@@ -1510,6 +1565,7 @@ class Pipeline(Redis):
|
|
|
1510
1565
|
if command_name in self.response_callbacks:
|
|
1511
1566
|
r = self.response_callbacks[command_name](r, **options)
|
|
1512
1567
|
data.append(r)
|
|
1568
|
+
|
|
1513
1569
|
return data
|
|
1514
1570
|
|
|
1515
1571
|
def _execute_pipeline(self, connection, commands, raise_on_error):
|
|
@@ -1517,16 +1573,17 @@ class Pipeline(Redis):
|
|
|
1517
1573
|
all_cmds = connection.pack_commands([args for args, _ in commands])
|
|
1518
1574
|
connection.send_packed_command(all_cmds)
|
|
1519
1575
|
|
|
1520
|
-
|
|
1576
|
+
responses = []
|
|
1521
1577
|
for args, options in commands:
|
|
1522
1578
|
try:
|
|
1523
|
-
|
|
1579
|
+
responses.append(self.parse_response(connection, args[0], **options))
|
|
1524
1580
|
except ResponseError as e:
|
|
1525
|
-
|
|
1581
|
+
responses.append(e)
|
|
1526
1582
|
|
|
1527
1583
|
if raise_on_error:
|
|
1528
|
-
self.raise_first_error(commands,
|
|
1529
|
-
|
|
1584
|
+
self.raise_first_error(commands, responses)
|
|
1585
|
+
|
|
1586
|
+
return responses
|
|
1530
1587
|
|
|
1531
1588
|
def raise_first_error(self, commands, response):
|
|
1532
1589
|
for i, r in enumerate(response):
|
|
@@ -1611,6 +1668,8 @@ class Pipeline(Redis):
|
|
|
1611
1668
|
lambda error: self._disconnect_raise_on_watching(conn, error),
|
|
1612
1669
|
)
|
|
1613
1670
|
finally:
|
|
1671
|
+
# in reset() the connection is disconnected before returned to the pool if
|
|
1672
|
+
# it is marked for reconnect.
|
|
1614
1673
|
self.reset()
|
|
1615
1674
|
|
|
1616
1675
|
def discard(self):
|
redis/cluster.py
CHANGED
|
@@ -170,6 +170,7 @@ REDIS_ALLOWED_KEYS = (
|
|
|
170
170
|
"redis_connect_func",
|
|
171
171
|
"password",
|
|
172
172
|
"port",
|
|
173
|
+
"timeout",
|
|
173
174
|
"queue_class",
|
|
174
175
|
"retry",
|
|
175
176
|
"retry_on_timeout",
|
|
@@ -183,6 +184,8 @@ REDIS_ALLOWED_KEYS = (
|
|
|
183
184
|
"ssl_ca_data",
|
|
184
185
|
"ssl_certfile",
|
|
185
186
|
"ssl_cert_reqs",
|
|
187
|
+
"ssl_include_verify_flags",
|
|
188
|
+
"ssl_exclude_verify_flags",
|
|
186
189
|
"ssl_keyfile",
|
|
187
190
|
"ssl_password",
|
|
188
191
|
"ssl_check_hostname",
|
|
@@ -2716,8 +2719,8 @@ class PipelineStrategy(AbstractStrategy):
|
|
|
2716
2719
|
|
|
2717
2720
|
If one of the retryable exceptions has been thrown we assume that:
|
|
2718
2721
|
- connection_pool was disconnected
|
|
2719
|
-
- connection_pool was
|
|
2720
|
-
-
|
|
2722
|
+
- connection_pool was reset
|
|
2723
|
+
- refresh_table_asap set to True
|
|
2721
2724
|
|
|
2722
2725
|
It will try the number of times specified by
|
|
2723
2726
|
the retries in config option "self.retry"
|