Created
October 15, 2017 17:00
-
-
Save iamFIREcracker/fecdd30a1ca2bf0e84a7fdec06f3fd79 to your computer and use it in GitHub Desktop.
Python decorator to recover from failed function calls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""Script showing how to use decorators to recover from failed function calls | |
""" | |
import sys | |
import time | |
def backoff1(step): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
while not func(*args, **kwargs): | |
print "Sleeping for {0} seconds".format(delta) | |
time.sleep(delta) | |
delta *= 2 | |
return wrapper2 | |
return wrapper1 | |
def backoff2(step, linear): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
exponential = True | |
while not func(*args, **kwargs): | |
print "Sleeping for {0} seconds".format(delta) | |
time.sleep(delta) | |
if not exponential: | |
delta += step | |
else: | |
new_delta = delta * 2 | |
if new_delta < linear: | |
delta = new_delta | |
else: | |
delta = linear | |
exponential = False | |
return wrapper2 | |
return wrapper1 | |
class ExecutionLimitException(Exception): | |
"""Exception raised if a backoffed function keep failing and failing.""" | |
def __init__(self, func, *args, **kwargs): | |
""" | |
Keywords: | |
func failed function. | |
args positional arguments passed to the function. | |
kwargs named arguments passed to the function. | |
""" | |
super(ExecutionLimitException, self).__init__( | |
"Function: {0} Arguments: {1} {2}".format( | |
func.func_name, args, kwargs)) | |
self.func = func | |
self.args_ = args # self.args seems to be used by someonelse... | |
self.kwargs = kwargs | |
def backoff3(step, linear, limit): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
exponential = True | |
left = limit # prevent UnboundLocalError exception | |
while not func(*args, **kwargs): | |
if left is not None: | |
left -= 1 | |
if not left: | |
raise ExecutionLimitException(func, *args, **kwargs) | |
print "Sleeping for {0} seconds".format(delta) | |
time.sleep(delta) | |
if not exponential: | |
delta += step | |
else: | |
new_delta = delta * 2 | |
if new_delta < linear: | |
delta = new_delta | |
else: | |
delta = linear | |
exponential = False | |
return wrapper2 | |
return wrapper1 | |
def _evalfunc(v): | |
"""Return True if and only if input argument is True.""" | |
return v == True | |
def backoff4(step, linear, limit, evalfunc=_evalfunc): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
exponential = True | |
left = limit # prevent UnboundLocalError exception | |
while not evalfunc(func(*args, **kwargs)): | |
if left is not None: | |
left -= 1 | |
if not left: | |
raise ExecutionLimitException(func, *args, **kwargs) | |
print "Sleeping for {0} seconds".format(delta) | |
time.sleep(delta) | |
if not exponential: | |
delta += step | |
else: | |
new_delta = delta * 2 | |
if new_delta < linear: | |
delta = new_delta | |
else: | |
delta = linear | |
exponential = False | |
return wrapper2 | |
return wrapper1 | |
def _waitfunc(delta): | |
"""Sleep for the given period of time, then return.""" | |
import time as _time | |
_time.sleep(delta) | |
def backoff5(step, linear, limit, evalfunc=_evalfunc, waitfunc=_waitfunc): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
exponential = True | |
left = limit | |
while not evalfunc(func(*args, **kwargs)): | |
if left is not None: | |
left -= 1 | |
if not left: | |
raise ExecutionLimitException(func, *args, **kwargs) | |
print "Sleeping for {0} seconds".format(delta) | |
waitfunc(delta) | |
if not exponential: | |
delta += step | |
else: | |
new_delta = delta * 2 | |
if new_delta < linear: | |
delta = new_delta | |
else: | |
delta = linear | |
exponential = False | |
return wrapper2 | |
return wrapper1 | |
def backoff(step, linear, limit, evalfunc=_evalfunc, waitfunc=_waitfunc): | |
def wrapper1(func): | |
def wrapper2(*args, **kwargs): | |
delta = step | |
exponential = True | |
left = limit | |
while not evalfunc(func(*args, **kwargs)): | |
if left is not None: | |
left -= 1 | |
if not left: | |
raise ExecutionLimitException(func, *args, **kwargs) | |
waitfunc(delta) | |
if not exponential: | |
delta += step | |
else: | |
new_delta = delta * 2 | |
if new_delta < linear: | |
delta = new_delta | |
else: | |
delta = linear | |
exponential = False | |
return wrapper2 | |
return wrapper1 | |
class DecoratedFunction(object): | |
def __init__(self, func, dec, *args, **kwargs): | |
self._func = func | |
self._dec = dec(*args, **kwargs) | |
def __call__(self, *args, **kwargs): | |
self._dec(self._func)(*args, **kwargs) | |
def noman(): | |
"""Function which always return False.""" | |
return False | |
def waitnprint(delta): | |
print "Sleeping for {0} seconds".format(delta) | |
time.sleep(delta) | |
def testwait(delta): | |
print "Sleeping for {0} seconds".format(delta) | |
def main(args): | |
def lognsleep(delta): | |
print "Sleeping for {0} seconds".format(delta) | |
#time.sleep(delta) | |
DecoratedFunction(noman, backoff5, step=1, linear=60, limit=10, | |
waitfunc=lognsleep)() | |
if __name__ == '__main__': | |
main(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment