2013-09-21 2 views
0

Я написал функцию декоратора для системы пользовательских разрешений 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 не входит в сферу применения, когда другой параметр находится в области? Как я могу исправить этот декоратор?

ответ

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 никогда не назначается во внутренней функции и остается нелокальной ссылкой на внешнюю область.

+0

Это имеет смысл. Один последующий вопрос, который поможет мне понять: как импорт влияет на область «пользователь», когда исключение возникает до того, как выполняется импорт? – raddevon

+1

Компилятор перечисляет все назначения в функции; все, что присваивается (включая импорт), помечено как локальное, автоматически. Все остальное является свободной переменной. Если у родительской области есть соответствующие локальные, свободные переменные будут смотреться с закрытий, иначе они являются глобальными. Вы можете использовать ключевое слово 'global', чтобы переопределить это (пометьте локальный как глобальный), Python 3 также добавляет ключевое слово' nonlocal'. * Где * происходит импорт или назначение, не имеет значения. Любая ссылка на имя перед присвоением или импортом вызывает исключение 'UnboundLocal'. –