Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python decorator TypeError missing 1 required positional argument

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'
like image 883
Blue Moon Avatar asked Aug 17 '18 09:08

Blue Moon


People also ask

How to fix Python TypeError “missing 1 required positional argument “self”?

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.

Why is my positional argument missing from my class?

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.

What is the required positional argument in JavaScript?

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?

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.


3 Answers

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>
like image 99
Sraw Avatar answered Oct 19 '22 08:10

Sraw


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:

  • https://wrapt.readthedocs.io/en/latest/decorators.html#decorators-with-optional-arguments

You might also consider using the wrapt package to make your decorators easier and more robust.

like image 20
Graham Dumpleton Avatar answered Oct 19 '22 06:10

Graham Dumpleton


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 !

like image 4
tahesse Avatar answered Oct 19 '22 08:10

tahesse