Reputation: 54514
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?
Upvotes: 299
Views: 219368
Reputation: 8556
Python has just made it better in 3.11, with the addition of codeobject.co_qualname
, which return the function fully qualified name.
If you were using the solution from @Asclepius above, consider doing this instead, as it also includes the class name where the method is defined:
import inspect
def who_is_calling():
print(inspect.currentframe().f_back.f_code.co_qualname)
class Klass:
def method(self):
who_is_calling() # prints 'Klass.method'
@classmethod
def class_method(cls):
who_is_calling() # prints 'Klass.class_method'
@staticmethod
def static_method():
who_is_calling() # prints 'Klass.static_method'
who_is_calling() # prints 'Klass'
class InnerKlass:
def inner_method(self):
who_is_calling() # prints 'Klass.InnerKlass.inner_method'
def unbound_function():
who_is_calling() # prints 'unbound_function'
What is good about this? It works for any functions, methods, class methods, static methods... and even a class itself (and nested classes)!
Upvotes: 2
Reputation: 16188
Shorter version:
import inspect
def f1(): f2()
def f2():
print ('caller name:', inspect.stack()[1][3])
f1()
(with thanks to @Alex, and Stefaan Lippen)
Upvotes: 130
Reputation: 436
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__
Upvotes: 0
Reputation: 146
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)
Upvotes: 0
Reputation: 23
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'
Upvotes: 1
Reputation: 1187
An alternative to sys._getframe()
is used by Python's Logging library to find caller information. Here's the idea:
raise an Exception
immediately catch it in an Except clause
use sys.exc_info
to get Traceback frame (tb_frame
).
from tb_frame
get last caller's frame using f_back
.
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.
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.
Upvotes: 6
Reputation: 492
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
Upvotes: 5
Reputation: 4160
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$
Upvotes: 1
Reputation: 63282
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:
inspect.stack
but it's known to be too slow.sys._getframe
which is an internal private function given its leading underscore, and so its use is implicitly discouraged.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.
Upvotes: 66
Reputation: 881555
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.
Upvotes: 350
Reputation: 858
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()
Upvotes: 0
Reputation: 666
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
Upvotes: 14
Reputation: 20521
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)
Upvotes: 38
Reputation: 2462
This seems to work just fine:
import sys
print sys._getframe().f_back.f_code.co_name
Upvotes: 90