Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# This decorator re-executes the decorated function when it raises an exception. It enables to control the exception
# classes that should trigger the re-execution, the maximum number of retries, the delay between retries, and set the
# execution of a preparation method before every retry. The delay is specfied by means of user-customizable functions.
#
# Delay functions should return a compute function with a single parameter, the number of retry. For instance:
# delay_linear(initial=0, increment=0):
# adds a constant delay of 0 seconds between retries
# delay_linear(initial=1, increment=0):
# adds a constant delay of 1 second between retries
# delay_linear(initial=1, increment=0.5, maximum=10):
# adds an increasing delay between retries, starting with 1 second, and incresing it linearly by steps of 0.5
# seconds, up to 10 seconds, every time an exception is caught within the current execution.
# E.g. 1.0, 1.5, 2.0, 2.5, ..., 10.0, 10.0, 10.0, ...
# delay_exponential(initial=1, increment=1): adds a constant delay of 1 second between retries
# delay_exponential(initial=1, increment=2, maximum=10):
# adds an increasing delay between retries, starting with 1 second, and incresing it exponentially by steps of 2
# seconds, up to 10 seconds, every time an exception is caught within the current execution.
# E.g. 1.0, 2.0, 4.0, 8.0, 10.0, 10.0, 10.0, ...
# Arguments:
# - exceptions: defines the set of exception classes to be catched for reconnection. Others are re-raised.
# By default all exceptions are re-raised.
# - max_retries: defines the maximum number of retries acceptable before giving up. By default, 0 retries are executed.
# - delay_function: defines the delay computation method to be used. By default, delay_linear with a fixed delay of 0.1
# seconds is used.
# - prepare_method_name: if not None, defines the name of the preparation method within the same class to be executed
# when an exception in exceptions is caught, and before running the next retry. By default, is None, meaning that no
# method is executed.
# - prepare_method_args: defines the list of positional arguments to be provided to the preparation method. If no
# preparation method is specified, the argument is silently ignored. By default, an empty list is defined.
# - prepare_method_kwargs: defines the dictionary of keyword arguments to be provided to the preparation method. If no
# preparation method is specified, the argument is silently ignored. By default, an empty dictionary is defined.
import time
def delay_linear(initial=0, increment=0, maximum=None):
def compute(num_try):
delay = initial + (num_try - 1) * increment
if maximum is not None: delay = max(delay, maximum)
return delay
return compute
def delay_exponential(initial=1, increment=1, maximum=None):
def compute(num_try):
delay = initial * (num_try - 1) ^ increment
if maximum is not None: delay = max(delay, maximum)
return delay
return compute
def retry(exceptions=set(), max_retries=0, delay_function=delay_linear(initial=0, increment=0),
prepare_method_name=None, prepare_method_args=[], prepare_method_kwargs={}):
def _reconnect(func):
def wrapper(self, *args, **kwargs):
if prepare_method_name is not None:
prepare_method = getattr(self, prepare_method_name, None)
if prepare_method is None: raise Exception('Prepare Method ({}) not found'.format(prepare_method_name))
num_try, given_up = 0, False
while not given_up:
try:
return func(self, *args, **kwargs)
except Exception as e:
if not isinstance(e, tuple(exceptions)): raise
num_try += 1
given_up = num_try > max_retries
if given_up: raise Exception('Giving up... {} tries failed'.format(max_retries))
if delay_function is not None: time.sleep(delay_function(num_try))
if prepare_method_name is not None: prepare_method(*prepare_method_args, **prepare_method_kwargs)
return(wrapper)
return(_reconnect)