python-backoff 2.4.0__tar.gz → 3.1.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.
Files changed (51) hide show
  1. {python_backoff-2.4.0 → python_backoff-3.1.0}/.gitignore +2 -0
  2. {python_backoff-2.4.0 → python_backoff-3.1.0}/PKG-INFO +12 -7
  3. {python_backoff-2.4.0 → python_backoff-3.1.0}/README.rst +6 -2
  4. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/__init__.py +8 -5
  5. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_async.py +29 -20
  6. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_common.py +24 -49
  7. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_decorator.py +117 -63
  8. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_sync.py +16 -14
  9. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_typing.py +13 -23
  10. python_backoff-3.1.0/backoff/_wait_gen.py +187 -0
  11. python_backoff-3.1.0/backoff/types.py +6 -0
  12. python_backoff-3.1.0/docs/advanced/combining-decorators.md +123 -0
  13. python_backoff-3.1.0/docs/advanced/custom-strategies.md +144 -0
  14. python_backoff-3.1.0/docs/advanced/runtime-config.md +102 -0
  15. python_backoff-3.1.0/docs/api/backoff/index.md +1 -0
  16. python_backoff-3.1.0/docs/api/summary.md +1 -0
  17. python_backoff-3.1.0/docs/changelog.md +1 -0
  18. python_backoff-3.1.0/docs/examples.md +492 -0
  19. python_backoff-3.1.0/docs/faq.md +454 -0
  20. python_backoff-3.1.0/docs/getting-started.md +206 -0
  21. python_backoff-3.1.0/docs/index.md +73 -0
  22. python_backoff-3.1.0/docs/user-guide/async.md +194 -0
  23. python_backoff-3.1.0/docs/user-guide/configuration.md +397 -0
  24. python_backoff-3.1.0/docs/user-guide/context-manager.md +63 -0
  25. python_backoff-3.1.0/docs/user-guide/decorators.md +248 -0
  26. python_backoff-3.1.0/docs/user-guide/event-handlers.md +435 -0
  27. python_backoff-3.1.0/docs/user-guide/logging.md +234 -0
  28. python_backoff-3.1.0/docs/user-guide/wait-strategies.md +333 -0
  29. {python_backoff-2.4.0 → python_backoff-3.1.0}/pyproject.toml +37 -12
  30. python_backoff-3.1.0/tests/common.py +43 -0
  31. python_backoff-3.1.0/tests/conftest.py +10 -0
  32. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_backoff.py +361 -308
  33. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_backoff_async.py +345 -272
  34. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_integration.py +16 -13
  35. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_retry_context.py +77 -8
  36. python_backoff-3.1.0/tests/test_types.py +4 -0
  37. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_wait_gen.py +39 -0
  38. python_backoff-3.1.0/tests/typing/__init__.py +0 -0
  39. python_backoff-2.4.0/tests/test_typing.py → python_backoff-3.1.0/tests/typing/basic.py +3 -3
  40. python_backoff-3.1.0/tests/typing/decorators.py +54 -0
  41. python_backoff-3.1.0/tests/typing/pyrefly.py +21 -0
  42. python_backoff-2.4.0/backoff/_wait_gen.py +0 -113
  43. python_backoff-2.4.0/backoff/types.py +0 -5
  44. python_backoff-2.4.0/tests/common.py +0 -45
  45. python_backoff-2.4.0/tests/test_types.py +0 -3
  46. {python_backoff-2.4.0 → python_backoff-3.1.0}/LICENSE +0 -0
  47. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/_jitter.py +0 -0
  48. {python_backoff-2.4.0 → python_backoff-3.1.0}/backoff/py.typed +0 -0
  49. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/__init__.py +0 -0
  50. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_jitter.py +0 -0
  51. {python_backoff-2.4.0 → python_backoff-3.1.0}/tests/test_package.py +0 -0
@@ -1,3 +1,5 @@
1
+ uv.lock
2
+
1
3
  __pycache__/
2
4
  *.pyc
3
5
  .coverage*
