From 550532579ae0f3c5e140ae0576841d72c2ad07d0 Mon Sep 17 00:00:00 2001 From: talaatmagdyx Date: Fri, 7 Jun 2024 06:31:23 +0300 Subject: [PATCH] - default_stop_condition stops retrying after 3 attempts. - default_wait_condition waits 1 second between attempts. - If no stop_condition or wait_condition is provided, the defaults will be used. --- CHANGELOG.md | 6 +++++- README.md | 21 +++++++++++++++++++++ example/http_example.py | 31 +++++++++++++++++++++++++++++++ pyproject.toml | 2 +- retry/retry.py | 20 ++++++++++++++------ setup.cfg | 2 +- setup.py | 2 +- 7 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 example/http_example.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 9472ee0..e62af98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,8 @@ - Customizable retry conditions based on exceptions and result values. - Context manager support. - Logging and custom callbacks before/after retries and before sleep. -- Retry statistics and dynamic arguments at runtime. \ No newline at end of file +- Retry statistics and dynamic arguments at runtime. +## [1.0.4] - 2024-06-07 +- default_stop_condition stops retrying after 3 attempts. +- default_wait_condition waits 1 second between attempts. +- If no stop_condition or wait_condition is provided, the defaults will be used. \ No newline at end of file diff --git a/README.md b/README.md index 844c627..0d711db 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,28 @@ except Exception as e: print(f"Failed to fetch data after retries. Error: {e}") ``` +## Default Conditions +If `stop_condition` and `wait_condition` are not provided, the following defaults will be used: +- Stop Condition: Stops after 3 attempts. +- Wait Condition: Waits 1 second between attempts. +```python +from retry import Retry +import time + +@Retry() +def default_unreliable_function(): + print("Trying to perform an unreliable operation.") + if time.time() % 2 < 1: + raise ValueError("Simulated transient error.") + return "Success" + +try: + result = default_unreliable_function() + print(f"Function succeeded with result: {result}") +except Exception as e: + print(f"Function failed after retries with exception: {e}") +``` ## Tests diff --git a/example/http_example.py b/example/http_example.py new file mode 100644 index 0000000..d01750f --- /dev/null +++ b/example/http_example.py @@ -0,0 +1,31 @@ +import requests +from retry import Retry, stop_after_attempt, wait_exponential + +# Define a function to make an HTTP request +@Retry( + # stop_condition=stop_after_attempt(5), # Retry up to 5 times + # wait_condition=wait_exponential(multiplier=1, min_wait=1, max_wait=10), # Exponential backoff + retry_on_exceptions=(requests.RequestException,), # Retry on any requests exception + retry_on_result=lambda result: result.status_code != 200 # Retry if the status code is not 200 +) +def fetch_data_from_api(url): + print(f"Trying to fetch data from {url}") + response = requests.get(url) + response.raise_for_status() # Raise an HTTPError on bad status + return response + +# Use the function with a simulated unreliable endpoint +try: + # Simulating a service that returns 500 Internal Server Error 50% of the time + data = fetch_data_from_api("https://httpbin.org/status/500") + print("Data fetched successfully:", data) +except Exception as e: + print(f"Failed to fetch data after retries. Error: {e}") + +# Use the function with a simulated successful endpoint +try: + # Simulating a service that returns 200 OK + data = fetch_data_from_api("https://httpbin.org/status/200") + print("Data fetched successfully:", data) +except Exception as e: + print(f"Failed to fetch data after retries. Error: {e}") diff --git a/pyproject.toml b/pyproject.toml index 5be4199..0339158 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "retry_plus" -version = "1.0.3" +version = "1.0.4" description = "A generic retry package for Python" readme = "README.md" authors = [ diff --git a/retry/retry.py b/retry/retry.py index d6a7cf3..fdbeefe 100644 --- a/retry/retry.py +++ b/retry/retry.py @@ -25,8 +25,8 @@ class Retry: Retry decorator and context manager to retry operations based on specified conditions. Args: - stop_condition: Callable that determines when to stop retrying. - wait_condition: Callable that determines how long to wait between attempts. + stop_condition: Callable that determines when to stop retrying. Default stop condition: stops after 3 attempts. + wait_condition: Callable that determines how long to wait between attempts. Default wait condition: waits 1 second between attempts. retry_on_exceptions: Tuple of exception types that trigger a retry. retry_on_result: Callable that determines if the result should trigger a retry. before: Callable executed before each attempt. @@ -36,16 +36,16 @@ class Retry: """ def __init__(self, - stop_condition: Callable[[int, Optional[Exception], Optional[Any]], bool], - wait_condition: Callable[[int], float], + stop_condition: Callable[[int, Optional[Exception], Optional[Any]], bool] = None, + wait_condition: Callable[[int], float] = None, retry_on_exceptions: Tuple[Type[Exception], ...] = (Exception,), retry_on_result: Optional[Callable[[Any], bool]] = None, before: Optional[Callable] = None, after: Optional[Callable] = None, before_sleep: Optional[Callable] = None, reraise: bool = False): - self.stop_condition = stop_condition # Storing the stop condition function - self.wait_condition = wait_condition # Storing the wait condition function + self.stop_condition = stop_condition if stop_condition is not None else self.default_stop_condition + self.wait_condition = wait_condition if wait_condition is not None else self.default_wait_condition self.retry_on_exceptions = retry_on_exceptions # Storing the exceptions that trigger a retry self.retry_on_result = retry_on_result # Storing the result-based retry condition self.before = before # Storing the before attempt callback @@ -53,6 +53,14 @@ def __init__(self, self.before_sleep = before_sleep # Storing the before sleep callback self.reraise = reraise # Storing the reraise flag + def default_stop_condition(self, attempt: int, exception: Optional[Exception], result: Optional[Any]) -> bool: + """Default stop condition: stops after 3 attempts.""" + return attempt >= 3 + + def default_wait_condition(self, attempt: int) -> float: + """Default wait condition: waits 1 second between attempts.""" + return 1.0 + def __call__(self, func: Callable): """ Wraps the function with retry logic. diff --git a/setup.cfg b/setup.cfg index 57a10f6..62b37c3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = retry_plus -version = 1.0.3 +version = 1.0.4 description = A generic retry package for Python long_description = file: README.md long_description_content_type = text/markdown diff --git a/setup.py b/setup.py index fd97580..a57de38 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='retry_plus', - version='1.0.3', + version='1.0.4', description='A generic retry package for Python', long_description=long_description, long_description_content_type='text/markdown',