dycw-utilities 0.131.16__py3-none-any.whl → 0.131.17__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.
utilities/slack_sdk.py CHANGED
@@ -7,21 +7,21 @@ from typing import TYPE_CHECKING, Any, Self, override
7
7
 
8
8
  from slack_sdk.webhook.async_client import AsyncWebhookClient
9
9
 
10
- from utilities.asyncio import Looper, timeout_dur
11
- from utilities.datetime import MINUTE, SECOND, datetime_duration_to_float
10
+ from utilities.asyncio import Looper, timeout_td
12
11
  from utilities.functools import cache
13
- from utilities.math import safe_round
14
12
  from utilities.sentinel import Sentinel, sentinel
13
+ from utilities.whenever2 import MINUTE, SECOND
15
14
 
16
15
  if TYPE_CHECKING:
17
16
  from collections.abc import Callable
18
17
 
19
18
  from slack_sdk.webhook import WebhookResponse
19
+ from whenever import TimeDelta
20
20
 
21
- from utilities.types import Coroutine1, Duration
21
+ from utilities.types import Coroutine1
22
22
 
23
23
 
24
- _TIMEOUT: Duration = MINUTE
24
+ _TIMEOUT: TimeDelta = MINUTE
25
25
 
26
26
 
27
27
  ##
@@ -42,14 +42,14 @@ class SlackHandlerService(Handler, Looper[str]):
42
42
  url: str,
43
43
  auto_start: bool = False,
44
44
  empty_upon_exit: bool = True,
45
- freq: Duration = SECOND,
46
- backoff: Duration = SECOND,
45
+ freq: TimeDelta = SECOND,
46
+ backoff: TimeDelta = SECOND,
47
47
  logger: str | None = None,
48
- timeout: Duration | None = None,
48
+ timeout: TimeDelta | None = None,
49
49
  _debug: bool = False,
50
50
  level: int = NOTSET,
51
51
  sender: Callable[[str, str], Coroutine1[None]] = _send_adapter,
52
- send_timeout: Duration = SECOND,
52
+ send_timeout: TimeDelta = SECOND,
53
53
  ) -> None:
54
54
  Looper.__init__( # Looper first
55
55
  self,
@@ -81,7 +81,7 @@ class SlackHandlerService(Handler, Looper[str]):
81
81
  if self.empty():
82
82
  return
83
83
  text = "\n".join(self.get_all_nowait())
84
- async with timeout_dur(duration=self.send_timeout):
84
+ async with timeout_td(self.send_timeout):
85
85
  await self.sender(self.url, text)
86
86
 
87
87
  @override
@@ -90,10 +90,10 @@ class SlackHandlerService(Handler, Looper[str]):
90
90
  *,
91
91
  auto_start: bool | Sentinel = sentinel,
92
92
  empty_upon_exit: bool | Sentinel = sentinel,
93
- freq: Duration | Sentinel = sentinel,
94
- backoff: Duration | Sentinel = sentinel,
93
+ freq: TimeDelta | Sentinel = sentinel,
94
+ backoff: TimeDelta | Sentinel = sentinel,
95
95
  logger: str | None | Sentinel = sentinel,
96
- timeout: Duration | None | Sentinel = sentinel,
96
+ timeout: TimeDelta | None | Sentinel = sentinel,
97
97
  _debug: bool | Sentinel = sentinel,
98
98
  **kwargs: Any,
99
99
  ) -> Self:
@@ -115,11 +115,11 @@ class SlackHandlerService(Handler, Looper[str]):
115
115
 
116
116
 
117
117
  async def send_to_slack(
118
- url: str, text: str, /, *, timeout: Duration = _TIMEOUT
118
+ url: str, text: str, /, *, timeout: TimeDelta = _TIMEOUT
119
119
  ) -> None:
120
120
  """Send a message via Slack."""
121
121
  client = _get_client(url, timeout=timeout)
122
- async with timeout_dur(duration=timeout):
122
+ async with timeout_td(timeout):
123
123
  response = await client.send(text=text)
124
124
  if response.status_code != HTTPStatus.OK: # pragma: no cover
125
125
  raise SendToSlackError(text=text, response=response)
