Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonic way of retry running a function [duplicate]

How a Python Professinal would retry running a function that will request a web service(the webservice sometimes fails)

The function:

def some_request(uri):
    try:
        req = requests.post('http://someuri.com', {'test': 'yes'})
    except Exception as e:
        return False
    return {'id': req.json()['value'], 'other': req.json()['other']}

You handle the retry with a while or other python idiom?

Give me a clue on how to do it the right way.

like image 625
André Avatar asked Nov 28 '22 21:11

André


2 Answers

Define retry utility:

# Retry utility   
# set logging for `retry` channel
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('retry')

# Define Exception class for retry
class RetryException(Exception):
    u_str = "Exception ({}) raised after {} tries."

    def __init__(self, exp, max_retry):
        self.exp = exp
        self.max_retry = max_retry    
    def __unicode__(self):
        return self.u_str.format(self.exp, self.max_retry)
    def __str__(self):
        return self.__unicode__()

# Define retry util function
def retry_func(func, max_retry=10):
    """
    @param func: The function that needs to be retry
    @param max_retry: Maximum retry of `func` function, default is `10`
    @return: func
    @raise: RetryException if retries exceeded than max_retry
    """
    for retry in range(1, max_retry + 1):
        try:
            return func()
        except Exception, e:
            logger.info('Failed to call {}, in retry({}/{})'.format(func.func,
                                                           retry, max_retry))
    else:
        raise RetryException(e, max_retry)

Use it:

from functools import partial
import requests

def some_request(uri, post):
    req = requests.post(uri, post)
    return req

uri = 'http://someuri.com'
post = {'test': 'yes'}

try:
    retry_func(partial(some_request, uri, post), max_retry=3)
except RetryException, e:
    print(e)

Output:

INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(1/3)
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(2/3)
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(3/3)
Exception (HTTPConnectionPool(host='someuri.com', port=80): Max retries exceeded with url: / (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)) raised after 3 tries.
like image 70
Omid Raha Avatar answered Dec 28 '22 11:12

Omid Raha


The pattern I use retries a limited number of times, sleeping for a short, exponentially increasing time interval between each attempt, and finally raising the last-seen exception after repeated failure:

def wrappedRequest( self, uri, args ):
    lastException = None 
    interval = 1.0
    for _ in range(3):
        try:
            result = requests.post(uri, args)
            return result
        except Exception as e:
            lastException = e

        time.sleep(interval)
        interval *= 2.0
    raise lastException 

(I actually specifically check for HTTPError, URLError, IOError, and BadStatusLine exceptions rather than the broad net of Exception.)

like image 39
Russell Borogove Avatar answered Dec 28 '22 11:12

Russell Borogove