Python: How to get the caller’s method name in the called method?

Assume I have 2 methods:

def method1(self):
    ...
    a = A.method2()

def method2(self):
    ...

If I don’t want to do any change for method1, how to get the name of the caller (in this example, the name is method1) in method2?

inspect.getframeinfo and other related functions in inspect can help:

>>> import inspect
>>> def f1(): f2()
... 
>>> def f2():
...   curframe = inspect.currentframe()
...   calframe = inspect.getouterframes(curframe, 2)
...   print('caller name:', calframe[1][3])
... 
>>> f1()
caller name: f1

this introspection is intended to help debugging and development; it’s not advisable to rely on it for production-functionality purposes.

Shorter version:

import inspect

def f1(): f2()

def f2():
    print 'caller name:', inspect.stack()[1][3]

f1()

(with thanks to @Alex, and Stefaan Lippen)

This seems to work just fine:

import sys
print sys._getframe().f_back.f_code.co_name

I would use inspect.currentframe().f_back.f_code.co_name. Its use hasn’t been covered in any of the prior answers which are mainly of one of three types:

  • Some prior answers use inspect.stack but it’s known to be too slow.
  • Some prior answers use sys._getframe which is an internal private function given its leading underscore, and so its use is implicitly discouraged.
  • One prior answer uses inspect.getouterframes(inspect.currentframe(), 2)[1][3] but it’s entirely unclear what [1][3] is accessing.
import inspect
from types import FrameType
from typing import cast


def demo_the_caller_name() -> str:
    """Return the calling function's name."""
    # Ref: https://stackoverflow.com/a/57712700/
    return cast(FrameType, cast(FrameType, inspect.currentframe()).f_back).f_code.co_name


if __name__ == '__main__':
    def _test_caller_name() -> None:
        assert demo_the_caller_name() == '_test_caller_name'
    _test_caller_name()

Note that cast(FrameType, frame) is used to satisfy mypy.


Acknowlegement: comment by 1313e for an answer.

I’ve come up with a slightly longer version that tries to build a full method name including module and class.

https://gist.github.com/2151727 (rev 9cccbf)

# Public Domain, i.e. feel free to copy/paste
# Considered a hack in Python 2

import inspect

def caller_name(skip=2):
    """Get a name of a caller in the format module.class.method

       `skip` specifies how many levels of stack to skip while getting caller
       name. skip=1 means "who calls me", skip=2 "who calls my caller" etc.

       An empty string is returned if skipped levels exceed stack height
    """
    stack = inspect.stack()
    start = 0 + skip
    if len(stack) < start + 1:
      return ''
    parentframe = stack[start][0]    

    name = []
    module = inspect.getmodule(parentframe)
    # `modname` can be None when frame is executed directly in console
    # TODO(techtonik): consider using __main__
    if module:
        name.append(module.__name__)
    # detect classname
    if 'self' in parentframe.f_locals:
        # I don't know any way to detect call from the object method
        # XXX: there seems to be no way to detect static method call - it will
        #      be just a function call
        name.append(parentframe.f_locals['self'].__class__.__name__)
    codename = parentframe.f_code.co_name
    if codename != '<module>':  # top level usually
        name.append( codename ) # function or a method

    ## Avoid circular refs and frame leaks
    #  https://docs.python.org/2.7/library/inspect.html#the-interpreter-stack
    del parentframe, stack

    return ".".join(name)

Bit of an amalgamation of the stuff above. But here’s my crack at it.

def print_caller_name(stack_size=3):
    def wrapper(fn):
        def inner(*args, **kwargs):
            import inspect
            stack = inspect.stack()

            modules = [(index, inspect.getmodule(stack[index][0]))
                       for index in reversed(range(1, stack_size))]
            module_name_lengths = [len(module.__name__)
                                   for _, module in modules]

            s="{index:>5} : {module:^%i} : {name}" % (max(module_name_lengths) + 4)
            callers = ['',
                       s.format(index='level', module="module", name="name"),
                       '-' * 50]

            for index, module in modules:
                callers.append(s.format(index=index,
                                        module=module.__name__,
                                        name=stack[index][3]))

            callers.append(s.format(index=0,
                                    module=fn.__module__,
                                    name=fn.__name__))
            callers.append('')
            print('\n'.join(callers))

            fn(*args, **kwargs)
        return inner
    return wrapper

Use:

@print_caller_name(4)
def foo():
    return 'foobar'

def bar():
    return foo()

def baz():
    return bar()

def fizz():
    return baz()

fizz()

output is

level :             module             : name
--------------------------------------------------
    3 :              None              : fizz
    2 :              None              : baz
    1 :              None              : bar
    0 :            __main__            : foo

I found a way if you’re going across classes and want the class the method belongs to AND the method. It takes a bit of extraction work but it makes its point. This works in Python 2.7.13.

import inspect, os

class ClassOne:
    def method1(self):
        classtwoObj.method2()

class ClassTwo:
    def method2(self):
        curframe = inspect.currentframe()
        calframe = inspect.getouterframes(curframe, 4)
        print '\nI was called from', calframe[1][3], \
        'in', calframe[1][4][0][6: -2]

# create objects to access class methods
classoneObj = ClassOne()
classtwoObj = ClassTwo()

# start the program
os.system('cls')
classoneObj.method1()

Hey mate I once made 3 methods without plugins for my app and maybe that can help you, It worked for me so maybe gonna work for you too.

def method_1(a=""):
    if a == "method_2":
        print("method_2")

    if a == "method_3":
        print("method_3")


def method_2():
    method_1("method_2")


def method_3():
    method_1("method_3")


method_2()

Code:

#!/usr/bin/env python
import inspect

called=lambda: inspect.stack()[1][3]

def caller1():
    print "inside: ",called()

def caller2():
    print "inside: ",called()
    
if __name__=='__main__':
    caller1()
    caller2()

Output:

[email protected]:~/Documents$ python test_func.py 
inside:  caller1
inside:  caller2
[email protected]:~/Documents$