2013-09-10 5 views
0

У меня есть декоратор, который проверяет некоторые параметры и передает проверенный ключ к различным функциям:Как изменить параметры, хранящиеся в functools.wraps?

from functools import wraps 

ref validate(f): 
    @wraps(f) # This is to ensure docstrings are passed through the decorated function 
    def redirect_if_invalid(request, *args, **kwargs): 
     if request.valid == False: 
      return HttpResponseRedirect('/login') 
     else: 
      newkwargs = { 'key': request.key } 
     return f(request, *args, **newkwargs) 
return redirect_if_invalid 

Это используются некоторыми другими функциями:

@validate 
def genericHandler(request, key) 
    pass 

Я бы вызвать функцию так:

genericHandler(request) 

И декоратор генерирует «ключ» kwarg. Тем не менее, я хотел бы дополнительно пройти в ключе в какой-то другой точки, то есть вызов:

genericHandler(request, 'keydata') 

В настоящее время это дает мне ошибку:

TypeError: genericHandler() got multiple values for keyword argument 'key' 

Как я могу обойти это? Чтобы повторить, главная цель состоит в том, чтобы иметь возможность вызывать genericHandler() с необязательным параметром или без него, а декоратор генерирует параметр только в том случае, если он отсутствует.

До сих пор внутри декоратора я не могу понять, как определить, был ли передан параметр «ключ» или нет, потому что functools.wraps(), похоже, скрывает его.

+0

Почему вы говорите 'f unctools.wrap скрывает его? '' key "' заканчивается прямо там, в 'args', поэтому вы можете сделать' if len (args) == 0: ... '. Проблема в том, что он не знает, что такое позиция «ключ»? –

+0

Положите по-другому: какое поведение отличается, когда включена строка 'wraps (f)', а не когда это не так? –

+0

Да, этот декоратор используется для ряда функций, где «ключ» находится в разных положениях. – dragonx

ответ

1

Нет разумного способа сделать это, если вы хотите, чтобы подпись вашего обертки оставалась (request, *args, **kwargs). С другой стороны, похоже, что ваш декоратор уже предполагает, что обернутая функция принимает параметр key, поэтому почему бы не переписать обертку, чтобы взять ее? В этом случае становится тривиально проверять, было ли оно передано или нет.

def validate(f): 
    @wraps(f) 
    def redirect_if_invalid(request, key=None): 
     # do the validation 
     if key is None: 
      key = request.key 
     return f(request, key) 
    return redirect_if_invalid 

Вы можете добавить *args и **kwargs параметры обратно, если вы хотите, конечно.

+0

Я собираюсь попробовать это. Это неоптимально, потому что декоратор @validate используется для ряда функций, которые содержат множество списков аргументов, поэтому я думаю, что мне придется изменить все эти функции. Возможно, это не так уж плохо. – dragonx

+0

Это работает, за исключением случаев, когда украшенная функция f является обработчиком запросов django, а «ключ» не является параметром, который передается через обычную маршрутизацию django. – dragonx

0

Таким образом, лучшим способом для меня было явно передать kwargs как kwargs. Таким образом, декорированные функции должны быть на самом деле:

@validate 
def genericHandler(request, **kwargs) 
    key = kwargs.get('key') 
    pass 

Таким образом, я могу вызвать функцию или без аргументов:

genericHandler(request) 

или

genericHandler(request, **{ 'key' : key }) 

и фактический декорированные выглядит следующим образом:

def validate(f): 
    @wraps(f) # This is to ensure docstrings are passed through the decorated function 
    def redirect_if_invalid(request, *args, **kwargs): 
     key = kwargs.get('key') 
     if not key: 
      kwargs.set('key', request.key) 
     return f(request, *args, **kwargs) 
    return redirect_if_invalid