How to Retry a Loop in Python

Olorunfemi Akinlua Feb 12, 2024
  1. Retry a Loop Action in Python Using the tenacity Library retry Decorator
  2. Retry a Loop Action in Python Using the backoff Library @backoff.on_exception Decorator
  3. Retry a Loop Action in Python Using a Custom retry Decorator
  4. Conclusion
How to Retry a Loop in Python

In Python programming, loops are fundamental constructs used to iterate over sequences of data or perform repetitive tasks. However, sometimes, these tasks may fail due to transient errors such as network issues, API timeouts, or other external factors.

In order to enhance the reliability of your code in the face of such uncertainties, it’s essential to implement retry mechanisms for loop actions.

In this article, we explore three approaches for retrying loop actions in Python: the retry decorator from the tenacity library, the @backoff.on_exception decorator from the backoff library, and a custom retry decorator constructed as a higher-order function.

Retry a Loop Action in Python Using the tenacity Library retry Decorator

The tenacity library in Python provides a convenient retry decorator that simplifies the process of retrying a loop action until success. The @retry decorator enables a function to automatically retry in case of specified exceptions.

To begin, install the tenacity library using the following pip command:

pip install tenacity

Now, let’s explore the syntax and usage of the @retry decorator.

from tenacity import retry

@retry
def your_function():
    # Your action here

The @retry decorator takes care of the retry logic, allowing you to focus on the core functionality of your function. You can customize the retry behavior by specifying parameters like wait, stop, and retry, tailoring it to your specific use case.

Consider a scenario where we have a function generateRandomly that generates random numbers within a user-defined range. However, the function raises a ValueError if the generated number exceeds 20.

We want to use the @retry decorator to repeatedly attempt the generation until a valid number is produced.

import random
from tenacity import retry


@retry
def generateRandomly(start, end):
    generateNum = random.randint(start, end)
    if generateNum > 20:
        print("Tried")
        raise ValueError("Number generated isn't within range")
    else:
        return generateNum


userStartInput = int(input("Enter Start Number: "))
userEndInput = int(input("Enter End Number: "))

for i in range(0, 20):
    print(generateRandomly(userStartInput, userEndInput))

In this example, we define the generateRandomly function and decorate it with @retry. The function generates a random number and raises a ValueError if the number is greater than 20.

The @retry decorator automatically retries the function until it successfully produces a number within the specified range.

We then take user input for the start and end numbers and run the generateRandomly function in a loop. The output includes the additional "Tried" message, indicating how many attempts were made before obtaining a valid result.

Code Output:

Retry a Loop Action in Python Using the tenacity Library - Output

In this example, the generateRandomly function is retried until it produces a number less than or equal to 20. The "Tried" message provides visibility into the number of retry attempts made during the process.

Retry a Loop Action in Python Using the backoff Library @backoff.on_exception Decorator

In addition to the tenacity library, the backoff library provides another approach to implementing retry mechanisms in Python.

The backoff library is a Python library that provides a flexible and configurable way to implement exponential backoff strategies for retrying functions. Exponential backoff is a common approach in handling transient errors by gradually increasing the time between successive retries.

Before we get started, let’s make sure you have the backoff library installed. You can install it using the following pip command:

pip install backoff

The backoff library provides the @backoff.on_exception decorator, which can be applied to a function to introduce retry behavior. The basic syntax is as follows:

import backoff

@backoff.on_exception(backoff.expo, ExceptionType, max_tries=3)
def your_function():
    # Your action here

Here, backoff.expo signifies the use of exponential backoff, ExceptionType specifies the exception(s) that trigger a retry, and max_tries defines the maximum number of attempts.

Let’s consider a scenario similar to the previous example, where we have a function generateRandomly. This function generates random numbers within a user-defined range and raises a ValueError if the generated number exceeds 20.

We’ll use the @backoff.on_exception decorator to implement retry logic.

import random
import backoff


@backoff.on_exception(backoff.expo, ValueError, max_tries=3)
def generateRandomly(start, end):
    generateNum = random.randint(start, end)
    if generateNum > 20:
        print("Tried")
        raise ValueError("Number generated isn't within range")
    else:
        return generateNum


