I thought I knew a bit of Python, but then I stumped on this (a distilled form follows):
class A:
@staticmethod
def f():
print('some code here to report exactly
who called A.f()')
class B:
@staticmethod
def f():
A.f()
class C:
@staticmethod
def f():
A.f()
B.f() # I want this to print 'B.f'
C.f() # I want this to print 'C.f'
Note: compared to How can I get the address of the caller function?, this question describes a much more difficult situation (in my opinion). In fact, the referred question is easily solvable using an iteration over globals (see below).
Here's what I tried:
If I use the inspect
module I get access to the the caller function's name as a str (not the caller function's object itself), and so I can't use the __qualname__
.
If I try to iterate over the globals to see which global object has a method named 'f' I get two answers with no way to decide between the two.
I thought that an attribute 'cls' should be among the locals
, just like 'self' would be if these were regular functions (not static methods), and if so I could get to the class by inspect.stack()[1][0].f_locals['cls']
, but 'cls' is not there.
Any ideas?
UPD1: I keep digging and so far approach 1) seems the most promising: as soon as I get hold of the caller function object (not just the name, but the function itself) I'm all set. However, I don't see a way to do that in the setup above. One way around this would be to add an argument to A.f() and make all caller functions pass a copy of themselves: A.f(caller_func = Caller_Class.f)
. But it seems like double accounting. So, any ideas are still appreciated.
You were on the right track with your point 1. in your question: with inspect, you can get caller function's name as a str
(which is useless to get the qualified name), but you ignored that the frame
object also has the attribute f_code
, which is of type codeobject
and that recently in python 3.11 got the attribute co_qualname
, exactly the fully qualified function name at the given frame.
So, as simple as:
class A:
@staticmethod
def f():
print(inspect.currentframe().f_back.f_code.co_qualname)
B.f() # prints B.f
C.f() # prints C.f
You can see more details here.