简体   繁体   中英

Inconsistent scope of parameters in decorator

I've written a decorator function for a Flask user permission system. When I try to hit a view decorated with it, I get an UnboundLocalError on the user parameter. Here's the decorator function:

def user_is(role, user=None):
    """
    Takes an role (a string name of either a role or an ability) and returns the function if the user has that role
    """
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            from .models import Role
            desired_role = Role.query.filter_by(
                name=role).first()
            if not user:
                try:
                    from flast.ext.login import current_user as user
                except ImportError:
                    raise ImportError(
                        'User argument not passed and Flask-Login current_user could not be imported.')
            if desired_role in user.roles:
                return func(*args, **kwargs)
            else:
                # Make this do someting way better.
                return "You do not have access"
        return inner
    return wrapper

The traceback indicates user is undefined for if not user: . I'm not sure how this can be the case. It's my understanding that, if user doesn't exist in the scope for the inner function, Python will go out level-by-level through the nested functions until it finds it. This means I should only get the UnboundLocalError if user is undefined in the function, all functions wrapping it, and globally. This is plainly not the case here.

Another source of confusion is that I'm able to see using the Werkzeug debug console that my other parameter is defined in this scope. How can one parameter be defined and the other parameter, which is taken in by the decorator function, be undefined at the same point in the program flow? I thought maybe this was a quirk that only affected parameters with a default value, so I switched it over to a required parameter and manually passed in None , but this still produced the error?

Why is user out of scope when the other parameter is in scope? How can I fix this decorator?

You are importing user in the inner function:

from flast.ext.login import current_user as user

That makes user a local in your inner function and will never be looked up as a closure.

Do not rename the import; instead, assign user to current_user .

def user_is(role, user=None):
    """
    Takes an role (a string name of either a role or an ability) and returns the function if the user has that role
    """
    def wrapper(func):
        @wraps(func)
        def inner(*args, **kwargs):
            from .models import Role
            desired_role = Role.query.filter_by(
                name=role).first()
            if not user:
                try:
                    from flast.ext.login import current_user
                except ImportError:
                    raise ImportError(
                        'User argument not passed and Flask-Login current_user could not be imported.')
            else:
                current_user = user
            if desired_role in current_user.roles:
                return func(*args, **kwargs)
            else:
                # Make this do someting way better.
                return "You do not have access"
        return inner
    return wrapper

or perhaps name the outer user to default_user or similar.

Now user is never assigned to in the inner function, and remains a non-local reference to the outer scope.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM