Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access optional arguments in decorator

I'm trying to access all arguments passed to a function in the decorator, including optional arguments. Consider this example:

def decorator(fn):
    def wrapper(*args, **kwargs):
        print 'in wrapper', args, kwargs
        fn(*args, **kwargs)
    return wrapper

@decorator
def myFn(arg1, arg2, arg3=None):
    print 'in myFn', arg1, arg2, arg3

myFn(1,2)
myFn(1,2,3)

If I run this, I'll get:

in wrapper (1, 2) {}
in myFn 1 2 None
in wrapper (1, 2, 3) {}
in myFn 1 2 3

In the first run, since I don't specify 3 arguments, arg3 is defined as None for the purposes of myFn. But the fact that arg3 == None is not available inside of decorator, either in args or kwargs. If I explicitly pass it to myFn, it'll show up inside the decorator, but if I use the default value, it's nowhere to be found.

Why is this? And how can it be fixed?

like image 571
austin1howard Avatar asked Dec 20 '22 17:12

austin1howard


1 Answers

This is quite normal and cannot be "fixed"...

The decorator wrapper intercepts the arguments and keywords that are passed to the function: in other words, those passed by the caller of the function to the function itself.

arg3=None is a default argument defined at the function scope. It cannot be intercepted before the function is actually called (directly or via the wrapper) because it does not exist at that point.

However, the default values are stored in the function object:

def fn(arg1,arg2,arg3=None):
    pass

fn.func_defaults
-> (None,)

And you can use the following Get a function argument's default value? to map defaults to arguments... so I suppose the decorator could go to extreme lengths to print both passed and defaulted arguments. So I suppose my very first statement is not 100% correct :)

like image 57
isedev Avatar answered Jan 06 '23 09:01

isedev