Get function callers' information in python

Yes, the sys._getframe() function let's you retrieve frames from the current execution stack, which you can then inspect with the methods and documentation found in the inspect module; you'll be looking for specific locals in the f_locals attribute, as well as for the f_code information:

import sys
def special_func(x):
    callingframe = sys._getframe(1)
    print 'My caller is the %r function in a %r class' % (
        callingframe.f_code.co_name, 
        callingframe.f_locals['self'].__class__.__name__)

Note that you'll need to take some care to detect what kind of information you find in each frame.

sys._getframe() returns a frame object, you can chain through the whole stack by following the f_back reference on each. Or you can use the inspect.stack() function to produce a lists of frames with additional information.


An example:

def f1(a):
    import inspect
    print 'I am f1 and was called by', inspect.currentframe().f_back.f_code.co_name
    return a

def f2(a):
    return f1(a)

Will retrieve the "immediate" caller.

>>> f2(1)
I am f1 and was called by f2

And if wasn't called from another you get (in IDLE):

>>> f1(1)
I am f1 and was called by <module>

Thanks to Jon Clements answer I was able to make a function that returns an ordered list of all callers:

def f1():
    names = []
    frame = inspect.currentframe()
    ## Keep moving to next outer frame
    while True:
        try:
            frame = frame.f_back
            name = frame.f_code.co_name
            names.append(name)
        except:
            break
    return names

and when called in a chain:

def f2():
    return f1()

def f3():
    return f2()

def f4():
    return f3()

print f4()

looks like this:

['f2', 'f3', 'f4', '<module>']

In my case I filter out anything at '<module>' and after, and then take the last item to be the name of the originating caller.

Or modify the original loop to bail at the first appearance of any name starting with '<':

frame = frame.f_back
name = frame.f_code.co_name
if name[0] == '<':
    break
names.append(name)