2

I'm trying to use Python's inspect module (in Python 2) to show information about the function that called the current function, including its arguments.

Here's a simple test program:

import inspect

def caller_args():
    frame = inspect.currentframe()
    outer_frames = inspect.getouterframes(frame)
    caller_frame = outer_frames[1]
    return inspect.getargvalues(caller_frame)

def fun_a(arg1):
    print caller_args()

def fun_b():
    fun_a('foo')

if __name__ == '__main__':
    fun_b()

And this happens when I run it:

$ python getargvalues_test.py
Traceback (most recent call last):
  File "getargvalues_test.py", line 16, in <module>
    fun_b()
  File "getargvalues_test.py", line 13, in fun_b
    fun_a('foo')
  File "getargvalues_test.py", line 10, in fun_a
    print caller_args()
  File "getargvalues_test.py", line 7, in caller_args
    return inspect.getargvalues(caller_frame)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/inspect.py", line 829, in getargvalues
    args, varargs, varkw = getargs(frame.f_code)
AttributeError: 'tuple' object has no attribute 'f_code'

I've googled that AttributeError exception, but with no luck. What am I doing wrong?

(I've since found the problem, so I'm asking-and-answering this here so anyone who has this problem in future will find the answer here.)

TimB
  • 5,714
  • 2
  • 26
  • 30

2 Answers2

7

This similar question helped me discover the problem.

The Python documentation for the inspect module mentions both "frame records" and "frame objects", and explains the difference.

  • inspect.currentframe() returns a frame object, but
  • inspect.getouterframes() returns a list of frame records.

The mistake in the code above is not extracting the frame object from the frame record of the calling function, and passing inspect.getouterframes() the frame record instead of the frame object. (Note that inspect.getouterframes() doesn't check that its argument is a frame object.)

Here's the fixed definition of caller_args() (with the change to the assignment to caller_frame):

def caller_args():
    frame = inspect.currentframe()
    outer_frames = inspect.getouterframes(frame)
    caller_frame = outer_frames[1][0]
    return inspect.getargvalues(caller_frame)

Which runs as desired:

$ python getargvalues_test_fixed.py
ArgInfo(args=['arg1'], varargs=None, keywords=None, locals={'arg1': 'foo'})
Community
  • 1
  • 1
TimB
  • 5,714
  • 2
  • 26
  • 30
-1

cause of error

AttributeError: 'tuple' object has no attribute 'f_code'

in your function

def caller_args()

is that caller_frame is an array of which you need item [1][0] as argument for

inspect.getargvalues(...)

this works :

currframe = inspect.currentframe()
callerframe = inspect.getouterframes(currframe, 2)
inspect.getargvalues(callerframe[1][0])

Also, the getargvalues function returns 4 values. First three are unimportant in this case, fourth contains JSON like format key/value list of callerframe arguments

 _,_,_,values = inspect.getargvalues(callerframe[1][0])
 for i in values:
     argsstring += str(i) + ' : ' + str(values[i])

My test looks like this :

import inspect

def log(text):
    currframe = inspect.currentframe()
    callerframe = inspect.getouterframes(currframe, 2)
    _,_,_,values = inspect.getargvalues(callerframe[1][0])
    argsstring = ''
    for i in values:
        argsstring += str(i) + ' : ' + str(values[i])
    print('name of file : ' + callerframe[1][1])
    print('name of function : ' + callerframe[1][3])
    print('line number : ' + str(callerframe[1][2]))
    print('caller function arguments : ' + argsstring)

def doTest(text):
    log(text) 

doTest('this is a test')