278

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?

codeforester
  • 39,467
  • 16
  • 112
  • 140
zs2020
  • 53,766
  • 29
  • 154
  • 219
  • 7
    possible duplicate of [Getting the caller function name inside another function in Python?](http://stackoverflow.com/questions/900392/getting-the-caller-function-name-inside-another-function-in-python) – ChrisF Mar 23 '12 at 22:26

12 Answers12

327

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.

Yuval Pruss
  • 8,716
  • 15
  • 42
  • 67
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 26
    "it's not advisable to rely on it for production-functionality purposes." Why not? – beltsonata Nov 11 '14 at 19:52
  • 36
    @beltsonata it's dependent on the CPython implementation, so code using this will break if you ever try to use PyPy or Jython or other runtimes. that's fine if you're just developing and debugging locally but not really something you want in your production system. – robru Feb 10 '15 at 02:08
  • 2
    @EugeneKrevenets Beyond just the python version, i just ran into an issue with it where it makes code that runs under a second run in multiple minutes once introduced. it grossly inefficient – Dmitry Apr 07 '17 at 21:35
  • 2
    Why this is not mentioned in the python3 documentation? https://docs.python.org/3/library/inspect.html They would at least put a warning if it is bad right? –  Jun 11 '18 at 06:28
  • 6
    Its a shame that this is such a hit on performance. Logging could be so much nicer with something like this (or [CallerMemberName](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.callermembernameattribute?view=netframework-4.8)). – StingyJack Apr 22 '19 at 11:33
  • Is it possible to get callers path as well? – alper Dec 11 '20 at 11:19
  • @alper Yes, in the example above you could retrieve it with ```calframe[1][1]```. See also https://docs.python.org/3/library/inspect.html#the-interpreter-stack for more details on the return value of ```inspect.getouterframes```. – Ohley Jun 02 '21 at 12:59
  • 1
    Whilst this is probably not production-appropriate for speed reasons, a solution does not have to be interpreter-independent in order to be appropriate for use in production. Using PyPy et al should never be a decision made lightly. – Kye Aug 15 '22 at 22:31
  • @Dmitry `currentframe` keeps a reference to your frame objects, which increases their lifespan and memory consumption. When you're done with the frame you can `del frame` or if you need it around break the reference cycles with [`frame.clear()`](https://docs.python.org/3/reference/datamodel.html#frame.clear) – fcrozatier Jul 12 '23 at 15:35
124

Shorter version:

import inspect

def f1(): f2()

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

f1()

(with thanks to @Alex, and Stefaan Lippen)

Todd Owen
  • 15,650
  • 7
  • 54
  • 52
  • Hello, I am getting below error when i run this: File "/usr/lib/python2.7/inspect.py", line 528, in findsource if not sourcefile and file[0] + file[-1] != '<>': IndexError: string index out of range Can u please provide suggestion. Thanx in advance. – Pooja Aug 13 '14 at 11:07
  • This approach gave me an error: KeyError: '__main__' – Praxiteles Oct 01 '16 at 03:30
  • In Python 3 you need: print('caller name:', inspect.stack()[1][3]) – mropp Mar 18 '21 at 23:22
86

This seems to work just fine:

import sys
print sys._getframe().f_back.f_code.co_name
Augiwan
  • 2,392
  • 18
  • 22
  • 8
    This seems to be much faster than `inspect.stack` – kentwait Feb 23 '19 at 07:08
  • 4
    Still it uses a protected member, which is generally not advised, because it could fail after the `sys` module gets some heavy refactoring. – filiprem Apr 17 '20 at 14:45
  • 3
    Valid point. Thanks for putting it up here as a potential long term pitfall of this solution. – Augiwan Jul 28 '20 at 19:03
  • 2
    Also works with pyinstaller. Short & sweet. I use it with co_filename (instead of co_name) to know from which main program it is called or to know if it's called directly for testing purpose. – Le Droid Oct 03 '20 at 19:02
  • 6
    It is not protected member, it is documented in https://docs.python.org/3/library/sys.html#sys._getframe . It has underscore because it is implementation specific (Maybe CPython only). Anyway it is used by inspect module as well to get the stack. – Adam Wallner Feb 13 '22 at 22:11
  • Just wondering, wouldn't calling `sys._getframe(1)_code.co_name` be the same thing? Thus, also being more succinct? – Jab Jul 13 '22 at 01:07
  • 1
    This is also used by the `logging` module, see [here](https://github.com/python/cpython/blob/cb1001ce23205eeed69395cdbfdb99e99a37aca1/Lib/logging/__init__.py#L163). The fallback option is also interesting: raise Exception, catch Exception, then use `sys.exc_info()` – djvg Dec 15 '22 at 10:57
55

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.

Asclepius
  • 57,944
  • 17
  • 167
  • 143
  • I was wondering what the caller name is if the function is called directly. Printing it results in ``. Will this always be the case whenever the function is directly called by name? Or are there cases where the module name will pop-up? – Vahagn Tumanyan Apr 28 '21 at 08:08
  • 1
    @VahagnTumanyan If you call it from a module (from outside any function), it will say what you saw. This answer will not however show the module name. You can call it from a function to see the calling function's name. – Asclepius Apr 28 '21 at 14:35
  • 3
    It is the best way I guess, since there is no magic numbers like calframe[1][3] that can change in future. – Piotr Wasilewicz Sep 16 '21 at 12:41
  • 2
    Very nice, one more thing that's nice about this is it works in both Python 2 and 3 (or at least 2.7.18 and 3.9.7). – Adam Parkin Dec 22 '21 at 17:14
  • 1
    For those interested: [`inspect.currentframe()`](https://github.com/python/cpython/blob/b72014c783e5698beb18ee1249597e510b8bcb5a/Lib/inspect.py#L1743) simply returns `sys._getframe(1)` or `None`. Also note that `sys._getframe()` is [documented](https://docs.python.org/3/library/sys.html#sys._getframe), but is considered an implementation detail. – djvg Dec 15 '22 at 11:40
  • Helpful additional fields are `co_firstlineno` and `co_filename` which provide the line number and filename of the function `co_name`. – YPCrumble Aug 01 '23 at 14:56
38

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)
anatoly techtonik
  • 19,847
  • 9
  • 124
  • 140
  • Awesome, this worked well for me in my logging code, where I can be called from a lot of different places. Thanks very much. – little_birdie Jul 24 '15 at 06:50
  • 1
    Unless you delete also `stack`, it still leaks frames due to cirular refs as explained in the [inspect-docs](https://docs.python.org/3.6/library/inspect.html#the-interpreter-stack) – ankostis Dec 10 '16 at 11:56
  • @ankostis do you have some test code to prove that? – anatoly techtonik Dec 15 '16 at 16:17
  • 2
    Difficult to show in a comment... Copy-paste in an editor this driving code (typing from memory) and try both versions of your code: ``` import weakref class C: pass def kill(): print('Killed') def leaking(): caller_name() local_var = C() weakref.finalize(local_var, kill) leaking() print("Local_var must have been killed") ``` You should get: ``` Killed Local_var must have been killed ``` and not: ``` Local_var must have been killed Killed ``` – ankostis Dec 15 '16 at 18:42
  • 1
    Awesome! That worked when other solutions failed! I am using class methods and lambda-expressions so it's tricky. – Sergey Orshanskiy Sep 19 '17 at 00:04
  • It works - but strangely when you debug your scripts with ipdb it would only return `pdb.TerminalPdb.default` – Qohelet Jul 05 '21 at 15:13
14

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
Xie Yanbo
  • 430
  • 6
  • 16
migpok35
  • 646
  • 7
  • 14
  • 4
    This will raise an IndexError if the requeted stack depth is greater than the actual. Use `modules = [(index, inspect.getmodule(stack[index][0])) for index in reversed(range(1, min(stack_size, len(inspect.stack()))))]` to get the modules. – jake77 Feb 27 '18 at 13:37
  • Thanks a lot for that. I would suggest gathering modules a bit differently, aka: `modules = [(index, module) for index in reversed(range(1, min(stack_size, len(stack)))) if index and (module := inspect.getmodule(stack[index][0]))]`. That way you can ensure that you won't have `None`s in your further lists. Also I think it's good to add `module_name_lengths.append(len(func.__module__))`, because the formatting doesn't take into account the element the lowest in the table. – ashrasmun Oct 09 '21 at 10:34
  • In this solution, we should also probably `return fn(*args, **kwargs)`, as the current solution suppresses returns from decorated functions and breaks them. – ashrasmun Oct 09 '21 at 10:46
4

You can use decorators, and do not have to use stacktrace

If you want to decorate a method inside a class

import functools

# outside ur class
def printOuterFunctionName(func):
@functools.wraps(func)
def wrapper(self):
    print(f'Function Name is: {func.__name__}')
    func(self)    
return wrapper 

class A:
  @printOuterFunctionName
  def foo():
    pass

you may remove functools, self if it is procedural

Mahi
  • 472
  • 5
  • 7
4

An alternative to sys._getframe() is used by Python's Logging library to find caller information. Here's the idea:

  1. raise an Exception

  2. immediately catch it in an Except clause

  3. use sys.exc_info to get Traceback frame (tb_frame).

  4. from tb_frame get last caller's frame using f_back.

  5. from last caller's frame get the code object that was being executed in that frame.

    In our sample code it would be method1 (not method2) being executed.

  6. From code object obtained, get the object's name -- this is caller method's name in our sample.

Here's the sample code to solve example in the question:

def method1():
    method2()

def method2():
    try:
        raise Exception
    except Exception:
        frame = sys.exc_info()[2].tb_frame.f_back

    print("method2 invoked by: ", frame.f_code.co_name)

# Invoking method1
method1()

Output:

method2 invoked by: method1

Frame has all sorts of details, including line number, file name, argument counts, argument type and so on. The solution works across classes and modules too.

user47
  • 1,080
  • 11
  • 28
1

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:

shahid@shahid-VirtualBox:~/Documents$ python test_func.py 
inside:  caller1
inside:  caller2
shahid@shahid-VirtualBox:~/Documents$
rzlvmp
  • 7,512
  • 5
  • 16
  • 45
Mohammad Shahid Siddiqui
  • 3,730
  • 2
  • 27
  • 12
1

For multilevel calling with the purpose of only printing until before the module name, the following function is a simple solution:

import inspect

def caller_name():
    frames = inspect.stack()
    caller_name = ''
    for f in frames:
        if f.function == '<module>':
            return caller_name
        caller_name = f.function

def a():
    caller = caller_name()
    print(f"'a' was called from '{caller}'")

def b():
    a()

def c():
    b()

c()

Output:

'a' was called from 'c'
Danton Sá
  • 13
  • 3
0

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()
Michael Swartz
  • 858
  • 2
  • 15
  • 27
0

Here is a convenient method using @user47's answer.

def trace_caller():
    try:
        raise Exception
    except Exception:
        frame = sys.exc_info()[2].tb_frame.f_back.f_back
        print(" >> invoked by:", frame.f_code.co_name)
Anthony M.
  • 98
  • 5