@@ -1,7 +1,8 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: python-backoff
3
- Version: 2.4.0
4
- Summary: Function decoration for backoff and retry
3
+ Version: 3.1.0
4
+ Import-Name: backoff
5
+ Summary: Function decoration and context managers for backoff and retry
5
6
  Project-URL: Documentation, https://python-backoff.github.io/backoff
6
7
  Project-URL: Repository, https://github.com/python-backoff/backoff
7
8
  Author-email: Bob Green <rgreen@aquent.com>
@@ -15,17 +16,17 @@ Classifier: Natural Language :: English
15
16
  Classifier: Operating System :: OS Independent
16
17
  Classifier: Programming Language :: Python
17
18
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.9
19
19
  Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
23
  Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Programming Language :: Python :: 3.15
25
+ Classifier: Programming Language :: Python :: 3.16
25
26
  Classifier: Topic :: Internet :: WWW/HTTP
26
27
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
28
  Classifier: Topic :: Utilities
28
- Requires-Python: >=3.9
29
+ Requires-Python: >=3.10
29
30
  Description-Content-Type: text/x-rst
30
31
 
31
32
  backoff
@@ -40,9 +41,11 @@ backoff
40
41
  .. image:: https://img.shields.io/github/license/python-backoff/backoff
41
42
  :target: https://github.com/python-backoff/backoff/blob/main/LICENSE
42
43
 
44
+ *Note: this is an actively maintained fork of https://github.com/litl/backoff.*
45
+
43
46
  **Function decoration for backoff and retry**
44
47
 
45
- This module provides function decorators which can be used to wrap a
48
+ This library provides function decorators which can be used to wrap a
46
49
  function such that it will be retried until some condition is met. It
47
50
  is meant to be of use when accessing unreliable resources with the
48
51
  potential for intermittent failures i.e. network resources and external
@@ -53,6 +56,8 @@ Decorators support both regular functions for synchronous code and
53
56
  `asyncio <https://docs.python.org/3/library/asyncio.html>`__'s coroutines
54
57
  for asynchronous code.
55
58
 
59
+ Context managers are also available to enable retries of arbitrary code blocks.
60
+
56
61
  Examples
57
62
  ========
58
63
 
@@ -183,7 +188,7 @@ The ``on_predicate`` decorator is used to retry when a particular
183
188
  condition is true of the return value of the target function. This may
184
189
  be useful when polling a resource for externally generated content.
185
190
 
186
- Here's an example which uses a fibonacci sequence backoff when the
191
+ Here's an example which uses a Fibonacci sequence backoff when the
187
192
  return value of the target function is the empty list:
188
193
 
189
194
  .. code-block:: python
@@ -10,9 +10,11 @@ backoff
10
10
  .. image:: https://img.shields.io/github/license/python-backoff/backoff
11
11
  :target: https://github.com/python-backoff/backoff/blob/main/LICENSE
12
12
 
13
+ *Note: this is an actively maintained fork of https://github.com/litl/backoff.*
14
+
13
15
  **Function decoration for backoff and retry**
14
16
 
15
- This module provides function decorators which can be used to wrap a
17
+ This library provides function decorators which can be used to wrap a
16
18
  function such that it will be retried until some condition is met. It
17
19
  is meant to be of use when accessing unreliable resources with the
18
20
  potential for intermittent failures i.e. network resources and external
@@ -23,6 +25,8 @@ Decorators support both regular functions for synchronous code and
23
25
  `asyncio <https://docs.python.org/3/library/asyncio.html>`__'s coroutines
24
26
  for asynchronous code.
25
27
 
28
+ Context managers are also available to enable retries of arbitrary code blocks.
29
+
26
30
  Examples
27
31
  ========
28
32
 
@@ -153,7 +157,7 @@ The ``on_predicate`` decorator is used to retry when a particular
153
157
  condition is true of the return value of the target function. This may
154
158
  be useful when polling a resource for externally generated content.
155
159
 
156
- Here's an example which uses a fibonacci sequence backoff when the
160
+ Here's an example which uses a Fibonacci sequence backoff when the
157
161
  return value of the target function is the empty list:
158
162
 
159
163
  .. code-block:: python
