I'm trying to write a decorator to repeat an erroring function N times with increasingly sleeping times in between. This is my attempt so far:
def exponential_backoff(seconds=10, attempts=10):
def our_decorator(func):
def function_wrapper(*args, **kwargs):
for s in range(0, seconds*attempts, attempts):
sleep(s)
try:
return func(*args, **kwargs)
except Exception as e:
print(e)
return function_wrapper
return our_decorator
@exponential_backoff
def test():
for a in range(100):
if a - random.randint(0,1) == 0:
print('success count: {}'.format(a))
pass
else:
print('error count {}'.format(a))
'a' + 1
test()
I keep getting the error:
TypeError: our_decorator() missing 1 required positional argument: 'func'
The Python "TypeError: missing 1 required positional argument: 'self'" occurs when we call a method on the class instead of on an instance of the class. To solve the error, instantiate the class first and call the method on the instance, e.g. emp1.get_name (). Here is an example of how the error occurs.
The “missing 1 required positional argument: ‘self’” error can occur when you incorrectly instantiate a class. Consider the following code: While this code is similar to our solution from the last example, it is incorrect. This is because we have not added any parenthesis after the word Hero.
missing 1 required positional argument: ‘self’ Positional arguments refer to data that is passed into a function. In a class, every function must be given the value “ self ”. The value of “self” is similar to “this” in JavaScript. “self” represents the data stored in an object of a class.
What is a "positional argument" in Python programming? Short answer: A positional argument is any argument that's not supplied as a key=value pair. To understand what that means, unfortunately, is somewhat involved. The term "argument" is used somewhat imprecisely throughout the programming community and especially in Python documentation.
Understand what decorator is:
@exponential_backoff
def test():
pass
equals to:
def test():
pass
test = exponential_backoff(test)
In this case, test
is def our_decorator(func):
. That's why you get TypeError
when calling test()
.
So further:
@exponential_backoff()
def test():
pass
equals to:
def test():
pass
test = exponential_backoff()(test)
In this case, now test
is what you need.
Further, functools.wraps
helps you to copy all properties of original function to decorated function. Such as function's name or docstring:
from functools import wraps
def exponential_backoff(func):
# @wraps(func)
def function_wrapper(*args, **kwargs):
pass
return function_wrapper
@exponential_backoff
def test():
pass
print(test) # <function exponential_backoff.<locals>.function_wrapper at 0x7fcc343a4268>
# uncomment `@wraps(func)` line:
print(test) # <function test at 0x7fcc343a4400>
You should be using:
@exponential_backoff()
def test():
...
The overall decorator is not designed to have arguments be optional, so you must provide ()
when using it.
If want an example of how to make decorator allow argument list be optional, see:
You might also consider using the wrapt package to make your decorators easier and more robust.
Either you go for the solution provided by @Graham Dumpleton or you can just modify your decorator like so:
from functools import wraps, partial
def exponential_backoff(func=None, seconds=10, attempts=10):
if func is None:
return partial(exponential_backoff, seconds=seconds, attempts=attempts)
@wraps(func)
def function_wrapper(*args, **kwargs):
for s in range(0, seconds*attempts, attempts):
sleep(s)
try:
return func(*args, **kwargs)
except Exception as e:
print(e)
return function_wrapper
@exponential_backoff
def test():
for a in range(100):
if a - random.randint(0,1) == 0:
print('success count: {}'.format(a))
pass
else:
print('error count {}'.format(a))
'a' + 1
test()
EDIT My answer was not entirely correct, please see @GrahamDumpleton's answer which shows how to make my attempt of a solution viable (i.e. this link). Fixed it now, thank you @GrahamDumpleton !
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With