Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Python decorators to retry request

I have multiple functions in my script which does a REST API api requests.As i need to handle the error scenarios i have put a retry mechanism as below.

no_of_retries = 3
def check_status():
    for i in range(0,no_of_retries):
        url = "http://something/something"
        try:
            result = requests.get(url, auth=HTTPBasicAuth(COMMON_USERNAME, COMMON_PASSWORD)).json()
            if 'error' not in result:
                return result
            else:
                continue
        except Exception as e:
            continue
    return None

I have several different methods which does similar operation. How can we do it better way to avoid duplication may be using decorators.

like image 274
PGS Avatar asked May 09 '18 05:05

PGS


People also ask

Can a function have 2 decorators?

Python allows us to implement more than one decorator to a function. It makes decorators useful for reusable building blocks as it accumulates several effects together. It is also known as nested decorators in Python.

What are Python decorators used for?

A decorator in Python is a function that takes another function as its argument, and returns yet another function . Decorators can be extremely useful as they allow the extension of an existing function, without any modification to the original function source code.


2 Answers

if you do not mind installing a library you could use the tenacity (github.com/jd/tenacity) module. one of their examples:

import random
from tenacity import retry, stop_after_attempt

# @retry  # retry forever
@retry(stop=stop_after_attempt(3))
def do_something_unreliable():
    if random.randint(0, 10) > 1:
        raise IOError("Broken sauce, everything is hosed!!!111one")
    else:
        return "Awesome sauce!"

print(do_something_unreliable())

this also allows you to specify the number of tries or seconds you want to keep retrying.

for your case this might look something like this (not tested!):

@retry(stop=stop_after_attempt(3))
def retry_get():
    result = requests.get(
            url, auth=HTTPBasicAuth(COMMON_USERNAME, COMMON_PASSWORD)).json()
    if 'error' not in result:
        raise RequestException(result)
like image 163
hiro protagonist Avatar answered Oct 02 '22 09:10

hiro protagonist


You can use a decorator like this and handle your own exception.

def retry(times, exceptions):
    """
    Retry Decorator
    Retries the wrapped function/method `times` times if the exceptions listed
    in ``exceptions`` are thrown
    :param times: The number of times to repeat the wrapped function/method
    :type times: Int
    :param Exceptions: Lists of exceptions that trigger a retry attempt
    :type Exceptions: Tuple of Exceptions
    """
    def decorator(func):
        def newfn(*args, **kwargs):
            attempt = 0
            while attempt < times:
                try:
                    return func(*args, **kwargs)
                except exceptions:
                    print(
                        'Exception thrown when attempting to run %s, attempt '
                        '%d of %d' % (func, attempt, times)
                    )
                    attempt += 1
            return func(*args, **kwargs)
        return newfn
    return decorator

@retry(times=3, exceptions=(ValueError, TypeError))
def foo1():
    print('Some code here ....')
    print('Oh no, we have exception')
    raise ValueError('Some error')

foo1()
like image 38
mrkiril Avatar answered Oct 02 '22 07:10

mrkiril