qq_lib.core.retryer
Utility for retrying operations with configurable backoff.
This module provides the Retryer class, which executes a function repeatedly
until it succeeds or a maximum number of attempts is reached. Failures are
logged with timing information, and the final exception is re-raised with
context when retries are exhausted.
1# Released under MIT License. 2# Copyright (c) 2025-2026 Ladislav Bartos and Robert Vacha Lab 3 4""" 5Utility for retrying operations with configurable backoff. 6 7This module provides the `Retryer` class, which executes a function repeatedly 8until it succeeds or a maximum number of attempts is reached. Failures are 9logged with timing information, and the final exception is re-raised with 10context when retries are exhausted. 11""" 12 13from collections.abc import Callable 14from time import sleep 15from typing import Any 16 17from .error import QQError 18from .logger import get_logger 19 20logger = get_logger(__name__, show_time=True) 21 22 23class Retryer: 24 """ 25 Retryer repeatedly executes a function until it succeeds or max attempts are reached. 26 27 Attributes: 28 func (Callable): The function or method to execute. 29 args (Tuple): Positional arguments to pass to the function. 30 kwargs (Dict): Keyword arguments to pass to the function. 31 max_tries (int): Maximum number of attempts. 32 wait_seconds (float): Time to wait between attempts. 33 """ 34 35 def __init__( 36 self, 37 func: Callable, 38 *args: Any, 39 max_tries: int, 40 wait_seconds: float, 41 **kwargs: Any, 42 ): 43 self._func = func 44 self._args = args 45 self._kwargs = kwargs 46 self._max_tries = max_tries 47 self._wait_seconds = wait_seconds 48 49 def run(self) -> Any: 50 """ 51 Execute the function repeatedly until it succeeds or max_tries is reached. 52 53 Any situation when the function does not raise an exception is considered a success. 54 55 Returns: 56 Any: The return value of the function if successful. 57 58 Raises: 59 Exception: The last exception raised if all attempts fail. 60 """ 61 for attempt in range(1, self._max_tries + 1): 62 try: 63 return self._func(*self._args, **self._kwargs) 64 except Exception as e: 65 if attempt == self._max_tries: 66 raise type(e)( 67 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempts exhausted." 68 ) from e 69 logger.warning( 70 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempting again in {self._wait_seconds} seconds." 71 ) 72 sleep(self._wait_seconds) 73 74 # should never get here 75 raise QQError( 76 "Execution got into an unexpected part of the Retryer.run method. This is a bug, please report it." 77 )
logger =
<Logger qq_lib.core.retryer (INFO)>
class
Retryer:
24class Retryer: 25 """ 26 Retryer repeatedly executes a function until it succeeds or max attempts are reached. 27 28 Attributes: 29 func (Callable): The function or method to execute. 30 args (Tuple): Positional arguments to pass to the function. 31 kwargs (Dict): Keyword arguments to pass to the function. 32 max_tries (int): Maximum number of attempts. 33 wait_seconds (float): Time to wait between attempts. 34 """ 35 36 def __init__( 37 self, 38 func: Callable, 39 *args: Any, 40 max_tries: int, 41 wait_seconds: float, 42 **kwargs: Any, 43 ): 44 self._func = func 45 self._args = args 46 self._kwargs = kwargs 47 self._max_tries = max_tries 48 self._wait_seconds = wait_seconds 49 50 def run(self) -> Any: 51 """ 52 Execute the function repeatedly until it succeeds or max_tries is reached. 53 54 Any situation when the function does not raise an exception is considered a success. 55 56 Returns: 57 Any: The return value of the function if successful. 58 59 Raises: 60 Exception: The last exception raised if all attempts fail. 61 """ 62 for attempt in range(1, self._max_tries + 1): 63 try: 64 return self._func(*self._args, **self._kwargs) 65 except Exception as e: 66 if attempt == self._max_tries: 67 raise type(e)( 68 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempts exhausted." 69 ) from e 70 logger.warning( 71 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempting again in {self._wait_seconds} seconds." 72 ) 73 sleep(self._wait_seconds) 74 75 # should never get here 76 raise QQError( 77 "Execution got into an unexpected part of the Retryer.run method. This is a bug, please report it." 78 )
Retryer repeatedly executes a function until it succeeds or max attempts are reached.
Attributes:
- func (Callable): The function or method to execute.
- args (Tuple): Positional arguments to pass to the function.
- kwargs (Dict): Keyword arguments to pass to the function.
- max_tries (int): Maximum number of attempts.
- wait_seconds (float): Time to wait between attempts.
def
run(self) -> Any:
50 def run(self) -> Any: 51 """ 52 Execute the function repeatedly until it succeeds or max_tries is reached. 53 54 Any situation when the function does not raise an exception is considered a success. 55 56 Returns: 57 Any: The return value of the function if successful. 58 59 Raises: 60 Exception: The last exception raised if all attempts fail. 61 """ 62 for attempt in range(1, self._max_tries + 1): 63 try: 64 return self._func(*self._args, **self._kwargs) 65 except Exception as e: 66 if attempt == self._max_tries: 67 raise type(e)( 68 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempts exhausted." 69 ) from e 70 logger.warning( 71 f"{e}\nThis was attempt {attempt} of {self._max_tries}. Attempting again in {self._wait_seconds} seconds." 72 ) 73 sleep(self._wait_seconds) 74 75 # should never get here 76 raise QQError( 77 "Execution got into an unexpected part of the Retryer.run method. This is a bug, please report it." 78 )
Execute the function repeatedly until it succeeds or max_tries is reached.
Any situation when the function does not raise an exception is considered a success.
Returns:
Any: The return value of the function if successful.
Raises:
- Exception: The last exception raised if all attempts fail.