redis 6.0.0b2__py3-none-any.whl → 6.2.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/asyncio/client.py CHANGED
@@ -39,6 +39,7 @@ from redis.asyncio.connection import (
39
39
  )
40
40
  from redis.asyncio.lock import Lock
41
41
  from redis.asyncio.retry import Retry
42
+ from redis.backoff import ExponentialWithJitterBackoff
42
43
  from redis.client import (
43
44
  EMPTY_RESPONSE,
44
45
  NEVER_DECODE,
@@ -65,14 +66,13 @@ from redis.exceptions import (
65
66
  PubSubError,
66
67
  RedisError,
67
68
  ResponseError,
68
- TimeoutError,
69
69
  WatchError,
70
70
  )
71
71
  from redis.typing import ChannelT, EncodableT, KeyT
72
72
  from redis.utils import (
73
- HIREDIS_AVAILABLE,
74
73
  SSL_AVAILABLE,
75
74
  _set_info_logger,
75
+ deprecated_args,
76
76
  deprecated_function,
77
77
  get_lib_version,
78
78
  safe_str,
@@ -208,6 +208,11 @@ class Redis(
208
208
  client.auto_close_connection_pool = True
209
209
  return client
210
210
 
211
+ @deprecated_args(
212
+ args_to_warn=["retry_on_timeout"],
213
+ reason="TimeoutError is included by default.",
214
+ version="6.0.0",
215
+ )
211
216
  def __init__(
212
217
  self,
213
218
  *,
@@ -225,6 +230,9 @@ class Redis(
225
230
  encoding_errors: str = "strict",
226
231
  decode_responses: bool = False,
227
232
  retry_on_timeout: bool = False,
233
+ retry: Retry = Retry(
234
+ backoff=ExponentialWithJitterBackoff(base=1, cap=10), retries=3
235
+ ),
228
236
  retry_on_error: Optional[list] = None,
229
237
  ssl: bool = False,
230
238
  ssl_keyfile: Optional[str] = None,
@@ -232,7 +240,7 @@ class Redis(
232
240
  ssl_cert_reqs: Union[str, VerifyMode] = "required",
233
241
  ssl_ca_certs: Optional[str] = None,
234
242
  ssl_ca_data: Optional[str] = None,
235
- ssl_check_hostname: bool = False,
243
+ ssl_check_hostname: bool = True,
236
244
  ssl_min_version: Optional[TLSVersion] = None,
237
245
  ssl_ciphers: Optional[str] = None,
238
246
  max_connections: Optional[int] = None,
@@ -242,7 +250,6 @@ class Redis(
242
250
  lib_name: Optional[str] = "redis-py",
243
251
  lib_version: Optional[str] = get_lib_version(),
244
252
  username: Optional[str] = None,
245
- retry: Optional[Retry] = None,
246
253
  auto_close_connection_pool: Optional[bool] = None,
247
254
  redis_connect_func=None,
248
255
  credential_provider: Optional[CredentialProvider] = None,
@@ -251,10 +258,24 @@ class Redis(
251
258
  ):
252
259
  """
253
260
  Initialize a new Redis client.
254
- To specify a retry policy for specific errors, first set
255
- `retry_on_error` to a list of the error/s to retry on, then set
256
- `retry` to a valid `Retry` object.
257
- To retry on TimeoutError, `retry_on_timeout` can also be set to `True`.
261
+
262
+ To specify a retry policy for specific errors, you have two options:
263
+
264
+ 1. Set the `retry_on_error` to a list of the error/s to retry on, and
265
+ you can also set `retry` to a valid `Retry` object(in case the default
266
+ one is not appropriate) - with this approach the retries will be triggered
267
+ on the default errors specified in the Retry object enriched with the
268
+ errors specified in `retry_on_error`.
269
+
270
+ 2. Define a `Retry` object with configured 'supported_errors' and set
271
+ it to the `retry` parameter - with this approach you completely redefine
272
+ the errors on which retries will happen.
273
+
274
+ `retry_on_timeout` is deprecated - please include the TimeoutError
275
+ either in the Retry object or in the `retry_on_error` list.
276
+
277
+ When 'connection_pool' is provided - the retry configuration of the
278
+ provided pool will be used.
258
279
  """
259
280
  kwargs: Dict[str, Any]
260
281
  if event_dispatcher is None:
@@ -280,8 +301,6 @@ class Redis(
280
301
  # Create internal connection pool, expected to be closed by Redis instance
281
302
  if not retry_on_error:
282
303
  retry_on_error = []
283
- if retry_on_timeout is True:
284
- retry_on_error.append(TimeoutError)
285
304
  kwargs = {
286
305
  "db": db,
287
306
  "username": username,
@@ -291,7 +310,6 @@ class Redis(
291
310
  "encoding": encoding,
292
311
  "encoding_errors": encoding_errors,
293
312
  "decode_responses": decode_responses,
294
- "retry_on_timeout": retry_on_timeout,
295
313
  "retry_on_error": retry_on_error,
296
314
  "retry": copy.deepcopy(retry),
297
315
  "max_connections": max_connections,
@@ -403,10 +421,10 @@ class Redis(
403
421
  """Get the connection's key-word arguments"""
404
422
  return self.connection_pool.connection_kwargs
405
423
 
406
- def get_retry(self) -> Optional["Retry"]:
424
+ def get_retry(self) -> Optional[Retry]:
407
425
  return self.get_connection_kwargs().get("retry")
408
426
 
409
- def set_retry(self, retry: "Retry") -> None:
427
+ def set_retry(self, retry: Retry) -> None:
410
428
  self.get_connection_kwargs().update({"retry": retry})
411
429
  self.connection_pool.set_retry(retry)
412
430
 
@@ -633,18 +651,17 @@ class Redis(
633
651
  await conn.send_command(*args)
634
652
  return await self.parse_response(conn, command_name, **options)
635
653
 
636
- async def _disconnect_raise(self, conn: Connection, error: Exception):
654
+ async def _close_connection(self, conn: Connection):
637
655
  """
638
- Close the connection and raise an exception
639
- if retry_on_error is not set or the error
640
- is not one of the specified error types
656
+ Close the connection before retrying.
657
+
658
+ The supported exceptions are already checked in the
659
+ retry object so we don't need to do it here.
660
+
661
+ After we disconnect the connection, it will try to reconnect and
662
+ do a health check as part of the send_command logic(on connection level).
641
663
  """
642
664
  await conn.disconnect()
643
- if (
644
- conn.retry_on_error is None
645
- or isinstance(error, tuple(conn.retry_on_error)) is False
646
- ):
647
- raise error
648
665
 
649
666
  # COMMAND EXECUTION AND PROTOCOL PARSING
650
667
  async def execute_command(self, *args, **options):
@@ -661,7 +678,7 @@ class Redis(
661
678
  lambda: self._send_command_parse_response(
662
679
  conn, command_name, *args, **options
663
680
  ),
664
- lambda error: self._disconnect_raise(conn, error),
681
+ lambda _: self._close_connection(conn),
665
682
  )
666
683
  finally:
667
684
  if self.single_connection_client:
@@ -920,7 +937,7 @@ class PubSub:
920
937
  self.connection.register_connect_callback(self.on_connect)
921
938
  else:
922
939
  await self.connection.connect()
923
- if self.push_handler_func is not None and not HIREDIS_AVAILABLE:
940
+ if self.push_handler_func is not None:
924
941
  self.connection._parser.set_pubsub_push_handler(self.push_handler_func)
925
942
 
926
943
  self._event_dispatcher.dispatch(
@@ -929,19 +946,11 @@ class PubSub:
929
946
  )
930
947
  )
931
948
 
932
- async def _disconnect_raise_connect(self, conn, error):
949
+ async def _reconnect(self, conn):
933
950
  """
934
- Close the connection and raise an exception
935
- if retry_on_error is not set or the error is not one
936
- of the specified error types. Otherwise, try to
937
- reconnect
951
+ Try to reconnect
938
952
  """
939
953
  await conn.disconnect()
940
- if (
941
- conn.retry_on_error is None
942
- or isinstance(error, tuple(conn.retry_on_error)) is False
943
- ):
944
- raise error
945
954
  await conn.connect()
946
955
 
947
956
  async def _execute(self, conn, command, *args, **kwargs):
@@ -954,7 +963,7 @@ class PubSub:
954
963
  """
955
964
  return await conn.retry.call_with_retry(
956
965
  lambda: command(*args, **kwargs),
957
- lambda error: self._disconnect_raise_connect(conn, error),
966
+ lambda _: self._reconnect(conn),
958
967
  )
959
968
 
960
969
  async def parse_response(self, block: bool = True, timeout: float = 0):
@@ -1245,7 +1254,8 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1245
1254
  in one transmission. This is convenient for batch processing, such as
1246
1255
  saving all the values in a list to Redis.
1247
1256
 
1248
- All commands executed within a pipeline are wrapped with MULTI and EXEC
1257
+ All commands executed within a pipeline(when running in transactional mode,
1258
+ which is the default behavior) are wrapped with MULTI and EXEC
1249
1259
  calls. This guarantees all commands executed in the pipeline will be
1250
1260
  executed atomically.
1251
1261
 
@@ -1274,7 +1284,7 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1274
1284
  self.shard_hint = shard_hint
1275
1285
  self.watching = False
1276
1286
  self.command_stack: CommandStackT = []
1277
- self.scripts: Set["Script"] = set()
1287
+ self.scripts: Set[Script] = set()
1278
1288
  self.explicit_transaction = False
1279
1289
 
1280
1290
  async def __aenter__(self: _RedisT) -> _RedisT:
@@ -1346,36 +1356,36 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1346
1356
  return self.immediate_execute_command(*args, **kwargs)
1347
1357
  return self.pipeline_execute_command(*args, **kwargs)
1348
1358
 
1349
- async def _disconnect_reset_raise(self, conn, error):
1359
+ async def _disconnect_reset_raise_on_watching(
1360
+ self,
1361
+ conn: Connection,
1362
+ error: Exception,
1363
+ ):
1350
1364
  """
1351
- Close the connection, reset watching state and
1352
- raise an exception if we were watching,
1353
- if retry_on_error is not set or the error is not one
1354
- of the specified error types.
1365
+ Close the connection reset watching state and
1366
+ raise an exception if we were watching.
1367
+
1368
+ The supported exceptions are already checked in the
1369
+ retry object so we don't need to do it here.
1370
+
1371
+ After we disconnect the connection, it will try to reconnect and
1372
+ do a health check as part of the send_command logic(on connection level).
1355
1373
  """
1356
1374
  await conn.disconnect()
1357
1375
  # if we were already watching a variable, the watch is no longer
1358
1376
  # valid since this connection has died. raise a WatchError, which
1359
1377
  # indicates the user should retry this transaction.
1360
1378
  if self.watching:
1361
- await self.aclose()
1379
+ await self.reset()
1362
1380
  raise WatchError(
1363
- "A ConnectionError occurred on while watching one or more keys"
1381
+ f"A {type(error).__name__} occurred while watching one or more keys"
1364
1382
  )
1365
- # if retry_on_error is not set or the error is not one
1366
- # of the specified error types, raise it
1367
- if (
1368
- conn.retry_on_error is None
1369
- or isinstance(error, tuple(conn.retry_on_error)) is False
1370
- ):
1371
- await self.aclose()
1372
- raise
1373
1383
 
1374
1384
  async def immediate_execute_command(self, *args, **options):
1375
1385
  """
1376
- Execute a command immediately, but don't auto-retry on a
1377
- ConnectionError if we're already WATCHing a variable. Used when
1378
- issuing WATCH or subsequent commands retrieving their values but before
1386
+ Execute a command immediately, but don't auto-retry on the supported
1387
+ errors for retry if we're already WATCHing a variable.
1388
+ Used when issuing WATCH or subsequent commands retrieving their values but before
1379
1389
  MULTI is called.
1380
1390
  """
1381
1391
  command_name = args[0]
@@ -1389,7 +1399,7 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1389
1399
  lambda: self._send_command_parse_response(
1390
1400
  conn, command_name, *args, **options
1391
1401
  ),
1392
- lambda error: self._disconnect_reset_raise(conn, error),
1402
+ lambda error: self._disconnect_reset_raise_on_watching(conn, error),
1393
1403
  )
1394
1404
 
1395
1405
  def pipeline_execute_command(self, *args, **options):
@@ -1544,11 +1554,15 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1544
1554
  if not exist:
1545
1555
  s.sha = await immediate("SCRIPT LOAD", s.script)
1546
1556
 
1547
- async def _disconnect_raise_reset(self, conn: Connection, error: Exception):
1557
+ async def _disconnect_raise_on_watching(self, conn: Connection, error: Exception):
1548
1558
  """
1549
- Close the connection, raise an exception if we were watching,
1550
- and raise an exception if retry_on_error is not set or the
1551
- error is not one of the specified error types.
1559
+ Close the connection, raise an exception if we were watching.
1560
+
1561
+ The supported exceptions are already checked in the
1562
+ retry object so we don't need to do it here.
1563
+
1564
+ After we disconnect the connection, it will try to reconnect and
1565
+ do a health check as part of the send_command logic(on connection level).
1552
1566
  """
1553
1567
  await conn.disconnect()
1554
1568
  # if we were watching a variable, the watch is no longer valid
@@ -1556,16 +1570,8 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1556
1570
  # indicates the user should retry this transaction.
1557
1571
  if self.watching:
1558
1572
  raise WatchError(
1559
- "A ConnectionError occurred on while watching one or more keys"
1573
+ f"A {type(error).__name__} occurred while watching one or more keys"
1560
1574
  )
1561
- # if retry_on_error is not set or the error is not one
1562
- # of the specified error types, raise it
1563
- if (
1564
- conn.retry_on_error is None
1565
- or isinstance(error, tuple(conn.retry_on_error)) is False
1566
- ):
1567
- await self.reset()
1568
- raise
1569
1575
 
1570
1576
  async def execute(self, raise_on_error: bool = True) -> List[Any]:
1571
1577
  """Execute all the commands in the current pipeline"""
@@ -1590,7 +1596,7 @@ class Pipeline(Redis): # lgtm [py/init-calls-subclass]
1590
1596
  try:
1591
1597
  return await conn.retry.call_with_retry(
1592
1598
  lambda: execute(conn, stack, raise_on_error),
1593
- lambda error: self._disconnect_raise_reset(conn, error),
1599
+ lambda error: self._disconnect_raise_on_watching(conn, error),
1594
1600
  )
1595
1601
  finally:
1596
1602
  await self.reset()