Python decorator with Flask

10,682

Solution 1

You have two wrapper functions where you only need one. Notice that each wrapper function takes one argument. This should be a clue as to what is happening.

You have:

def decorator(take_a_function):
    def wrapper1(take_a_function):
        def wrapper2(*takes_multiple_arguments):
           # do stuff
           return take_a_function(*takes_multiple_arguments)

        return wrapper2
    return wrapper1

When you decorate a function with it:

@decorator
def my_function(*takes_multiple_arguments):
   pass

This is equivalent to:

def my_function(*takes_multiple_arguments):
   pass

my_function = decorator(my_function)

but doing decorator(my_function) returns wrapper1, which if you recall takes one argument, take_a_function. This is clearly not what you want. You want wrapper2 returned. As in your answer, the solution is to remove the outer wrapper(wrapper1):

from functools import wraps

def decorator(takes_a_function):
    @wraps(takes_a_function)
    def wrapper(*args, **kwargs):
        # logic here
        return takes_a_function(*args, **kwargs)

    return wrapper

Solution 2

Ok I solved this problem by reading this answer Route to view_func with same decorators "flask" given by @will-hart

I simply remove the def wrapper(f) and everything seems fine now. at leaset no grammar error.

from functools import wraps

def requires_admin(f):
    @wraps(f)
    def wrapped(*args, **kwargs):
        #if blah blah:
            #return blah blah
        return f(*args, **kwargs)
    return wrapped

Since I am pretty new to decorator and I dont know why. But hope this can help other ppl.

Share:
10,682

Related videos on Youtube

James King
Author by

James King

Updated on September 14, 2022

Comments

  • James King
    James King over 1 year

    I need to add a python decorator to Flask route functions, (basically I edited the code from here)

    def requires_admin(f):
        def wrapper(f):
            @wraps(f)
            def wrapped(*args, **kwargs):
                #if not admin:
                    #return render_template('error.html')
                return f(*args, **kwargs)
            return wrapped
        return wrapper
    

    and use it like this will be OK:

    @app.route('/admin/action')
    @requires_admin
    def AdminAction():
    #NO error if NO parameter
    

    But use it like this will have error:

    @app.route('/admin/action/<int:id>')
    @requires_admin
    def AdminAction(id):
    

    In Flask 0.10, I get errors like this (I just updated from Flask 0.9 to 0.10, and in Flask 0.9 there is no grammar error like this):

        @requires_admin
      File "/usr/local/lib/python2.6/dist-packages/Flask-0.10.1-py2.6.egg/flask/app.
    py", line 1013, in decorator
        self.add_url_rule(rule, endpoint, f, **options)
      File "/usr/local/lib/python2.6/dist-packages/Flask-0.10.1-py2.6.egg/flask/app.
    py", line 62, in wrapper_func
        return f(self, *args, **kwargs)
      File "/usr/local/lib/python2.6/dist-packages/Flask-0.10.1-py2.6.egg/flask/app.
    py", line 984, in add_url_rule
        'existing endpoint function: %s' % endpoint)
    AssertionError: View function mapping is overwriting an existing endpoint functi
    on: wrapper
    

    I am pretty new to the decorator stuff, how do I correct this error?

  • loki
    loki about 10 years
    In general a decorator with no arguments besides the wrapped function requires just one inner function. In your first example requires_admin returns a function which in turn takes a second function. Two level decorators are for things like @app.route(extra_args) or @wraps(f).