How to get the caller's method name in the called method?
Asked Answered
R

13

292

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?

Recollect answered 16/4, 2010 at 15:18 Comment(1)
possible duplicate of Getting the caller function name inside another function in Python?Perchance
I
339

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.

Introgression answered 16/4, 2010 at 15:20 Comment(9)
"it's not advisable to rely on it for production-functionality purposes." Why not?Tortilla
@Tortilla 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.Gooseflesh
@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 inefficientRios
Why this is not mentioned in the python3 documentation? docs.python.org/3/library/inspect.html They would at least put a warning if it is bad right?Tl
Its a shame that this is such a hit on performance. Logging could be so much nicer with something like this (or CallerMemberName).Hautesalpes
Is it possible to get callers path as well?Fritz
@Fritz Yes, in the example above you could retrieve it with calframe[1][1]. See also docs.python.org/3/library/inspect.html#the-interpreter-stack for more details on the return value of inspect.getouterframes.Accessary
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.Stereotomy
@Rios 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()Madelenemadelin
L
130

Shorter version:

import inspect

def f1(): f2()

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

f1()

(with thanks to @Alex, and Stefaan Lippen)

Laddie answered 29/12, 2011 at 4:4 Comment(3)
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.Edo
This approach gave me an error: KeyError: 'main'Festive
In Python 3 you need: print('caller name:', inspect.stack()[1][3])Jacquerie
B
89

This seems to work just fine:

import sys
print sys._getframe().f_back.f_code.co_name
Beiderbecke answered 24/7, 2014 at 18:8 Comment(7)
This seems to be much faster than inspect.stackGerdagerdeen
Still it uses a protected member, which is generally not advised, because it could fail after the sys module gets some heavy refactoring.Nitrite
Valid point. Thanks for putting it up here as a potential long term pitfall of this solution.Beiderbecke
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.Muhammadan
It is not protected member, it is documented in 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.Crambo
Just wondering, wouldn't calling sys._getframe(1)_code.co_name be the same thing? Thus, also being more succinct?Parvis
This is also used by the logging module, see here. The fallback option is also interesting: raise Exception, catch Exception, then use sys.exc_info()Fenestrated
R
62

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.

Routine answered 29/8, 2019 at 14:47 Comment(6)
I was wondering what the caller name is if the function is called directly. Printing it results in <module>. 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?Toxin
@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.Routine
It is the best way I guess, since there is no magic numbers like calframe[1][3] that can change in future.Tautology
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).Zaller
For those interested: inspect.currentframe() simply returns sys._getframe(1) or None. Also note that sys._getframe() is documented, but is considered an implementation detail.Fenestrated
Helpful additional fields are co_firstlineno and co_filename which provide the line number and filename of the function co_name.Hairy
C
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)
Collocate answered 21/3, 2012 at 20:12 Comment(6)
Awesome, this worked well for me in my logging code, where I can be called from a lot of different places. Thanks very much.Boric
Unless you delete also stack, it still leaks frames due to cirular refs as explained in the inspect-docsShalne
@Shalne do you have some test code to prove that?Collocate
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 ```Shalne
Awesome! That worked when other solutions failed! I am using class methods and lambda-expressions so it's tricky.Chapbook
It works - but strangely when you debug your scripts with ipdb it would only return pdb.TerminalPdb.defaultReversal
V
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
Viki answered 3/8, 2015 at 21:2 Comment(3)
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.Carinthia
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 Nones 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.Indiaindiaman
In this solution, we should also probably return fn(*args, **kwargs), as the current solution suppresses returns from decorated functions and breaks them.Indiaindiaman
H
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

Haggadah answered 12/4, 2022 at 5:52 Comment(1)
Is this implementation more reliable compared to other suggestions?Dereliction
S
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.

Saltwort answered 31/7, 2022 at 21:43 Comment(0)
L
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$
Lavenialaver answered 16/11, 2018 at 7:52 Comment(1)
called() is function that was called but not function that do call of callerN()Bleak
N
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'
Nitroparaffin answered 26/3, 2023 at 14:39 Comment(0)
C
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()
Carniola answered 4/9, 2017 at 3:54 Comment(0)
L
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)
Lightheaded answered 3/6, 2023 at 15:8 Comment(0)
R
0

Here is how to get the calling method and its class:

import sys
frame = sys._getframe().f_back
calling_method = frame.f_code.co_name
calling_class = frame.f_locals.get("self").__class__.__name__
Ramayana answered 27/10, 2023 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.