装饰器中参数的范围不一致

时间:2013-09-21 14:50:39

标签: python flask scope decorator python-decorators

我为Flask用户权限系统编写了一个装饰器函数。当我尝试点击用它装饰的视图时,我在UnboundLocalError参数上得到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 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

回溯表示user未定义if not user:。我不确定这是怎么回事。我的理解是,如果内部函数的作用域中不存在user,Python将通过嵌套函数逐级出去,直到找到它为止。这意味着如果UnboundLocalError在函数中未定义,包含它的所有函数和全局函数,我应该只获取user。这显然不是这种情况。

另一个混乱的原因是我能够看到使用Werkzeug调试控制台,我的其他参数 在此范围内定义。如何定义一个参数,而另一个参数(由装饰器函数接收)在程序流程的同一点是否未定义?我想也许这是一个怪癖只会影响默认值的参数,所以我把它切换到一个必需的参数并手动传入None,但这仍然产生了错误?

当其他参数在范围内时,为什么user超出范围?我该如何修复这个装饰器?

1 个答案:

答案 0 :(得分:3)

您正在内部函数中导入user

from flast.ext.login import current_user as user

这使得user成为你内在函数中的一个局部函数,永远不会被视为一个闭包。

不要重命名导入;相反,请将user分配给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

或者可能将外部user命名为default_user或类似。

现在user永远不会在内部函数中赋值,并且仍然是对外部作用域的非本地引用。