@@ -1,5 +1,5 @@
1
1
  """
2
- Function decoration for backoff and retry
2
+ Function decoration for backoff and retry.
3
3
 
4
4
  This module provides function decorators which can be used to wrap a
5
5
  function such that it will be retried until some condition is met. It
@@ -8,16 +8,19 @@ potential for intermittent failures i.e. network resources and external
8
8
  APIs. Somewhat more generally, it may also be of use for dynamically
9
9
  polling resources for externally generated content.
10
10
 
11
- For examples and full documentation see the README at
12
- https://github.com/python-backoff/backoff
11
+ For examples and full documentation, see
12
+ [the official documentation](https://backoff.readthedocs.io/en/latest/).
13
13
  """
14
14
 
15
+ from backoff._common import Attempt
15
16
  from backoff._decorator import aretry_context, on_exception, on_predicate, retry_context
16
17
  from backoff._jitter import full_jitter, random_jitter
17
- from backoff._wait_gen import constant, decay, expo, fibo, runtime
18
+ from backoff._wait_gen import capped, constant, decay, expo, fibo, runtime
18
19
 
19
20
  __all__ = [
21
+ "Attempt",
20
22
  "aretry_context",
23
+ "capped",
21
24
  "constant",
22
25
  "decay",
23
26
  "expo",
@@ -30,4 +33,4 @@ __all__ = [
30
33
  "runtime",
31
34
  ]
32
35
 
33
- __version__ = "2.4.0"
36
+ __version__ = "3.1.0"
@@ -3,13 +3,15 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import functools
5
5
  import inspect
6
- from typing import TYPE_CHECKING, Any, Callable, TypeVar
6
+ from collections.abc import Callable
7
+ from typing import TYPE_CHECKING, Any, TypeVar
7
8
 
8
- from backoff._common import _Attempt, _RetryState
9
+ from backoff._common import Attempt, _RetryState
9
10
 
10
11
  if TYPE_CHECKING:
11
12
  import sys
12
13
  from collections.abc import AsyncGenerator, Coroutine, Iterable
14
+ from typing import ParamSpec
13
15
 
14
16
  from backoff._typing import (
15
17
  ContextDetails,
@@ -25,11 +27,6 @@ if TYPE_CHECKING:
25
27
  _WaitGenerator,
26
28
  )
27
29
 
28
- if sys.version_info >= (3, 10):
29
- from typing import ParamSpec
30
- else:
31
- from typing_extensions import ParamSpec
32
-
33
30
  if sys.version_info >= (3, 11):
34
31
  from typing import Unpack
35
32
  else:
@@ -76,26 +73,27 @@ async def _call_handlers(
76
73
  "kwargs": kwargs,
77
74
  "tries": tries,
78
75
  "elapsed": elapsed,
76
+ **extra,
79
77
  }
80
- # pyrefly: ignore [no-matching-overload]
81
- details.update(extra)
82
78
  for handler in handlers:
83
79
  await handler(details)
84
80
 
85
81
 
86
82
  def retry_predicate(
87
- target: Callable[P, T],
83
+ target: Callable[P, Coroutine[object, object, T]],
88
84
  wait_gen: _WaitGenerator,
89
85
  predicate: _Predicate[T],
90
86
  *,
91
87
  max_tries: _MaybeCallable[int] | None,
92
88
  max_time: _MaybeCallable[float] | None,
93
89
  jitter: _Jitterer | None,
90
+ on_try: Iterable[_Handler],
94
91
  on_success: Iterable[_Handler],
95
92
  on_backoff: Iterable[_Handler],
96
93
  on_giveup: Iterable[_Handler],
97
94
  wait_gen_kwargs: dict[str, Any],
98
- ) -> Callable[P, T]:
95
+ ) -> Callable[P, Coroutine[object, object, T]]:
96
+ on_try = _ensure_coroutines(on_try)
99
97
  on_success = _ensure_coroutines(on_success)
100
98
  on_backoff = _ensure_coroutines(on_backoff)
101
99
  on_giveup = _ensure_coroutines(on_giveup)
@@ -116,14 +114,16 @@ def retry_predicate(
116
114
  )
117
115
  while True:
118
116
  state.start_attempt()
