redis 5.2.1__py3-none-any.whl → 5.3.0__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/client.py CHANGED
@@ -4,7 +4,17 @@ import threading
4
4
  import time
5
5
  import warnings
6
6
  from itertools import chain
7
- from typing import Any, Callable, Dict, List, Optional, Type, Union
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ Callable,
11
+ Dict,
12
+ List,
13
+ Mapping,
14
+ Optional,
15
+ Type,
16
+ Union,
17
+ )
8
18
 
9
19
  from redis._parsers.encoders import Encoder
10
20
  from redis._parsers.helpers import (
@@ -27,6 +37,13 @@ from redis.connection import (
27
37
  UnixDomainSocketConnection,
28
38
  )
29
39
  from redis.credentials import CredentialProvider
40
+ from redis.event import (
41
+ AfterPooledConnectionsInstantiationEvent,
42
+ AfterPubSubConnectionInstantiationEvent,
43
+ AfterSingleConnectionInstantiationEvent,
44
+ ClientType,
45
+ EventDispatcher,
46
+ )
30
47
  from redis.exceptions import (
31
48
  ConnectionError,
32
49
  ExecAbortError,
@@ -46,6 +63,11 @@ from redis.utils import (
46
63
  str_if_bytes,
47
64
  )
48
65
 
66
+ if TYPE_CHECKING:
67
+ import ssl
68
+
69
+ import OpenSSL
70
+
49
71
  SYM_EMPTY = b""
50
72
  EMPTY_RESPONSE = "EMPTY_RESPONSE"
51
73
 
@@ -168,51 +190,52 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
168
190
 
169
191
  def __init__(
170
192
  self,
171
- host="localhost",
172
- port=6379,
173
- db=0,
174
- password=None,
175
- socket_timeout=None,
176
- socket_connect_timeout=None,
177
- socket_keepalive=None,
178
- socket_keepalive_options=None,
179
- connection_pool=None,
180
- unix_socket_path=None,
181
- encoding="utf-8",
182
- encoding_errors="strict",
183
- charset=None,
184
- errors=None,
185
- decode_responses=False,
186
- retry_on_timeout=False,
187
- retry_on_error=None,
188
- ssl=False,
189
- ssl_keyfile=None,
190
- ssl_certfile=None,
191
- ssl_cert_reqs="required",
192
- ssl_ca_certs=None,
193
- ssl_ca_path=None,
194
- ssl_ca_data=None,
195
- ssl_check_hostname=False,
196
- ssl_password=None,
197
- ssl_validate_ocsp=False,
198
- ssl_validate_ocsp_stapled=False,
199
- ssl_ocsp_context=None,
200
- ssl_ocsp_expected_cert=None,
201
- ssl_min_version=None,
202
- ssl_ciphers=None,
203
- max_connections=None,
204
- single_connection_client=False,
205
- health_check_interval=0,
206
- client_name=None,
207
- lib_name="redis-py",
208
- lib_version=get_lib_version(),
209
- username=None,
210
- retry=None,
211
- redis_connect_func=None,
193
+ host: str = "localhost",
194
+ port: int = 6379,
195
+ db: int = 0,
196
+ password: Optional[str] = None,
197
+ socket_timeout: Optional[float] = None,
198
+ socket_connect_timeout: Optional[float] = None,
199
+ socket_keepalive: Optional[bool] = None,
200
+ socket_keepalive_options: Optional[Mapping[int, Union[int, bytes]]] = None,
201
+ connection_pool: Optional[ConnectionPool] = None,
202
+ unix_socket_path: Optional[str] = None,
203
+ encoding: str = "utf-8",
204
+ encoding_errors: str = "strict",
205
+ charset: Optional[str] = None,
206
+ errors: Optional[str] = None,
207
+ decode_responses: bool = False,
208
+ retry_on_timeout: bool = False,
209
+ retry_on_error: Optional[List[Type[Exception]]] = None,
210
+ ssl: bool = False,
211
+ ssl_keyfile: Optional[str] = None,
212
+ ssl_certfile: Optional[str] = None,
213
+ ssl_cert_reqs: str = "required",
214
+ ssl_ca_certs: Optional[str] = None,
215
+ ssl_ca_path: Optional[str] = None,
216
+ ssl_ca_data: Optional[str] = None,
217
+ ssl_check_hostname: bool = False,
218
+ ssl_password: Optional[str] = None,
219
+ ssl_validate_ocsp: bool = False,
220
+ ssl_validate_ocsp_stapled: bool = False,
221
+ ssl_ocsp_context: Optional["OpenSSL.SSL.Context"] = None,
222
+ ssl_ocsp_expected_cert: Optional[str] = None,
223
+ ssl_min_version: Optional["ssl.TLSVersion"] = None,
224
+ ssl_ciphers: Optional[str] = None,
225
+ max_connections: Optional[int] = None,
226
+ single_connection_client: bool = False,
227
+ health_check_interval: int = 0,
228
+ client_name: Optional[str] = None,
229
+ lib_name: Optional[str] = "redis-py",
230
+ lib_version: Optional[str] = get_lib_version(),
231
+ username: Optional[str] = None,
232
+ retry: Optional[Retry] = None,
233
+ redis_connect_func: Optional[Callable[[], None]] = None,
212
234
  credential_provider: Optional[CredentialProvider] = None,
213
235
  protocol: Optional[int] = 2,
214
236
  cache: Optional[CacheInterface] = None,
215
237
  cache_config: Optional[CacheConfig] = None,
238
+ event_dispatcher: Optional[EventDispatcher] = None,
216
239
  ) -> None:
217
240
  """
218
241
  Initialize a new Redis client.
@@ -227,6 +250,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
227
250
  if `True`, connection pool is not used. In that case `Redis`
228
251
  instance use is not thread safe.
229
252
  """
253
+ if event_dispatcher is None:
254
+ self._event_dispatcher = EventDispatcher()
255
+ else:
256
+ self._event_dispatcher = event_dispatcher
230
257
  if not connection_pool:
231
258
  if charset is not None:
232
259
  warnings.warn(
@@ -313,9 +340,19 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
313
340
  }
314
341
  )
315
342
  connection_pool = ConnectionPool(**kwargs)
343
+ self._event_dispatcher.dispatch(
344
+ AfterPooledConnectionsInstantiationEvent(
345
+ [connection_pool], ClientType.SYNC, credential_provider
346
+ )
347
+ )
316
348
  self.auto_close_connection_pool = True
317
349
  else:
318
350
  self.auto_close_connection_pool = False
351
+ self._event_dispatcher.dispatch(
352
+ AfterPooledConnectionsInstantiationEvent(
353
+ [connection_pool], ClientType.SYNC, credential_provider
354
+ )
355
+ )
319
356
 
320
357
  self.connection_pool = connection_pool
321
358
 
@@ -325,9 +362,16 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
325
362
  ]:
