Decorator with configurable attributes got an unexpected keyword argument
Question: I am attempting to combine two decorator tutorials into a single decorator
that will log function arguments at a specified log level.

The first tutorial is from [here](http://juandebravo.com/2012/07/24/why-
python-rocks_and_two/) and looks like this (and works as expected):

    
    
    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
    

The second tutorial is from
[here](http://chimera.labs.oreilly.com/books/1230000000393/ch09.html#_solution_148).

My combined code looks like this and produces an error.

    
    
    #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
    

It is being called from my flask application like this:

    
    
    @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
    

The error that is produced is

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

The entire stack trace is

    
    
    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'
    

How can I fix the combined code to eliminate this error?


Answer: You need to actually call the `logged` decorator, even if you don't have a
parameter to pass.

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

Your code has three functions:

  1. The `logged` function, which creates a decorator that is configured with the arguments passed to it. It returns:
  2. The inner `decorator` function, which takes a function to decorate. It returns:
  3. The wrapper function, which wraps the decorated function.

So your code should be called like this:

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

But in your code, it is called like this:

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

The `decorator` function doesn't expect the `name` argument, which is what
causes the error.

You can use a hack allow the decorator to be used without calling it first.
You need to detect when the decorator is used without being called. I think it
would be something like this:

    
    
    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
    