userStartInput = int(input("Enter Start Number: "))
userEndInput = int(input("Enter End Number: "))

for i in range(0, 10):
    print(generateRandomly(userStartInput, userEndInput))

In this example, the generateRandomly function is decorated with @backoff.on_exception. This means that if the function raises a ValueError, the decorator will automatically introduce an exponential backoff strategy, allowing the function to be retried up to a maximum of three times.

The function generates a random number, raising an exception if the number is greater than 20.

User input is taken for the start and end numbers, and the generateRandomly function is executed in a loop. The output includes the additional "Tried" message, indicating how many attempts were made before obtaining a valid result.

Code Output:

Retry a Loop Action in Python Using the backoff Library - Output

The backoff library provides various parameters that you can customize according to your requirements. Some common parameters include factor (the exponential factor), max_time (maximum backoff time), and max_tries (maximum number of retry attempts).

Retry a Loop Action in Python Using a Custom retry Decorator

A custom retry decorator is another powerful tool that simplifies the implementation of retry logic, making it an elegant solution for scenarios where loop actions may fail temporarily.

The retry decorator is created using a combination of Python’s built-in functools.wraps and time.sleep functions. It takes three optional parameters:

  • Max_retries: The maximum number of retry attempts.
  • Delay: The delay between retries.
  • Exceptions: A tuple of exception types to catch.

The decorator wraps the target function or loop action, attempting to execute it until either success is achieved or the maximum number of retries is reached.

Here is the basic structure of the retry decorator:

from functools import wraps
import time


def retry(max_retries=3, delay=1, exceptions=(Exception,)):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(max_retries):
                try:
                    result = func(*args, **kwargs)
                    return result
                except exceptions as e:
                    print(f"Retry: {e}")
                    time.sleep(delay)
            raise RuntimeError(f"Failed after {max_retries} retries")

        return wrapper

    return decorator

Let’s illustrate the usage of the retry decorator with a simple example. Consider a function named example_function that represents a loop action. We want to retry this function in case of a ValueError and limit the retries to 5 attempts with a 2-second delay between retries.

@retry(max_retries=5, delay=2, exceptions=(ValueError,))
def example_function():
    # Simulating a loop action that may raise a ValueError
    import random

    if random.random() < 0.3:
        raise ValueError("Random error occurred")
    print("Loop action successful")


# Call the decorated function
result = example_function()
print("Final Result:", result)

The retry decorator is a nested function. The outer function (retry) takes the retry parameters as arguments and returns the inner function (decorator).

The inner function (decorator) takes the target function (func) and returns the wrapper function. The wrapper function is the one that incorporates the retry logic.

Within the wrapper function, a loop iterates for a maximum of max_retries attempts. The func is called within a try block, and if it succeeds, the result is returned.

If an exception of the specified type occurs, it’s caught in the except block. The decorator then prints a retry message, sleeps for the specified delay, and continues the loop.

If the loop exhausts without successful execution, a RuntimeError is raised, indicating that the action failed after the maximum number of retries.

The output will vary due to the random nature of the simulated loop action. However, it will demonstrate the retry attempts and the final result. For example:

Retry a Loop Action in Python Using the Custom retry Decorator - Output

In this example, the example_function completes after two retry attempts due to the random errors. Adjust the parameters of the decorator according to your specific requirements to achieve the desired retry behavior.

Conclusion

Retrying loop actions in Python is a valuable practice for building resilient and fault-tolerant applications. The tenacity and backoff libraries, along with custom decorators, provide versatile solutions catering to different needs.

Choosing the right approach depends on the specific requirements of your application, offering a balance between simplicity and customization. Experiment with these methods to enhance the resilience of your Python applications in the face of transient errors.

Olorunfemi Akinlua avatar Olorunfemi Akinlua avatar

Olorunfemi is a lover of technology and computers. In addition, I write technology and coding content for developers and hobbyists. When not working, I learn to design, among other things.

LinkedIn

Related Article - Python Loop