326
363
  raise RedisError("Client caching is only supported with RESP version 3")
327
364
 
365
+ self.single_connection_lock = threading.Lock()
328
366
  self.connection = None
329
- if single_connection_client:
330
- self.connection = self.connection_pool.get_connection("_")
367
+ self._single_connection_client = single_connection_client
368
+ if self._single_connection_client:
369
+ self.connection = self.connection_pool.get_connection()
370
+ self._event_dispatcher.dispatch(
371
+ AfterSingleConnectionInstantiationEvent(
372
+ self.connection, ClientType.SYNC, self.single_connection_lock
373
+ )
374
+ )
331
375
 
332
376
  self.response_callbacks = CaseInsensitiveDict(_RedisCallbacks)
333
377
 
@@ -500,7 +544,9 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
500
544
  subscribe to channels and listen for messages that get published to
501
545
  them.
502
546
  """
503
- return PubSub(self.connection_pool, **kwargs)
547
+ return PubSub(
548
+ self.connection_pool, event_dispatcher=self._event_dispatcher, **kwargs
549
+ )
504
550
 
505
551
  def monitor(self):
506
552
  return Monitor(self.connection_pool)
@@ -519,7 +565,7 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
519
565
  def __del__(self):
520
566
  self.close()
521
567
 
522
- def close(self):
568
+ def close(self) -> None:
523
569
  # In case a connection property does not yet exist
524
570
  # (due to a crash earlier in the Redis() constructor), return
525
571
  # immediately as there is nothing to clean-up.
@@ -562,7 +608,10 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
562
608
  """Execute a command and return a parsed response"""
563
609
  pool = self.connection_pool
564
610
  command_name = args[0]
565
- conn = self.connection or pool.get_connection(command_name, **options)
611
+ conn = self.connection or pool.get_connection()
612
+
613
+ if self._single_connection_client:
614
+ self.single_connection_lock.acquire()
566
615
  try:
567
616
  return conn.retry.call_with_retry(
568
617
  lambda: self._send_command_parse_response(
@@ -571,6 +620,8 @@ class Redis(RedisModuleCommands, CoreCommands, SentinelCommands):
571
620
  lambda error: self._disconnect_raise(conn, error),
572
621
  )
573
622
  finally:
623
+ if self._single_connection_client:
624
+ self.single_connection_lock.release()
574
625
  if not self.connection:
575
626
  pool.release(conn)
576
627
 
@@ -616,7 +667,7 @@ class Monitor:
616
667
 
617
668
  def __init__(self, connection_pool):
618
669
  self.connection_pool = connection_pool
619
- self.connection = self.connection_pool.get_connection("MONITOR")
670
+ self.connection = self.connection_pool.get_connection()
620
671
 
621
672
  def __enter__(self):
622
673
  self.connection.send_command("MONITOR")
@@ -691,6 +742,7 @@ class PubSub:
691
742
  ignore_subscribe_messages: bool = False,
692
743
  encoder: Optional["Encoder"] = None,
693
744
  push_handler_func: Union[None, Callable[[str], None]] = None,
745
+ event_dispatcher: Optional["EventDispatcher"] = None,
694
746
  ):
695
747
  self.connection_pool = connection_pool
696
748
  self.shard_hint = shard_hint
@@ -701,6 +753,11 @@ class PubSub:
701
753
  # to lookup channel and pattern names for callback handlers.
702
754
  self.encoder = encoder
703
755
  self.push_handler_func = push_handler_func
756
+ if event_dispatcher is None:
757
+ self._event_dispatcher = EventDispatcher()
758
+ else:
759
+ self._event_dispatcher = event_dispatcher
760
+ self._lock = threading.Lock()
704
761
  if self.encoder is None:
705
762
  self.encoder = self.connection_pool.get_encoder()
706
763
  self.health_check_response_b = self.encoder.encode(self.HEALTH_CHECK_MESSAGE)
@@ -783,19 +840,23 @@ class PubSub:
783
840
  # subscribed to one or more channels
784
841
 
785
842
  if self.connection is None:
786
- self.connection = self.connection_pool.get_connection(
787
- "pubsub", self.shard_hint
788
- )
843
+ self.connection = self.connection_pool.get_connection()
789
844
  # register a callback that re-subscribes to any channels we
790
845
  # were listening to when we were disconnected
791
846
  self.connection.register_connect_callback(self.on_connect)
792
847
  if self.push_handler_func is not None and not HIREDIS_AVAILABLE:
793
848
  self.connection._parser.set_pubsub_push_handler(self.push_handler_func)
849
+ self._event_dispatcher.dispatch(
850
+ AfterPubSubConnectionInstantiationEvent(
851
+ self.connection, self.connection_pool, ClientType.SYNC, self._lock
852
+ )
853
+ )
794
854
  connection = self.connection
795
855
  kwargs = {"check_health": not self.subscribed}
796
856
  if not self.subscribed:
797
857
  self.clean_health_check_responses()
798
- self._execute(connection, connection.send_command, *args, **kwargs)
858
+ with self._lock:
859
+ self._execute(connection, connection.send_command, *args, **kwargs)
799
860
 
800
861
  def clean_health_check_responses(self) -> None:
801
862
  """
