PLPeeters
PLPeeters

Reputation: 1040

Decorator isn't getting arguments

I'm trying to add an access_rights decorator to my Bottle app to check permissions when accessing a route. However, it's not getting the decorated function's arguments, which causes an error when trying to call my decorated function again.

Here's an example of code using the decorator:

@route('/users')
@access_rights({'POST': ['admin']})
def users(user):
    pass

The user parameter comes from a Bottle plugin I wrote that gets the user from the token passed with the request. This is my current decorator:

def access_rights(permissions):
    def decorator(f):    
        def wrapper(*args, **kwargs):
            # Check permissions rights here (not implemented yet)

            return f(*args, **kwargs)

        return wrapper

    return decorator

With this, I get TypeError: users() takes exactly 1 argument (0 given) when doing a GET /users, meaning args and kwargs were both empty. However, when I change the decorator as follows, it works:

def access_rights(permissions):
    def decorator(f):  
        return f

    return decorator

I haven't worked with decorators a lot, but from my understanding, both implementations above should call the users function with its original parameters, yet for some reason the first one doesn't get the parameters. Why is that?

Upvotes: 1

Views: 1391

Answers (1)

ron rothman
ron rothman

Reputation: 18168

Your route handler, function users, expects one parameter.

But your decorator, access_rights, which you wrap around users, isn't passing a user param; it's just passing any params that it received (and, in this case, there aren't any, hence the "0 given" part of the error message).

An example should help clarify. Here's a small but complete working app, based on your original code:

from bottle import route, Bottle

app = Bottle()

def access_rights(permissions):
    def decorator(f):
        def wrapper(*args, **kwargs):
            # Check permissions rights here (not implemented yet)

            the_user = 'ron'  # hard-coded for this example

            return f(the_user, *args, **kwargs)

        return wrapper

    return decorator


@app.route('/users')
@access_rights({'POST': ['admin']})
def users(user):
    return ['hello, {}'.format(user)]


app.run(host='127.0.0.1', port=8080, debug=True)

Note that the only substantial change I made was to have access_rights actually pass a user param on down the line. (How it determines the user is, naturally, up to you--presumably it's the "not implemented yet" part that you called out in your comment).

Hope that helps!

Upvotes: 0

Related Questions