Decorator chain

I am creating several decorators based on Django @permission_required

decorator. These decorators will simply call decor_required decorator with different parameters.

For example:

def permission_required_ajax(perm):
    return permission_required(perm, raise_exception=True)

      

This works well and I have several examples like this.

The problem is that my decorator takes no arguments at all:

def special_permission():
    return permission_required_ajax('special_permission')

      

This does not work. When I decorate the function with a decorator @special_permission

, I get the following error:

TypeError: special_permission () takes 0 positional arguments, but 1 is given

What am I missing?

+3


source to share


2 answers


You still need to call your factory decorator:

@special_permission()
def function():
    # ...

      

Whatever the expression after the sign @

is used as a decorator; the decorator is called, passing in the function to decorate.

In other words, a decorator is syntactic sugar for:

def function():
    # ...
special_permission()(function)

      



If you leave the call ()

, the function is passed in instead of your shell.

Alternatively, let your decorator take this function and pass it directly to the decorator created by the call permission_required_ajax()

:

def special_permission(func):
    return permission_required_ajax('special_permission')(func)

      

and then use it without calling (it's a decorator now, not a factory decorator):

@special_permission  # no () call now
def function():
    # ...

      

+5


source


How decorator parameters work

When the decorator has no parameters, it gets the passed function / class. When it has parameters, they are first called with parameters and then whatever is returned is called with function / class decoration.

Repeat:

# Case 1, no arguments

@foo
def bar(): pass

      

The function foo

will be called by passing the function bar

, and whatever is returned is then bound to the name bar

.

# Case 2

@foo(1, 2, 3)
def bar(): pass

      



The function foo

is called passing 1, 2, and 3 as parameters. What it foo

returns is then called by passing the function bar

as an argument, and the return value is bound to the name bar

.

For your case

The solution might be to just add parentheses to the decoder call special_permission

...

@special_permission()  # <-- note the parenthesis
def bar(): ...

      

Alternatively, if you don't like adding parentheses to using a decorator, this can be implemented as:

def special_permission(f):
    return permission_required_ajax('special_permission')(f)

      

+1


source







All Articles