Pythonic重试方式运行函数[重复]

时间:2022-03-16 22:08:24

This question already has an answer here:

这个问题在这里已有答案:

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

Python Professinal如何重试运行将请求Web服务的函数(Web服务有时会失败)

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?

你用一段时间或其他python成语处理重试?

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

给我一个如何以正确的方式做到这一点的线索。

4 个解决方案

#1


9  

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.

#2


4  

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.)

(我实际上专门检查HTTPError,URLError,IOError和BadStatusLine异常,而不是广泛的异常网络。)

#3


3  

this definitely calls for a decorator.

这绝对需要一个装饰。

you'd probably want something like:

你可能想要这样的东西:

import functools
def retry(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        retExc = None
        for i in xrange(4):
            try:
                return func(*args, **kwargs)
            except Exception, e:
                retExc = e
        raise retExc
    return wrapper

and then to run your code, just use:

然后运行你的代码,只需使用:

@retry
def some_request(...):
    ...

you could make it fancier by adding in a little sleep, adding a numTimes to retry, etc, but this will do it.

你可以通过添加一点睡眠,添加numTimes来重试等来让它变得更加漂亮,但是这样做会。

#4


2  

Here's a generalized decorator that lets you specify the number of retries and also the exception type to ignore:

这是一个通用的装饰器,它允许您指定重试次数以及要忽略的异常类型:

from functools import wraps
def retry(count=5, exc_type=Exception):
    def decorator(func):
        @wraps(func):
        def result(*args, **kwargs):
            for _ in range(count):
                try:
                    return func(*args, **kwargs)
                except exc_type:
                    pass
                raise
         return result
     return decorator

Use it like this:

像这样用它:

@retry(count=10, exc_type=IOError)
def read_file():
    pass # do something

#1


9  

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.

#2


4  

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.)

(我实际上专门检查HTTPError,URLError,IOError和BadStatusLine异常,而不是广泛的异常网络。)

#3


3  

this definitely calls for a decorator.

这绝对需要一个装饰。

you'd probably want something like:

你可能想要这样的东西:

import functools
def retry(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        retExc = None
        for i in xrange(4):
            try:
                return func(*args, **kwargs)
            except Exception, e:
                retExc = e
        raise retExc
    return wrapper

and then to run your code, just use:

然后运行你的代码,只需使用:

@retry
def some_request(...):
    ...

you could make it fancier by adding in a little sleep, adding a numTimes to retry, etc, but this will do it.

你可以通过添加一点睡眠,添加numTimes来重试等来让它变得更加漂亮,但是这样做会。

#4


2  

Here's a generalized decorator that lets you specify the number of retries and also the exception type to ignore:

这是一个通用的装饰器,它允许您指定重试次数以及要忽略的异常类型:

from functools import wraps
def retry(count=5, exc_type=Exception):
    def decorator(func):
        @wraps(func):
        def result(*args, **kwargs):
            for _ in range(count):
                try:
                    return func(*args, **kwargs)
                except exc_type:
                    pass
                raise
         return result
     return decorator

Use it like this:

像这样用它:

@retry(count=10, exc_type=IOError)
def read_file():
    pass # do something