@@ -138,10 +138,9 @@ class SendToSlackError(Exception):
138
138
 
139
139
 
140
140
  @cache
141
- def _get_client(url: str, /, *, timeout: Duration = _TIMEOUT) -> AsyncWebhookClient:
141
+ def _get_client(url: str, /, *, timeout: TimeDelta = _TIMEOUT) -> AsyncWebhookClient:
142
142
  """Get the Slack client."""
143
- timeout_use = safe_round(datetime_duration_to_float(timeout))
144
- return AsyncWebhookClient(url, timeout=timeout_use)
143
+ return AsyncWebhookClient(url, timeout=round(timeout.in_seconds()))
145
144
 
146
145
 
147
146
  __all__ = ["SendToSlackError", "SlackHandlerService", "send_to_slack"]
utilities/sqlalchemy.py CHANGED
@@ -18,7 +18,16 @@ from itertools import chain
18
18
  from math import floor
19
19
  from operator import ge, le
20
20
  from re import search
21
- from typing import Any, Literal, TypeGuard, TypeVar, assert_never, cast, override
21
+ from typing import (
22
+ TYPE_CHECKING,
23
+ Any,
24
+ Literal,
25
+ TypeGuard,
26
+ TypeVar,
27
+ assert_never,
28
+ cast,
29
+ override,
30
+ )
22
31
 
