Coverage for rdflib_ocdm/retry_utils.py: 94%

24 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-11-01 22:02 +0000

1#!/usr/bin/python 

2# -*- coding: utf-8 -*- 

3# Copyright 2023 Arcangelo Massari <arcangelo.massari@unibo.it> 

4# 

5# Permission to use, copy, modify, and/or distribute this software for any purpose 

6# with or without fee is hereby granted, provided that the above copyright notice 

7# and this permission notice appear in all copies. 

8# 

9# THE SOFTWARE IS PROVIDED 'AS IS' AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 

10# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 

11# FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, 

12# OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 

13# DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 

14# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 

15# SOFTWARE. 

16 

17from __future__ import annotations 

18 

19import random 

20import time 

21from functools import wraps 

22from typing import Any, Callable, Optional, TypeVar, Union 

23 

24T = TypeVar('T') 

25 

26 

27def execute_with_retry(func: Callable[..., T], *args: Any, max_retries: int = 5, 

28 base_wait_time: int = 1, reporter: Any = None, **kwargs: Any) -> T: 

29 """ 

30 A function that executes the given function with retry logic and exponential backoff. 

31 This is useful when you can't use the decorator directly. 

32  

33 :param func: The function to execute with retry logic 

34 :param args: Positional arguments to pass to the function 

35 :param max_retries: Maximum number of retry attempts before giving up 

36 :param base_wait_time: Initial wait time in seconds, which will be increased exponentially 

37 :param reporter: Optional reporter object with add_sentence method for logging 

38 :param kwargs: Keyword arguments to pass to the function 

39 :return: The result of the function call 

40 """ 

41 retry_count = 0 

42 

43 while retry_count <= max_retries: 43 ↛ exitline 43 didn't return from function 'execute_with_retry' because the condition on line 43 was always true

44 try: 

45 return func(*args, **kwargs) 

46 except Exception as e: 

47 retry_count += 1 

48 if retry_count <= max_retries: 

49 # Calculate wait time with exponential backoff and some randomness 

50 wait_time = (base_wait_time * (2 ** (retry_count - 1))) + (random.random() * 0.5) 

51 

52 # Log the retry attempt 

53 message = f"Query attempt {retry_count}/{max_retries} failed: {e}. Retrying in {wait_time:.2f} seconds..." 

54 if reporter and hasattr(reporter, 'add_sentence'): 

55 reporter.add_sentence(message) 

56 else: 

57 print(message) 

58 

59 time.sleep(wait_time) 

60 else: 

61 # All retries failed 

62 error_message = f"Failed after {max_retries} attempts: {e}" 

63 if reporter and hasattr(reporter, 'add_sentence'): 63 ↛ 65line 63 didn't jump to line 65 because the condition on line 63 was always true

64 reporter.add_sentence(f"[ERROR] {error_message}") 

65 raise ValueError(error_message)