@@ -1334,7 +1395,7 @@ class Pipeline(Redis):
1334
1395
  conn = self.connection
1335
1396
  # if this is the first call, we need a connection
1336
1397
  if not conn:
1337
- conn = self.connection_pool.get_connection(command_name, self.shard_hint)
1398
+ conn = self.connection_pool.get_connection()
1338
1399
  self.connection = conn
1339
1400
 
1340
1401
  return conn.retry.call_with_retry(
@@ -1503,11 +1564,10 @@ class Pipeline(Redis):
1503
1564
  conn.retry_on_error is None
1504
1565
  or isinstance(error, tuple(conn.retry_on_error)) is False
1505
1566
  ):
1506
-
1507
1567
  self.reset()
1508
1568
  raise error
1509
1569
 
1510
- def execute(self, raise_on_error=True):
1570
+ def execute(self, raise_on_error: bool = True) -> List[Any]:
1511
1571
  """Execute all the commands in the current pipeline"""
1512
1572
  stack = self.command_stack
1513
1573
  if not stack and not self.watching:
@@ -1521,7 +1581,7 @@ class Pipeline(Redis):
1521
1581
 
1522
1582
  conn = self.connection
1523
1583
  if not conn:
1524
- conn = self.connection_pool.get_connection("MULTI", self.shard_hint)
1584
+ conn = self.connection_pool.get_connection()
1525
1585
  # assign to self.connection so reset() releases the connection
1526
1586
  # back to the pool after we're done
1527
1587
  self.connection = conn