23
32
  from sqlalchemy import (
24
33
  URL,
@@ -57,9 +66,8 @@ from sqlalchemy.orm import (
57
66
  from sqlalchemy.orm.exc import UnmappedClassError
58
67
  from sqlalchemy.pool import NullPool, Pool
59
68
 
60
- from utilities.asyncio import Looper, timeout_dur
69
+ from utilities.asyncio import Looper, timeout_td
61
70
  from utilities.contextlib import suppress_super_object_attribute_error
62
- from utilities.datetime import SECOND
63
71
  from utilities.functions import (
64
72
  ensure_str,
65
73
  get_class_name,
@@ -82,13 +90,11 @@ from utilities.iterables import (
82
90
  )
83
91
  from utilities.reprlib import get_repr
84
92
  from utilities.text import snake_case
85
- from utilities.types import (
86
- Duration,
87
- MaybeIterable,
88
- MaybeType,
89
- StrMapping,
90
- TupleOrStrMapping,
91
- )
93
+ from utilities.types import MaybeIterable, MaybeType, StrMapping, TupleOrStrMapping
94
+ from utilities.whenever2 import SECOND
95
+
96
+ if TYPE_CHECKING:
97
+ from whenever import TimeDelta
92
98
 
93
99
  _T = TypeVar("_T")
94
100
  type _EngineOrConnectionOrAsync = Engine | Connection | AsyncEngine | AsyncConnection
@@ -105,7 +111,7 @@ async def check_engine(
105
111
  engine: AsyncEngine,
106
112
  /,
107
113
  *,
108
- timeout: Duration | None = None,
114
+ timeout: TimeDelta | None = None,
109
115
  error: type[Exception] = TimeoutError,
110
116
  num_tables: int | tuple[int, float] | None = None,
111
117
  ) -> None:
@@ -229,7 +235,7 @@ async def ensure_tables_created(
229
235
  engine: AsyncEngine,
230
236
  /,
231
237
  *tables_or_orms: TableOrORMInstOrClass,
232
- timeout: Duration | None = None,
238
+ timeout: TimeDelta | None = None,
233
239
  error: type[Exception] = TimeoutError,
234
240
  ) -> None:
235
241
  """Ensure a table/set of tables is/are created."""
@@ -258,7 +264,7 @@ async def ensure_tables_created(
258
264
  async def ensure_tables_dropped(
259
265
  engine: AsyncEngine,
260
266
  *tables_or_orms: TableOrORMInstOrClass,
261
- timeout: Duration | None = None,
267
+ timeout: TimeDelta | None = None,
262
268
  error: type[Exception] = TimeoutError,
263
269
  ) -> None:
264
270
  """Ensure a table/set of tables is/are dropped."""
@@ -384,9 +390,9 @@ async def insert_items(
384
390
  snake: bool = False,
385
391
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
386
392
  assume_tables_exist: bool = False,
387
- timeout_create: Duration | None = None,
393
+ timeout_create: TimeDelta | None = None,
388
394
  error_create: type[Exception] = TimeoutError,
389
- timeout_insert: Duration | None = None,
395
+ timeout_insert: TimeDelta | None = None,
390
396
  error_insert: type[Exception] = TimeoutError,
391
397
  ) -> None:
392
398
  """Insert a set of items into a database.
@@ -485,9 +491,9 @@ async def migrate_data(
485
491
  table_or_orm_to: TableOrORMInstOrClass | None = None,
486
492
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
487
493
  assume_tables_exist: bool = False,
488
- timeout_create: Duration | None = None,
494
+ timeout_create: TimeDelta | None = None,
489
495
  error_create: type[Exception] = TimeoutError,
490
- timeout_insert: Duration | None = None,
496
+ timeout_insert: TimeDelta | None = None,
491
497
  error_insert: type[Exception] = TimeoutError,
492
498
  ) -> None:
493
499
  """Migrate the contents of a table from one database to another."""
@@ -615,8 +621,8 @@ class UpsertService(Looper[_InsertItem]):
615
621
  """Service to upsert items to a database."""
616
622
 
617
623
  # base
618
- freq: Duration = field(default=SECOND, repr=False)
619
- backoff: Duration = field(default=SECOND, repr=False)
624
+ freq: TimeDelta = field(default=SECOND, repr=False)
625
+ backoff: TimeDelta = field(default=SECOND, repr=False)
620
626
  empty_upon_exit: bool = field(default=True, repr=False)
621
627
  # self
622
628
  engine: AsyncEngine
@@ -624,9 +630,9 @@ class UpsertService(Looper[_InsertItem]):
624
630
  selected_or_all: _SelectedOrAll = "selected"
625
631
  chunk_size_frac: float = CHUNK_SIZE_FRAC
626
632
  assume_tables_exist: bool = False
627
- timeout_create: Duration | None = None
633
+ timeout_create: TimeDelta | None = None
628
634
  error_create: type[Exception] = TimeoutError
629
- timeout_insert: Duration | None = None
635
+ timeout_insert: TimeDelta | None = None
630
636
  error_insert: type[Exception] = TimeoutError
631
637
 
632
638
  @override
@@ -651,11 +657,11 @@ class UpsertServiceMixin:
651
657
  """Mix-in for the upsert service."""
652
658
 
653
659
  # base - looper
654
- upsert_service_freq: Duration = field(default=SECOND, repr=False)
655
- upsert_service_backoff: Duration = field(default=SECOND, repr=False)
660
+ upsert_service_freq: TimeDelta = field(default=SECOND, repr=False)
661
+ upsert_service_backoff: TimeDelta = field(default=SECOND, repr=False)
656
662
  upsert_service_empty_upon_exit: bool = field(default=False, repr=False)
657
663
  upsert_service_logger: str | None = field(default=None, repr=False)
658
- upsert_service_timeout: Duration | None = field(default=None, repr=False)
664
+ upsert_service_timeout: TimeDelta | None = field(default=None, repr=False)
659
665
  upsert_service_debug: bool = field(default=False, repr=False)
660
666
  # base - upsert service
661
667
  upsert_service_database: AsyncEngine
@@ -663,9 +669,9 @@ class UpsertServiceMixin:
663
669
  upsert_service_selected_or_all: _SelectedOrAll = "selected"
664
670
  upsert_service_chunk_size_frac: float = CHUNK_SIZE_FRAC
665
671
  upsert_service_assume_tables_exist: bool = False
666
- upsert_service_timeout_create: Duration | None = None
672
+ upsert_service_timeout_create: TimeDelta | None = None
667
673
  upsert_service_error_create: type[Exception] = TimeoutError
668
- upsert_service_timeout_insert: Duration | None = None
674
+ upsert_service_timeout_insert: TimeDelta | None = None
669
675
  upsert_service_error_insert: type[Exception] = TimeoutError
670
676
  # self
671
677
  _upsert_service: UpsertService = field(init=False, repr=False)
@@ -713,9 +719,9 @@ async def upsert_items(
713
719
  selected_or_all: _SelectedOrAll = "selected",
714
720
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
715
721
  assume_tables_exist: bool = False,
716
- timeout_create: Duration | None = None,
722
+ timeout_create: TimeDelta | None = None,
717
723
  error_create: type[Exception] = TimeoutError,
718
- timeout_insert: Duration | None = None,
724
+ timeout_insert: TimeDelta | None = None,
719
725
  error_insert: type[Exception] = TimeoutError,
720
726
  ) -> None:
721
727
  """Upsert a set of items into a database.
@@ -835,11 +841,11 @@ async def yield_connection(
835
841
  engine: AsyncEngine,
836
842
  /,
837
843
  *,
838
- timeout: Duration | None = None,
844
+ timeout: TimeDelta | None = None,
839
845
  error: MaybeType[BaseException] = TimeoutError,
840
846
  ) -> AsyncIterator[AsyncConnection]:
841
847
  """Yield an async connection."""
842
- async with timeout_dur(duration=timeout, error=error), engine.begin() as conn:
848
+ async with timeout_td(timeout, error=error), engine.begin() as conn:
843
849
  yield conn
844
850
 
845
851
 
@@ -26,7 +26,7 @@ from polars import (
26
26
  from sqlalchemy import Column, Select, select
27
27
  from sqlalchemy.exc import DuplicateColumnError
28
28
 
29
- from utilities.asyncio import timeout_dur
29
+ from utilities.asyncio import timeout_td
30
30
  from utilities.functions import identity
31
31
  from utilities.iterables import (
32
32
  CheckDuplicatesError,
@@ -63,11 +63,8 @@ if TYPE_CHECKING:
63
63
  from sqlalchemy.ext.asyncio import AsyncEngine
64
64
  from sqlalchemy.sql import ColumnCollection
65
65
  from sqlalchemy.sql.base import ReadOnlyColumnCollection
66
- from tenacity.retry import RetryBaseT
67
- from tenacity.stop import StopBaseT
68
- from tenacity.wait import WaitBaseT
66
+ from whenever import TimeDelta
69
67
 
70
- import utilities.types
71
68
  from utilities.types import MaybeType, TimeZoneLike
72
69
 
73
70
 
@@ -81,9 +78,9 @@ async def insert_dataframe(
81
78
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
82
79
  assume_tables_exist: bool = False,
83
80
  upsert: Literal["selected", "all"] | None = None,
84
- timeout_create: utilities.types.Duration | None = None,
81
+ timeout_create: TimeDelta | None = None,
85
82
  error_create: type[Exception] = TimeoutError,
86
- timeout_insert: utilities.types.Duration | None = None,
83
+ timeout_insert: TimeDelta | None = None,
87
84
  error_insert: type[Exception] = TimeoutError,
88
85
  ) -> None:
89
86
  """Insert/upsert a DataFrame into a database."""
@@ -231,10 +228,7 @@ async def select_to_dataframe(
231
228
  in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
232
229
  in_clauses_chunk_size: int | None = None,
233
230
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
234
- stop: StopBaseT | None = None,
235
- wait: WaitBaseT | None = None,
236
- retry: RetryBaseT | None = None,
237
- timeout: utilities.types.Duration | None = None,
231
+ timeout: TimeDelta | None = None,
238
232
  **kwargs: Any,
239
233
  ) -> DataFrame: ...
240
234
  @overload
@@ -249,10 +243,7 @@ async def select_to_dataframe(
249
243
  in_clauses: None = None,
250
244
  in_clauses_chunk_size: int | None = None,
251
245
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
252
- stop: StopBaseT | None = None,
253
- wait: WaitBaseT | None = None,
254
- retry: RetryBaseT | None = None,
255
- timeout: utilities.types.Duration | None = None,
246
+ timeout: TimeDelta | None = None,
256
247
  **kwargs: Any,
257
248
  ) -> Iterable[DataFrame]: ...
258
249
  @overload
@@ -267,10 +258,7 @@ async def select_to_dataframe(
267
258
  in_clauses: tuple[Column[Any], Iterable[Any]],
268
259
  in_clauses_chunk_size: int | None = None,
269
260
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
270
- stop: StopBaseT | None = None,
271
- wait: WaitBaseT | None = None,
272
- retry: RetryBaseT | None = None,
273
- timeout: utilities.types.Duration | None = None,
261
+ timeout: TimeDelta | None = None,
274
262
  **kwargs: Any,
275
263
  ) -> AsyncIterable[DataFrame]: ...
276
264
  @overload
@@ -285,10 +273,7 @@ async def select_to_dataframe(
285
273
  in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
286
274
  in_clauses_chunk_size: int | None = None,
287
275
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
288
- stop: StopBaseT | None = None,
289
- wait: WaitBaseT | None = None,
290
- retry: RetryBaseT | None = None,
291
- timeout: utilities.types.Duration | None = None,
276
+ timeout: TimeDelta | None = None,
292
277
  **kwargs: Any,
293
278
  ) -> DataFrame | Iterable[DataFrame] | AsyncIterable[DataFrame]: ...
294
279
  async def select_to_dataframe(
@@ -302,7 +287,7 @@ async def select_to_dataframe(
302
287
  in_clauses: tuple[Column[Any], Iterable[Any]] | None = None,
303
288
  in_clauses_chunk_size: int | None = None,
304
289
  chunk_size_frac: float = CHUNK_SIZE_FRAC,
305
- timeout: utilities.types.Duration | None = None,
290
+ timeout: TimeDelta | None = None,
306
291
  error: MaybeType[BaseException] = TimeoutError,
307
292
  **kwargs: Any,
308
293
  ) -> DataFrame | Iterable[DataFrame] | AsyncIterable[DataFrame]:
@@ -311,7 +296,7 @@ async def select_to_dataframe(
311
296
  sel = _select_to_dataframe_apply_snake(sel)
312
297
  schema = _select_to_dataframe_map_select_to_df_schema(sel, time_zone=time_zone)
313
298
  if in_clauses is None:
314
- async with timeout_dur(duration=timeout, error=error):
299
+ async with timeout_td(timeout, error=error):
315
300
  return read_database(
316
301
  sel,
317
302
  cast("Any", engine),
@@ -328,7 +313,7 @@ async def select_to_dataframe(
328
313
  chunk_size_frac=chunk_size_frac,
329
314
  )
330
315
  if batch_size is None:
331
- async with timeout_dur(duration=timeout, error=error):
316
+ async with timeout_td(timeout, error=error):
332
317
  dfs = [
333
318
  await select_to_dataframe(
334
319
  sel,
@@ -349,7 +334,7 @@ async def select_to_dataframe(
349
334
  return DataFrame(schema=schema)
350
335
 
351
336
  async def yield_dfs() -> AsyncIterator[DataFrame]:
352
- async with timeout_dur(duration=timeout, error=error):
337
+ async with timeout_td(timeout, error=error):
353
338
  for sel_i in sels:
354
339
  for df in await select_to_dataframe(
355
340
  sel_i,
utilities/tenacity.py DELETED
@@ -1,145 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from contextlib import AbstractAsyncContextManager, asynccontextmanager
4
- from typing import TYPE_CHECKING, Any, override
5
-
6
- from tenacity import (
7
- AsyncRetrying,
8
- AttemptManager,
9
- RetryCallState,
10
- RetryError,
11
- after_nothing,
12
- before_nothing,
13
- retry_if_exception_type,
14
- stop_never,
15
- wait_none,
16
- )
17
- from tenacity import wait_exponential_jitter as _wait_exponential_jitter
18
- from tenacity._utils import MAX_WAIT
19
- from tenacity.asyncio import _portable_async_sleep
20
-
21
- from utilities.asyncio import timeout_dur
22
- from utilities.contextlib import NoOpContextManager
23
- from utilities.datetime import datetime_duration_to_float
24
-
25
- if TYPE_CHECKING:
26
- from collections.abc import AsyncIterator, Callable
27
-
28
- from tenacity.retry import RetryBaseT
29
- from tenacity.stop import StopBaseT
30
- from tenacity.wait import WaitBaseT
31
-
32
- from utilities.types import Duration, MaybeAwaitable
33
-
34
-
35
- type MaybeAttemptManager = NoOpContextManager | AttemptManager
36
- type MaybeAttemptContextManager = AbstractAsyncContextManager[MaybeAttemptManager]
37
-
38
-
39
- class wait_exponential_jitter(_wait_exponential_jitter): # noqa: N801
40
- """Subclass of `wait_exponential_jitter` accepting durations."""
41
-
42
- @override
43
- def __init__(
44
- self,
45
- initial: Duration = 1,
46
- max: Duration = MAX_WAIT,
47
- exp_base: float = 2,
48
- jitter: Duration = 1,
49
- ) -> None:
50
- super().__init__(
51
- initial=datetime_duration_to_float(initial),
52
- max=datetime_duration_to_float(max),
53
- exp_base=exp_base,
54
- jitter=datetime_duration_to_float(jitter),
55
- )
56
-
57
-
58
- async def yield_attempts(
59
- *,
60
- sleep: Callable[[int | float], MaybeAwaitable[None]] | None = None,
61
- stop: StopBaseT | None = None,
62
- wait: WaitBaseT | None = None,
63
- retry: RetryBaseT | None = None,
64
- before: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
65
- after: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
66
- before_sleep: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
67
- reraise: bool | None = None,
68
- retry_error_cls: type[RetryError] | None = None,
69
- retry_error_callback: Callable[[RetryCallState], MaybeAwaitable[Any]] | None = None,
70
- ) -> AsyncIterator[MaybeAttemptManager]:
71
- """Yield the attempts."""
72
- if (
73
- (sleep is None)
74
- and (stop is None)
75
- and (wait is None)
76
- and (retry is None)
77
- and (before is None)
78
- and (after is None)
79
- and (before_sleep is None)
80
- and (reraise is None)
81
- and (retry_error_cls is None)
82
- ):
83
- yield NoOpContextManager()
84
- else:
85
- retrying = AsyncRetrying(
86
- sleep=_portable_async_sleep if sleep is None else sleep,
87
- stop=stop_never if stop is None else stop,
88
- wait=wait_none() if wait is None else wait,
89
- retry=retry_if_exception_type() if retry is None else retry,
90
- before=before_nothing if before is None else before,
91
- after=after_nothing if after is None else after,
92
- before_sleep=None if before_sleep is None else before_sleep,
93
- reraise=False if reraise is None else reraise,
94
- retry_error_cls=RetryError if retry_error_cls is None else retry_error_cls,
95
- retry_error_callback=retry_error_callback,
96
- )
97
- async for attempt in retrying:
98
- yield attempt
99
-
100
-
101
- async def yield_timeout_attempts(
102
- *,
103
- sleep: Callable[[int | float], MaybeAwaitable[None]] | None = None,
104
- stop: StopBaseT | None = None,
105
- wait: WaitBaseT | None = None,
106
- retry: RetryBaseT | None = None,
107
- before: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
108
- after: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
109
- before_sleep: Callable[[RetryCallState], MaybeAwaitable[None]] | None = None,
110
- reraise: bool | None = None,
111
- retry_error_cls: type[RetryError] | None = None,
112
- retry_error_callback: Callable[[RetryCallState], MaybeAwaitable[Any]] | None = None,
113
- timeout: Duration | None = None,
114
- ) -> AsyncIterator[MaybeAttemptContextManager]:
115
- """Yield the attempts, with timeout."""
116
- async for attempt in yield_attempts(
117
- sleep=sleep,
118
- stop=stop,
119
- wait=wait,
120
- retry=retry,
121
- before=before,
122
- after=after,
123
- before_sleep=before_sleep,
124
- reraise=reraise,
125
- retry_error_cls=retry_error_cls,
126
- retry_error_callback=retry_error_callback,
127
- ):
128
-
129
- @asynccontextmanager
130
- async def new(
131
- attempt: MaybeAttemptManager, /
132
- ) -> AsyncIterator[MaybeAttemptManager]:
133
- with attempt:
134
- async with timeout_dur(duration=timeout):
135
- yield attempt
136
-
137
- yield new(attempt)
138
-
139
-
140
- __all__ = [
141
- "MaybeAttemptManager",
142
- "wait_exponential_jitter",
143
- "yield_attempts",
144
- "yield_timeout_attempts",
145
- ]