【问题标题】:Decorator with configurable attributes got an unexpected keyword argument具有可配置属性的装饰器得到了一个意外的关键字参数
【发布时间】:2014-10-21 14:30:18
【问题描述】:

我正在尝试将两个装饰器教程合并到一个装饰器中,该装饰器将在指定的日志级别记录函数参数。

第一个教程来自here,看起来像这样(并且按预期工作):

import logging

logging.basicConfig(level=logging.DEBUG)

def dump_args(func):
    # get function arguments name
    argnames = func.func_code.co_varnames[:func.func_code.co_argcount]

    # get function name
    fname = func.func_name
    logger = logging.getLogger(fname)

    def echo_func(*args, **kwargs):
        """
        Log arguments, including name, type and value
        """
        def format_arg(arg):
            return '%s=%s<%s>' % (arg[0], arg[1].__class__.__name__, arg[1])
        logger.debug(" args => {0}".format(', '.join(
            format_arg(entry) for entry in zip(argnames, args) + kwargs.items())))
        return func(*args, **kwargs)

    return echo_func

第二个教程来自here

我的组合代码如下所示并产生错误。

#decorators.py

from functools import wraps
import logging

logging.basicConfig(level=logging.DEBUG)

def logged(level=logging.INFO, name=None, message=None):
    '''
    Dump function arguments to log file.

    Optionally, change the logging level of the call, the name of the logger to
    use and the specific message to log as well
    '''

    def decorate(func):
        # get function arguments name
        argnames = func.func_code.co_varnames[:func.func_code.co_argcount]

        # get function name
        fname = name if name else func.__module__
        logger = logging.getLogger(fname)
        logmsg = message if message else None

        @wraps(func)
        def wrapper(*args, **kwargs):
            """
            Log arguments, including name, type and value
            """
            def format_arg(arg):
                return '%s=%s<%s>' % (arg[0], arg[1].__class__.__name__, arg[1])
            logger.log(level, " args => {0}".format(', '.join(
                format_arg(entry) for entry in zip(argnames, args) + kwargs.items())))
            if logmsg:
                logger.log(level, logmsg)
            return func(*args, **kwargs)
        return wrapper
    return decorate

它是从我的烧瓶应用程序中调用的,如下所示:

@app.route("/hello/")
@app.route("/hello/<name>")
@api_decorators.logged
def hello(name=None):
    s = "Hello"
    if name:
        s = "%s %s!" % (s, name)
    else:
        s = "%s %s!" % (s, "World!")
    return s

产生的错误是

TypeError: decorate() got an unexpected keyword argument 'name'

整个堆栈跟踪是

Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\flask\app.py", line 1836, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1820, in wsgi_app
    response = self.make_response(self.handle_exception(e))
  File "C:\Python27\lib\site-packages\flask\app.py", line 1403, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1817, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Python27\lib\site-packages\flask\app.py", line 1477, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1381, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Python27\lib\site-packages\flask\app.py", line 1475, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Python27\lib\site-packages\flask\app.py", line 1461, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
TypeError: decorate() got an unexpected keyword argument 'name'

如何修复组合代码以消除此错误?

【问题讨论】:

    标签: python python-2.7 flask decorator


    【解决方案1】:

    您需要实际调用logged 装饰器,即使您没有要传递的参数。

    @api_decorators.logged()
    def hello(name=None):
    

    您的代码具有三个功能:

    1. logged 函数,它创建一个装饰器,该装饰器配置有传递给它的参数。它返回:
    2. 内部decorator函数,它接受一个函数来装饰。它返回:
    3. 包装函数,用于包装修饰函数。

    所以你的代码应该这样调用:

    logged()(hello)(name='something')
    #Call 1 2      3, which calls hello inside it
    

    但在你的代码中,它是这样调用的:

    logged(hello)(name='something')
    #Call 1      2
    

    decorator 函数不需要 name 参数,这是导致错误的原因。

    您可以使用 hack 允许使用装饰器而无需先调用它。您需要检测何时使用装饰器而不被调用。我想应该是这样的:

    def logged(level=logging.INFO, name=None, message=None):
    ...
    # At the bottom, replace the return with this
    # If level is callable, that means logged is being called as a decorator
    if callable(level): 
        f = level
        level = logging.INFO
        return decorator(f)
    else:
        return decorator
    

    【讨论】:

    • 谢谢。对于装饰器调用来说,这看起来很奇怪。有没有办法设置它,以便在我不传递参数时不需要()
    • @NewGuy 我已经编辑了答案以添加我想到的第一个解决方案。不过,这有点 hacky。
    【解决方案2】:

    您可以通过将签名修改为类似于此来做到这一点:

    def logged(func=None, level=logging.INFO, name=None, message=None):
    

    在这种情况下,您将删除您实现的decorate 函数并保留wrapper 函数:

    def logged(func=None, level=logging.INFO, name=None, message=None):     
        if func is None:
            return partial(logged, level=level, name=name, message=message)
    
    
            # get function arguments name
        argnames = func.func_code.co_varnames[:func.func_code.co_argcount]
    
            # get function name
        fname = name if name else func.__name__
        logger = logging.getLogger(fname)
        logmsg = message if message else None
    
        @wraps(func)
        def wrapper(*args, **kwargs):
            def format_arg(arg):
                return '%s=%s<%s>' % (arg[0], arg[1].__class__.__name__, arg[1])
            logger.log(level, " args => {0}".format(', '.join(
                format_arg(entry) for entry in zip(argnames, args) + kwargs.items())))
            if logmsg:
                logger.log(level, logmsg)
            return func(*args, **kwargs)
        return wrapper
    

    这利用了partial 方法。

    使用您发布的应用程序代码(未修改)的示例结果。这不需要() 调用@api_decorators.logged

    2014-10-21 15:53:08,756 - INFO - hello -  args =>
    2014-10-21 15:53:12,730 - INFO - hello -  args => name=unicode<Andy>
    

    第一个调用/hello/,第二个调用/hello/Andy

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-17
      • 2015-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-23
      • 2021-08-30
      • 2019-09-15
      相关资源
      最近更新 更多