Search code examples
pythonmethodsargumentsdecorator

Decorator args and kwargs returned within tuple


I have the following decorator and class.

def auth(fn):

    def check_headers(self):
        print 'checking headers...'
        #self.headers work done here
        
    def inner(self, *args, **kwargs):
        check_headers(self)
        fn(self, args, kwargs)

    return inner

class Worker(object):

    @auth
    def work(self, *args, **kwargs):
        print 'auth passed'
        print args
        print kwargs


worker_obj = Worker()
worker_obj.work('arg', kw='kwarg')

Which outputs :

> checking headers...
> auth passed
> (('arg',), {'kw': 'kwarg'})
> {}

But am I expecting this :

> checking headers...
> auth passed
> ('arg',)
> {'kw': 'kwarg'}

How come the args/kwargs are getting put in a tuple when the original method (work()) is being run, post-decoration?

I know that stripping it down to

def auth(fn):
    return fn

returns the parameters correctly, but I need to do some work on the worker instance (self) before returning. I surely missed something about decorators.


Solution

  • When you call fn(self, args, kwargs), you are passing two positional arguments: the tuple of args, and the dict of kwargs. So if you call work(1, x=2), you will call func(self, (1,), {'x': 2}). To expand the origianl args and kwargs into separate arguments, you need to do

    fn(self, *args, **kwargs)
    

    This will mean that when you call work(1, x=2), then you will also call fn(self, 1, x=2).

    You can see documentation on this here.