Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
__init__.py751 linesDownload Raw Back to tenacity
1# Copyright 2016-2018 Julien Danjou2# Copyright 2017 Elisey Zanko3# Copyright 2016 Étienne Bersac4# Copyright 2016 Joshua Harlow5# Copyright 2013-2014 Ray Holder6#7# Licensed under the Apache License, Version 2.0 (the "License");8# you may not use this file except in compliance with the License.9# You may obtain a copy of the License at10#11# http://www.apache.org/licenses/LICENSE-2.012#13# Unless required by applicable law or agreed to in writing, software14# distributed under the License is distributed on an "AS IS" BASIS,15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.16# See the License for the specific language governing permissions and17# limitations under the License.18import dataclasses19import functools20import sys21import threading22import time23import types24import typing as t25import warnings26from abc import ABC, abstractmethod27from concurrent import futures28 29from . import _utils30 31# Import all built-in retry strategies for easier usage.32from .retry import retry_base  # noqa33from .retry import retry_all  # noqa34from .retry import retry_always  # noqa35from .retry import retry_any  # noqa36from .retry import retry_if_exception  # noqa37from .retry import retry_if_exception_type  # noqa38from .retry import retry_if_exception_cause_type  # noqa39from .retry import retry_if_not_exception_type  # noqa40from .retry import retry_if_not_result  # noqa41from .retry import retry_if_result  # noqa42from .retry import retry_never  # noqa43from .retry import retry_unless_exception_type  # noqa44from .retry import retry_if_exception_message  # noqa45from .retry import retry_if_not_exception_message  # noqa46 47# Import all nap strategies for easier usage.48from .nap import sleep  # noqa49from .nap import sleep_using_event  # noqa50 51# Import all built-in stop strategies for easier usage.52from .stop import stop_after_attempt  # noqa53from .stop import stop_after_delay  # noqa54from .stop import stop_before_delay  # noqa55from .stop import stop_all  # noqa56from .stop import stop_any  # noqa57from .stop import stop_never  # noqa58from .stop import stop_when_event_set  # noqa59 60# Import all built-in wait strategies for easier usage.61from .wait import wait_chain  # noqa62from .wait import wait_combine  # noqa63from .wait import wait_exception  # noqa64from .wait import wait_exponential  # noqa65from .wait import wait_fixed  # noqa66from .wait import wait_incrementing  # noqa67from .wait import wait_none  # noqa68from .wait import wait_random  # noqa69from .wait import wait_random_exponential  # noqa70from .wait import wait_random_exponential as wait_full_jitter  # noqa71from .wait import wait_exponential_jitter  # noqa72 73# Import all built-in before strategies for easier usage.74from .before import before_log  # noqa75from .before import before_nothing  # noqa76 77# Import all built-in after strategies for easier usage.78from .after import after_log  # noqa79from .after import after_nothing  # noqa80 81# Import all built-in before sleep strategies for easier usage.82from .before_sleep import before_sleep_log  # noqa83from .before_sleep import before_sleep_nothing  # noqa84 85try:86    import tornado87except ImportError:88    tornado = None89 90if t.TYPE_CHECKING:91    from typing_extensions import Self92 93    from . import asyncio as tasyncio94    from .retry import RetryBaseT95    from .stop import StopBaseT96    from .wait import WaitBaseT97 98 99WrappedFnReturnT = t.TypeVar("WrappedFnReturnT")100WrappedFn = t.TypeVar("WrappedFn", bound=t.Callable[..., t.Any])101P = t.ParamSpec("P")102R = t.TypeVar("R")103 104 105@dataclasses.dataclass(slots=True)106class IterState:107    actions: t.List[t.Callable[["RetryCallState"], t.Any]] = dataclasses.field(108        default_factory=list109    )110    retry_run_result: bool = False111    delay_since_first_attempt: int = 0112    stop_run_result: bool = False113    is_explicit_retry: bool = False114 115    def reset(self) -> None:116        self.actions = []117        self.retry_run_result = False118        self.delay_since_first_attempt = 0119        self.stop_run_result = False120        self.is_explicit_retry = False121 122 123class TryAgain(Exception):124    """Always retry the executed function when raised."""125 126 127NO_RESULT = object()128 129 130class DoAttempt:131    pass132 133 134class DoSleep(float):135    pass136 137 138class BaseAction:139    """Base class for representing actions to take by retry object.140 141    Concrete implementations must define:142    - __init__: to initialize all necessary fields143    - REPR_FIELDS: class variable specifying attributes to include in repr(self)144    - NAME: for identification in retry object methods and callbacks145    """146 147    REPR_FIELDS: t.Sequence[str] = ()148    NAME: t.Optional[str] = None149 150    def __repr__(self) -> str:151        state_str = ", ".join(152            f"{field}={getattr(self, field)!r}" for field in self.REPR_FIELDS153        )154        return f"{self.__class__.__name__}({state_str})"155 156    def __str__(self) -> str:157        return repr(self)158 159 160class RetryAction(BaseAction):161    REPR_FIELDS = ("sleep",)162    NAME = "retry"163 164    def __init__(self, sleep: t.SupportsFloat) -> None:165        self.sleep = float(sleep)166 167 168_unset = object()169 170 171def _first_set(first: t.Union[t.Any, object], second: t.Any) -> t.Any:172    return second if first is _unset else first173 174 175class RetryError(Exception):176    """Encapsulates the last attempt instance right before giving up."""177 178    def __init__(self, last_attempt: "Future") -> None:179        self.last_attempt = last_attempt180        super().__init__(last_attempt)181 182    def reraise(self) -> t.NoReturn:183        if self.last_attempt.failed:184            raise self.last_attempt.result()185        raise self186 187    def __str__(self) -> str:188        return f"{self.__class__.__name__}[{self.last_attempt}]"189 190 191class AttemptManager:192    """Manage attempt context."""193 194    def __init__(self, retry_state: "RetryCallState"):195        self.retry_state = retry_state196 197    def __enter__(self) -> None:198        pass199 200    def __exit__(201        self,202        exc_type: t.Optional[t.Type[BaseException]],203        exc_value: t.Optional[BaseException],204        traceback: t.Optional["types.TracebackType"],205    ) -> t.Optional[bool]:206        if exc_type is not None and exc_value is not None:207            self.retry_state.set_exception((exc_type, exc_value, traceback))208            return True  # Swallow exception.209        else:210            # We don't have the result, actually.211            self.retry_state.set_result(None)212            return None213 214 215class BaseRetrying(ABC):216    def __init__(217        self,218        sleep: t.Callable[[t.Union[int, float]], None] = sleep,219        stop: "StopBaseT" = stop_never,220        wait: "WaitBaseT" = wait_none(),221        retry: "RetryBaseT" = retry_if_exception_type(),222        before: t.Callable[["RetryCallState"], None] = before_nothing,223        after: t.Callable[["RetryCallState"], None] = after_nothing,224        before_sleep: t.Optional[t.Callable[["RetryCallState"], None]] = None,225        reraise: bool = False,226        retry_error_cls: t.Type[RetryError] = RetryError,227        retry_error_callback: t.Optional[t.Callable[["RetryCallState"], t.Any]] = None,228    ):229        self.sleep = sleep230        self.stop = stop231        self.wait = wait232        self.retry = retry233        self.before = before234        self.after = after235        self.before_sleep = before_sleep236        self.reraise = reraise237        self._local = threading.local()238        self.retry_error_cls = retry_error_cls239        self.retry_error_callback = retry_error_callback240 241    def copy(242        self,243        sleep: t.Union[t.Callable[[t.Union[int, float]], None], object] = _unset,244        stop: t.Union["StopBaseT", object] = _unset,245        wait: t.Union["WaitBaseT", object] = _unset,246        retry: t.Union[retry_base, object] = _unset,247        before: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,248        after: t.Union[t.Callable[["RetryCallState"], None], object] = _unset,249        before_sleep: t.Union[250            t.Optional[t.Callable[["RetryCallState"], None]], object251        ] = _unset,252        reraise: t.Union[bool, object] = _unset,253        retry_error_cls: t.Union[t.Type[RetryError], object] = _unset,254        retry_error_callback: t.Union[255            t.Optional[t.Callable[["RetryCallState"], t.Any]], object256        ] = _unset,257    ) -> "Self":258        """Copy this object with some parameters changed if needed."""259        return self.__class__(260            sleep=_first_set(sleep, self.sleep),261            stop=_first_set(stop, self.stop),262            wait=_first_set(wait, self.wait),263            retry=_first_set(retry, self.retry),264            before=_first_set(before, self.before),265            after=_first_set(after, self.after),266            before_sleep=_first_set(before_sleep, self.before_sleep),267            reraise=_first_set(reraise, self.reraise),268            retry_error_cls=_first_set(retry_error_cls, self.retry_error_cls),269            retry_error_callback=_first_set(270                retry_error_callback, self.retry_error_callback271            ),272        )273 274    def __repr__(self) -> str:275        return (276            f"<{self.__class__.__name__} object at 0x{id(self):x} ("277            f"stop={self.stop}, "278            f"wait={self.wait}, "279            f"sleep={self.sleep}, "280            f"retry={self.retry}, "281            f"before={self.before}, "282            f"after={self.after})>"283        )284 285    @property286    def statistics(self) -> t.Dict[str, t.Any]:287        """Return a dictionary of runtime statistics.288 289        This dictionary will be empty when the controller has never been290        ran. When it is running or has ran previously it should have (but291        may not) have useful and/or informational keys and values when292        running is underway and/or completed.293 294        .. warning:: The keys in this dictionary **should** be some what295                     stable (not changing), but there existence **may**296                     change between major releases as new statistics are297                     gathered or removed so before accessing keys ensure that298                     they actually exist and handle when they do not.299 300        .. note:: The values in this dictionary are local to the thread301                  running call (so if multiple threads share the same retrying302                  object - either directly or indirectly) they will each have303                  there own view of statistics they have collected (in the304                  future we may provide a way to aggregate the various305                  statistics from each thread).306        """307        if not hasattr(self._local, "statistics"):308            self._local.statistics = t.cast(t.Dict[str, t.Any], {})309        return self._local.statistics  # type: ignore[no-any-return]310 311    @property312    def iter_state(self) -> IterState:313        if not hasattr(self._local, "iter_state"):314            self._local.iter_state = IterState()315        return self._local.iter_state  # type: ignore[no-any-return]316 317    def wraps(self, f: WrappedFn) -> WrappedFn:318        """Wrap a function for retrying.319 320        :param f: A function to wraps for retrying.321        """322 323        @functools.wraps(324            f, functools.WRAPPER_ASSIGNMENTS + ("__defaults__", "__kwdefaults__")325        )326        def wrapped_f(*args: t.Any, **kw: t.Any) -> t.Any:327            # Always create a copy to prevent overwriting the local contexts when328            # calling the same wrapped functions multiple times in the same stack329            copy = self.copy()330            wrapped_f.statistics = copy.statistics  # type: ignore[attr-defined]331            return copy(f, *args, **kw)332 333        def retry_with(*args: t.Any, **kwargs: t.Any) -> WrappedFn:334            return self.copy(*args, **kwargs).wraps(f)335 336        # Preserve attributes337        wrapped_f.retry = self  # type: ignore[attr-defined]338        wrapped_f.retry_with = retry_with  # type: ignore[attr-defined]339        wrapped_f.statistics = {}  # type: ignore[attr-defined]340 341        return wrapped_f  # type: ignore[return-value]342 343    def begin(self) -> None:344        self.statistics.clear()345        self.statistics["start_time"] = time.monotonic()346        self.statistics["attempt_number"] = 1347        self.statistics["idle_for"] = 0348 349    def _add_action_func(self, fn: t.Callable[..., t.Any]) -> None:350        self.iter_state.actions.append(fn)351 352    def _run_retry(self, retry_state: "RetryCallState") -> None:353        self.iter_state.retry_run_result = self.retry(retry_state)354 355    def _run_wait(self, retry_state: "RetryCallState") -> None:356        if self.wait:357            sleep = self.wait(retry_state)358        else:359            sleep = 0.0360 361        retry_state.upcoming_sleep = sleep362 363    def _run_stop(self, retry_state: "RetryCallState") -> None:364        self.statistics["delay_since_first_attempt"] = retry_state.seconds_since_start365        self.iter_state.stop_run_result = self.stop(retry_state)366 367    def iter(self, retry_state: "RetryCallState") -> t.Union[DoAttempt, DoSleep, t.Any]:  # noqa368        self._begin_iter(retry_state)369        result = None370        for action in self.iter_state.actions:371            result = action(retry_state)372        return result373 374    def _begin_iter(self, retry_state: "RetryCallState") -> None:  # noqa375        self.iter_state.reset()376 377        fut = retry_state.outcome378        if fut is None:379            if self.before is not None:380                self._add_action_func(self.before)381            self._add_action_func(lambda rs: DoAttempt())382            return383 384        self.iter_state.is_explicit_retry = fut.failed and isinstance(385            fut.exception(), TryAgain386        )387        if not self.iter_state.is_explicit_retry:388            self._add_action_func(self._run_retry)389        self._add_action_func(self._post_retry_check_actions)390 391    def _post_retry_check_actions(self, retry_state: "RetryCallState") -> None:392        if not (self.iter_state.is_explicit_retry or self.iter_state.retry_run_result):393            self._add_action_func(lambda rs: rs.outcome.result())394            return395 396        if self.after is not None:397            self._add_action_func(self.after)398 399        self._add_action_func(self._run_wait)400        self._add_action_func(self._run_stop)401        self._add_action_func(self._post_stop_check_actions)402 403    def _post_stop_check_actions(self, retry_state: "RetryCallState") -> None:404        if self.iter_state.stop_run_result:405            if self.retry_error_callback:406                self._add_action_func(self.retry_error_callback)407                return408 409            def exc_check(rs: "RetryCallState") -> None:410                fut = t.cast(Future, rs.outcome)411                retry_exc = self.retry_error_cls(fut)412                if self.reraise:413                    raise retry_exc.reraise()414                raise retry_exc from fut.exception()415 416            self._add_action_func(exc_check)417            return418 419        def next_action(rs: "RetryCallState") -> None:420            sleep = rs.upcoming_sleep421            rs.next_action = RetryAction(sleep)422            rs.idle_for += sleep423            self.statistics["idle_for"] += sleep424            self.statistics["attempt_number"] += 1425 426        self._add_action_func(next_action)427 428        if self.before_sleep is not None:429            self._add_action_func(self.before_sleep)430 431        self._add_action_func(lambda rs: DoSleep(rs.upcoming_sleep))432 433    def __iter__(self) -> t.Generator[AttemptManager, None, None]:434        self.begin()435 436        retry_state = RetryCallState(self, fn=None, args=(), kwargs={})437        while True:438            do = self.iter(retry_state=retry_state)439            if isinstance(do, DoAttempt):440                yield AttemptManager(retry_state=retry_state)441            elif isinstance(do, DoSleep):442                retry_state.prepare_for_next_attempt()443                self.sleep(do)444            else:445                break446 447    @abstractmethod448    def __call__(449        self,450        fn: t.Callable[..., WrappedFnReturnT],451        *args: t.Any,452        **kwargs: t.Any,453    ) -> WrappedFnReturnT:454        pass455 456 457class Retrying(BaseRetrying):458    """Retrying controller."""459 460    def __call__(461        self,462        fn: t.Callable[..., WrappedFnReturnT],463        *args: t.Any,464        **kwargs: t.Any,465    ) -> WrappedFnReturnT:466        self.begin()467 468        retry_state = RetryCallState(retry_object=self, fn=fn, args=args, kwargs=kwargs)469        while True:470            do = self.iter(retry_state=retry_state)471            if isinstance(do, DoAttempt):472                try:473                    result = fn(*args, **kwargs)474                except BaseException:  # noqa: B902475                    retry_state.set_exception(sys.exc_info())  # type: ignore[arg-type]476                else:477                    retry_state.set_result(result)478            elif isinstance(do, DoSleep):479                retry_state.prepare_for_next_attempt()480                self.sleep(do)481            else:482                return do  # type: ignore[no-any-return]483 484 485class Future(futures.Future[t.Any]):486    """Encapsulates a (future or past) attempted call to a target function."""487 488    def __init__(self, attempt_number: int) -> None:489        super().__init__()490        self.attempt_number = attempt_number491 492    @property493    def failed(self) -> bool:494        """Return whether a exception is being held in this future."""495        return self.exception() is not None496 497    @classmethod498    def construct(499        cls, attempt_number: int, value: t.Any, has_exception: bool500    ) -> "Future":501        """Construct a new Future object."""502        fut = cls(attempt_number)503        if has_exception:504            fut.set_exception(value)505        else:506            fut.set_result(value)507        return fut508 509 510class RetryCallState:511    """State related to a single call wrapped with Retrying."""512 513    def __init__(514        self,515        retry_object: BaseRetrying,516        fn: t.Optional[WrappedFn],517        args: t.Any,518        kwargs: t.Any,519    ) -> None:520        #: Retry call start timestamp521        self.start_time = time.monotonic()522        #: Retry manager object523        self.retry_object = retry_object524        #: Function wrapped by this retry call525        self.fn = fn526        #: Arguments of the function wrapped by this retry call527        self.args = args528        #: Keyword arguments of the function wrapped by this retry call529        self.kwargs = kwargs530 531        #: The number of the current attempt532        self.attempt_number: int = 1533        #: Last outcome (result or exception) produced by the function534        self.outcome: t.Optional[Future] = None535        #: Timestamp of the last outcome536        self.outcome_timestamp: t.Optional[float] = None537        #: Time spent sleeping in retries538        self.idle_for: float = 0.0539        #: Next action as decided by the retry manager540        self.next_action: t.Optional[RetryAction] = None541        #: Next sleep time as decided by the retry manager.542        self.upcoming_sleep: float = 0.0543 544    @property545    def seconds_since_start(self) -> t.Optional[float]:546        if self.outcome_timestamp is None:547            return None548        return self.outcome_timestamp - self.start_time549 550    def prepare_for_next_attempt(self) -> None:551        self.outcome = None552        self.outcome_timestamp = None553        self.attempt_number += 1554        self.next_action = None555 556    def set_result(self, val: t.Any) -> None:557        ts = time.monotonic()558        fut = Future(self.attempt_number)559        fut.set_result(val)560        self.outcome, self.outcome_timestamp = fut, ts561 562    def set_exception(563        self,564        exc_info: t.Tuple[565            t.Type[BaseException], BaseException, "types.TracebackType| None"566        ],567    ) -> None:568        ts = time.monotonic()569        fut = Future(self.attempt_number)570        fut.set_exception(exc_info[1])571        self.outcome, self.outcome_timestamp = fut, ts572 573    def __repr__(self) -> str:574        if self.outcome is None:575            result = "none yet"576        elif self.outcome.failed:577            exception = self.outcome.exception()578            result = f"failed ({exception.__class__.__name__} {exception})"579        else:580            result = f"returned {self.outcome.result()}"581 582        slept = float(round(self.idle_for, 2))583        clsname = self.__class__.__name__584        return f"<{clsname} {id(self)}: attempt #{self.attempt_number}; slept for {slept}; last result: {result}>"585 586 587class _AsyncRetryDecorator(t.Protocol):588    @t.overload589    def __call__(590        self, fn: "t.Callable[P, types.CoroutineType[t.Any, t.Any, R]]"591    ) -> "t.Callable[P, types.CoroutineType[t.Any, t.Any, R]]": ...592    @t.overload593    def __call__(594        self, fn: t.Callable[P, t.Coroutine[t.Any, t.Any, R]]595    ) -> t.Callable[P, t.Coroutine[t.Any, t.Any, R]]: ...596    @t.overload597    def __call__(598        self, fn: t.Callable[P, t.Awaitable[R]]599    ) -> t.Callable[P, t.Awaitable[R]]: ...600    @t.overload601    def __call__(self, fn: t.Callable[P, R]) -> t.Callable[P, t.Awaitable[R]]: ...602 603 604@t.overload605def retry(func: WrappedFn) -> WrappedFn: ...606 607 608@t.overload609def retry(610    *,611    sleep: t.Callable[[t.Union[int, float]], t.Awaitable[None]],612    stop: "StopBaseT" = ...,613    wait: "WaitBaseT" = ...,614    retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = ...,615    before: t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] = ...,616    after: t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]] = ...,617    before_sleep: t.Optional[618        t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]619    ] = ...,620    reraise: bool = ...,621    retry_error_cls: t.Type["RetryError"] = ...,622    retry_error_callback: t.Optional[623        t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]624    ] = ...,625) -> _AsyncRetryDecorator: ...626 627 628@t.overload629def retry(630    sleep: t.Callable[[t.Union[int, float]], None] = sleep,631    stop: "StopBaseT" = stop_never,632    wait: "WaitBaseT" = wait_none(),633    retry: "t.Union[RetryBaseT, tasyncio.retry.RetryBaseT]" = retry_if_exception_type(),634    before: t.Callable[635        ["RetryCallState"], t.Union[None, t.Awaitable[None]]636    ] = before_nothing,637    after: t.Callable[638        ["RetryCallState"], t.Union[None, t.Awaitable[None]]639    ] = after_nothing,640    before_sleep: t.Optional[641        t.Callable[["RetryCallState"], t.Union[None, t.Awaitable[None]]]642    ] = None,643    reraise: bool = False,644    retry_error_cls: t.Type["RetryError"] = RetryError,645    retry_error_callback: t.Optional[646        t.Callable[["RetryCallState"], t.Union[t.Any, t.Awaitable[t.Any]]]647    ] = None,648) -> t.Callable[[WrappedFn], WrappedFn]: ...649 650 651def retry(*dargs: t.Any, **dkw: t.Any) -> t.Any:652    """Wrap a function with a new `Retrying` object.653 654    :param dargs: positional arguments passed to Retrying object655    :param dkw: keyword arguments passed to the Retrying object656    """657    # support both @retry and @retry() as valid syntax658    if len(dargs) == 1 and callable(dargs[0]):659        return retry()(dargs[0])660    else:661 662        def wrap(f: WrappedFn) -> WrappedFn:663            if isinstance(f, retry_base):664                warnings.warn(665                    f"Got retry_base instance ({f.__class__.__name__}) as callable argument, "666                    f"this will probably hang indefinitely (did you mean retry={f.__class__.__name__}(...)?)"667                )668            r: "BaseRetrying"669            sleep = dkw.get("sleep")670            if _utils.is_coroutine_callable(f) or (671                sleep is not None and _utils.is_coroutine_callable(sleep)672            ):673                r = AsyncRetrying(*dargs, **dkw)674            elif (675                tornado676                and hasattr(tornado.gen, "is_coroutine_function")677                and tornado.gen.is_coroutine_function(f)678            ):679                r = TornadoRetrying(*dargs, **dkw)680            else:681                r = Retrying(*dargs, **dkw)682 683            return r.wraps(f)684 685        return wrap686 687 688from tenacity.asyncio import AsyncRetrying  # noqa:E402,I100689 690if tornado:691    from tenacity.tornadoweb import TornadoRetrying692 693 694__all__ = [695    "retry_base",696    "retry_all",697    "retry_always",698    "retry_any",699    "retry_if_exception",700    "retry_if_exception_type",701    "retry_if_exception_cause_type",702    "retry_if_not_exception_type",703    "retry_if_not_result",704    "retry_if_result",705    "retry_never",706    "retry_unless_exception_type",707    "retry_if_exception_message",708    "retry_if_not_exception_message",709    "sleep",710    "sleep_using_event",711    "stop_after_attempt",712    "stop_after_delay",713    "stop_before_delay",714    "stop_all",715    "stop_any",716    "stop_never",717    "stop_when_event_set",718    "wait_chain",719    "wait_combine",720    "wait_exception",721    "wait_exponential",722    "wait_fixed",723    "wait_incrementing",724    "wait_none",725    "wait_random",726    "wait_random_exponential",727    "wait_full_jitter",728    "wait_exponential_jitter",729    "before_log",730    "before_nothing",731    "after_log",732    "after_nothing",733    "before_sleep_log",734    "before_sleep_nothing",735    "retry",736    "WrappedFn",737    "TryAgain",738    "NO_RESULT",739    "DoAttempt",740    "DoSleep",741    "BaseAction",742    "RetryAction",743    "RetryError",744    "AttemptManager",745    "BaseRetrying",746    "Retrying",747    "Future",748    "RetryCallState",749    "AsyncRetrying",750]751 
codekingpro/portable-devtools · Team Ai