Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tell how an argument was received by a function?

I would like to know if the following introspection is possible in cpython:

>>> def potato(x=69):
...     if x == 69 and ???:
...         print '69 was taken from argument defaults'
...     if x == 69 and ???:
...         print '69 was passed as positional arg'
...     if x == 69 and ???:
...         print '69 was passed as kwarg'
...
>>> potato()
69 was taken from argument defaults
>>> potato(69)
69 was passed as positional arg
>>> potato(x=69)
69 was passed as kwarg

I'm interested in both python2 and python3 answers, if they are different.

Any kind of blackmagic involving inspect, traceback, pdb, sys._getframe etc is permissable here. Of course, modifying the argspec of the function is not allowed.

like image 237
wim Avatar asked Apr 29 '14 14:04

wim


1 Answers

It doesn't look like inspect can provide this information directly although frames have a string called code_context which gives you the source line at which the function was called. The problem is that one would have to rewrite a small parser to make sense of it.

Here is a simpler solution based on a wrapper around the function you want to examine. It doesn't change the arg spec and the arg validation isn't changed either :

import inspect

def track_args(func):
    def tracker(*args, **kwargs):
        r = func(*args, **kwargs)
        for arg_num,arg_name in enumerate(inspect.getargspec(func)[0]):
            if arg_name in kwargs:
                print "%s was provided as keyword arg" % arg_name
            elif arg_num < len(args):
                print "%s was provided as positional arg" % arg_name
            else:
                print "%s was provided by default value" % arg_name
        return r
    return tracker

@track_args
def f(a, b, c=30):
    print "I'm f()"

f(20, b=10)
f(20)

Result with valid arguments:

I'm f()
a was provided as positional arg
b was provided as keyword arg
c was provided by default value

Result with invalid arguments:

Traceback (most recent call last):
  File "test.py", line 21, in <module>
    f(20)
  File "test.py", line 5, in tracker
    r = func(*args, **kwargs)
TypeError: f() takes at least 2 arguments (1 given)
like image 68
Grapsus Avatar answered Sep 28 '22 06:09

Grapsus