119
- ret = await target(*args, **kwargs)
120
117
  details: _BaseDetails = {
121
118
  "target": target,
122
119
  "args": args,
123
120
  "kwargs": kwargs,
124
121
  "tries": state.tries,
125
- "elapsed": state.record_elapsed(),
122
+ "elapsed": state.elapsed,
126
123
  }
124
+ await _call_handlers(on_try, **details)
125
+ ret = await target(*args, **kwargs)
126
+ details["elapsed"] = state.record_elapsed()
127
127
 
128
128
  if predicate(ret):
129
129
  if state.exhausted():
@@ -154,7 +154,7 @@ def retry_predicate(
154
154
 
155
155
  return ret
156
156
 
157
- return retry # type: ignore[return-value] # ty:ignore[invalid-return-type]
157
+ return retry
158
158
 
159
159
 
160
160
  def _adapt_context_handlers(
@@ -177,7 +177,7 @@ def _adapt_context_handlers(
177
177
 
178
178
 
179
179
  def retry_exception(
180
- target: Callable[P, T],
180
+ target: Callable[P, Coroutine[object, object, T]],
181
181
  wait_gen: _WaitGenerator,
182
182
  exception: _MaybeTuple[type[Exception]],
183
183
  *,
@@ -185,12 +185,14 @@ def retry_exception(
185
185
  max_time: _MaybeCallable[float] | None,
186
186
  jitter: _Jitterer | None,
187
187
  giveup: _Predicate[Exception],
188
+ on_try: Iterable[_Handler],
188
189
  on_success: Iterable[_Handler],
189
190
  on_backoff: Iterable[_Handler],
190
191
  on_giveup: Iterable[_Handler],
191
192
  raise_on_giveup: bool,
192
193
  wait_gen_kwargs: dict[str, Any],
193
- ) -> Callable[P, T]:
194
+ ) -> Callable[P, Coroutine[object, object, T]]:
195
+ on_try = _ensure_coroutines(on_try)
194
196
  on_success = _ensure_coroutines(on_success)
195
197
  on_backoff = _ensure_coroutines(on_backoff)
196
198
  on_giveup = _ensure_coroutines(on_giveup)
@@ -214,6 +216,7 @@ def retry_exception(
214
216
  max_time=max_time,
215
217
  jitter=jitter,
216
218
  giveup=giveup,
219
+ on_try=_adapt_context_handlers(on_try, target, args, kwargs),
217
220
  on_success=_adapt_context_handlers(on_success, target, args, kwargs),
218
221
  on_backoff=_adapt_context_handlers(on_backoff, target, args, kwargs),
219
222
  on_giveup=_adapt_context_handlers(on_giveup, target, args, kwargs),
@@ -221,11 +224,11 @@ def retry_exception(
221
224
  wait_gen_kwargs=wait_gen_kwargs,
222
225
  ):
223
226
  with attempt:
224
- ret = await target(*args, **kwargs) # type: ignore[misc] # ty:ignore[invalid-await]
227
+ ret = await target(*args, **kwargs)
225
228
 
226
229
  return ret
227
230
 
228
- return retry # type: ignore[return-value] # ty:ignore[invalid-return-type]
231
+ return retry
229
232
 
230
233
 
231
234
  async def _dispatch_handlers(
@@ -243,12 +246,13 @@ async def aretry_context(
243
246
  max_time: _MaybeCallable[float] | None,
244
247
  jitter: _Jitterer | None,
245
248
  giveup: _Predicate[BaseException],
249
+ on_try: Iterable[_ContextHandler],
246
250
  on_success: Iterable[_ContextHandler],
247
251
  on_backoff: Iterable[_ContextHandler],
248
252
  on_giveup: Iterable[_ContextHandler],
249
253
  raise_on_giveup: bool,
250
254
  wait_gen_kwargs: dict[str, Any],
251
- ) -> AsyncGenerator[_Attempt, None]:
255
+ ) -> AsyncGenerator[Attempt, None]:
252
256
  giveup = _ensure_coroutine(giveup)
253
257
 
254
258
  state = _RetryState(
@@ -259,7 +263,12 @@ async def aretry_context(
259
263
  )
260
264
  while True:
261
265
  state.start_attempt()
262
- attempt = _Attempt(exception)
266
+ attempt = Attempt(exception)
267
+ await _dispatch_handlers(
268
+ handlers=on_try,
269
+ tries=state.tries,
270
+ elapsed=state.elapsed,
271
+ )
263
272
  yield attempt
264
273
  elapsed = state.record_elapsed()
265
274
 
@@ -4,16 +4,18 @@ import functools
4
4
  import logging
5
5
  import time
6
6
  import traceback
7
- import warnings
8
- from typing import TYPE_CHECKING, Any, Callable, TypeVar
7
+ from dataclasses import dataclass, field
8
+ from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  import sys
12
- from collections.abc import Generator, Iterable
12
+ from collections.abc import Callable, Generator, Iterable
13
13
 
14
14
  from backoff._typing import (
15
15
  ContextDetails,
16
16
  Details,
17
+ _AnyLogger,
18
+ _AnyLoggerOrName,
17
19
  _ContextHandler,
18
20
  _Jitterer,
19
21
  _MaybeCallable,
@@ -25,6 +27,8 @@ if TYPE_CHECKING:
25
27
  else:
26
28
  from typing_extensions import Self
27
29
 
30
+ ExceptionTypes: TypeAlias = type[Exception] | tuple[type[Exception], ...]
31
+
28
32
 
29
33
  # Use module-specific logger with a default null handler.
30
34
  _logger = logging.getLogger("backoff")
@@ -39,7 +43,7 @@ _HandlerT = TypeVar("_HandlerT")
39
43
  def _maybe_call(f: _MaybeCallable[T] | None, *args: Any, **kwargs: Any) -> T | None:
40
44
  if callable(f):
41
45
  try:
42
- return f(*args, **kwargs) # ty:ignore[call-top-callable, invalid-return-type]
46
+ return f(*args, **kwargs) # ty:ignore[invalid-return-type]
43
47
  except TypeError:
44
48
  return f # type: ignore[return-value] # ty:ignore[invalid-return-type]
45
49
  else:
@@ -64,18 +68,7 @@ def _next_wait(
64
68
  max_time: float | None,
65
69
  ) -> float:
66
70
  value = wait.send(send_value)
67
- try:
68
- seconds = jitter(value) if jitter is not None else value
69
- except TypeError:
70
- warnings.warn(
71
- "Nullary jitter function signature is deprecated. Use "
72
- "unary signature accepting a wait value in seconds and "
73
- "returning a jittered version of it.",
74
- DeprecationWarning,
75
- stacklevel=2,
76
- )
77
-
78
- seconds = value + jitter() # type: ignore[call-arg, misc] # ty:ignore[missing-argument, call-non-callable]
71
+ seconds = jitter(value) if jitter is not None else value
79
72
 
80
73
  # don't sleep longer than remaining allotted max_time
81
74
  if max_time is not None:
@@ -132,7 +125,8 @@ class _RetryState:
132
125
  return _next_wait(self.wait, send_value, jitter, self.elapsed, self.max_time)
133
126
 
134
127
 
135
- class _Attempt:
128
+ @dataclass(slots=True)
129
+ class Attempt:
136
130
  """A single attempt yielded by `retry_context`/`aretry_context`.
137
131
 
138
132
  Used as `with attempt: ...`. Exceptions matching `exception_types` are
@@ -141,16 +135,10 @@ class _Attempt:
141
135
  exception at all, is left for the `with` block's normal exit behavior.
142
136
  """
143
137
 
144
- __slots__ = (
145
- "exception",
146
- "exception_types",
147
- )
138
+ exception_types: ExceptionTypes
139
+ exception: BaseException | None = field(init=False)
148
140
 
149
- def __init__(
150
- self,
151
- exception_types: type[Exception] | tuple[type[Exception], ...],
152
- ) -> None:
153
- self.exception_types = exception_types
141
+ def __post_init__(self) -> None:
154
142
  self.exception: BaseException | None = None
155
143
 
156
144
  def __enter__(self) -> Self:
@@ -163,7 +151,6 @@ class _Attempt:
163
151
  tb: object,
164
152
  ) -> bool:
165
153
  if exc_type is None:
166
- # self.outcome = _Success()
167
154
  return False
168
155
 
169
156
  if not issubclass(exc_type, self.exception_types):
@@ -178,9 +165,7 @@ def _dispatch_handlers(handlers: Iterable[_ContextHandler], **details: Any) -> N
178
165
  hdlr(details) # type: ignore[arg-type] # ty:ignore[invalid-argument-type]
179
166
 
180
167
 
181
- def _prepare_logger(
182
- logger: str | logging.Logger | logging.LoggerAdapter | None,
183
- ) -> logging.Logger | logging.LoggerAdapter | None:
168
+ def _prepare_logger(logger: _AnyLoggerOrName | None) -> _AnyLogger | None:
184
169
  if isinstance(logger, str):
185
170
  logger = logging.getLogger(logger)
186
171
  return logger
@@ -192,7 +177,7 @@ def _config_handlers(
192
177
  user_handlers: _HandlerT | Iterable[_HandlerT] | None,
193
178
  *,
194
179
  default_handler: Callable[..., None] | None = None,
195
- logger: logging.Logger | logging.LoggerAdapter | None = None,
180
+ logger: _AnyLogger | None = None,
196
181
  log_level: int | None = None,
197
182
  ) -> list[_HandlerT]:
198
183
  handlers: list[_HandlerT] = []
@@ -225,16 +210,11 @@ def _config_handlers(
225
210
 
226
211
 
227
212
  # Default backoff handler
228
- def _log_backoff(
229
- details: Details,
230
- logger: logging.Logger | logging.LoggerAdapter,
231
- log_level: int,
232
- ) -> None:
213
+ def _log_backoff(details: Details, logger: _AnyLogger, log_level: int) -> None:
233
214
  msg = "Backing off %s(...) for %.1fs (%s)"
234
- log_args = [details["target"].__name__, details["wait"]] # ty:ignore[unresolved-attribute]
215
+ log_args = [details["target"].__name__, details["wait"]] # ty:ignore[missing-attribute]
235
216
 
236
- exc = details.get("exception")
237
- if exc is not None:
217
+ if (exc := details.get("exception")) is not None:
238
218
  exc_fmt = traceback.format_exception_only(type(exc), exc)[-1]
239
219
  log_args.append(exc_fmt.rstrip("\n"))
240
220
  else:
@@ -243,16 +223,11 @@ def _log_backoff(
243
223
 
244
224
 
245
225
  # Default giveup handler
246
- def _log_giveup(
247
- details: Details,
248
- logger: logging.Logger | logging.LoggerAdapter,
249
- log_level: int,
250
- ) -> None:
226
+ def _log_giveup(details: Details, logger: _AnyLogger, log_level: int) -> None:
251
227
  msg = "Giving up %s(...) after %d tries (%s)"
252
- log_args = [details["target"].__name__, details["tries"]] # ty:ignore[unresolved-attribute]
228
+ log_args = [details["target"].__name__, details["tries"]] # ty:ignore[missing-attribute]
253
229
 
254
- exc = details.get("exception")
255
- if exc is not None:
230
+ if (exc := details.get("exception")) is not None:
256
231
  exc_fmt = traceback.format_exception_only(type(exc), exc)[-1]
257
232
  log_args.append(exc_fmt.rstrip("\n"))
258
233
  else:
@@ -266,7 +241,7 @@ def _log_giveup(
266
241
  # directly since it's no longer the active exception by this point).
267
242
  def _log_backoff_context(
268
243
  details: ContextDetails,
269
- logger: logging.Logger | logging.LoggerAdapter,
244
+ logger: _AnyLogger,
270
245
  log_level: int,
271
246
  ) -> None:
272
247
  logger.log(
@@ -280,7 +255,7 @@ def _log_backoff_context(
280
255
  # Default giveup handler for retry_context/aretry_context.
281
256
  def _log_giveup_context(
282
257
  details: ContextDetails,
283
- logger: logging.Logger | logging.LoggerAdapter,
258
+ logger: logging.Logger | logging.LoggerAdapter[Any],
284
259
  log_level: int,
285
260
  ) -> None:
286
261
  logger.log(