Question

My objective is to stimulate a sequence diagram of an application for this I need the information about a caller and callee class names at runtime. I can successfully retrieve the caller function but not able to get a caller class name?

#Scenario caller.py:

import inspect

class A:

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad()



class B:

    def Bad(self):
        print"dude"
        print inspect.stack()


a=A()
a.Apple()

When I printed the stack there was no information about the caller class. So is it possible to retrieve the caller class during runtime ?

Was it helpful?

Solution

Well, after some digging at the prompt, here's what I get:

stack = inspect.stack()
the_class = stack[1][0].f_locals["self"].__class__.__name__
the_method = stack[1][0].f_code.co_name

print("I was called by {}.{}()".format(the_class, the_method))
# => I was called by A.a()

When invoked:

➤ python test.py
A.a()
B.b()
  I was called by A.a()

given the file test.py:

import inspect

class A:
  def a(self):
    print("A.a()")
    B().b()

class B:
  def b(self):
    print("B.b()")
    stack = inspect.stack()
    the_class = stack[1][0].f_locals["self"].__class__.__name__
    the_method = stack[1][0].f_code.co_name
    print("  I was called by {}.{}()".format(the_class, the_method))

A().a()

Not sure how it will behave when called from something other than an object.

OTHER TIPS

Using the answer from Python: How to retrieve class information from a 'frame' object?

I get something like this...

import inspect

def get_class_from_frame(fr):
  args, _, _, value_dict = inspect.getargvalues(fr)
  # we check the first parameter for the frame function is
  # named 'self'
  if len(args) and args[0] == 'self':
    # in that case, 'self' will be referenced in value_dict
    instance = value_dict.get('self', None)
    if instance:
      # return its class
      return getattr(instance, '__class__', None)
  # return None otherwise
  return None


class A(object):

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad()

class B(object):

    def Bad(self):
        print"dude"
        frame = inspect.stack()[1][0]
        print get_class_from_frame(frame)


a=A()
a.Apple()

which gives me the following output:

Hello
dude
<class '__main__.A'>

clearly this returns a reference to the class itself. If you want the name of the class, you can get that from the __name__ attribute.

Unfortunately, this won't work for class or static methods ...

Perhaps this is breaking some Python programming protocol, but if Bad is always going to check the class of the caller, why not pass the caller's __class__ to it as part of the call?

class A:

    def Apple(self):
        print "Hello"
        b=B()
        b.Bad(self.__class__)



class B:

    def Bad(self, cls):
        print "dude"
        print "Calling class:", cls


a=A()
a.Apple()

Result:

Hello
dude
Calling class: __main__.A

If this is bad form, and using inspect truly is the preferred way to get the caller's class, please explain why. I'm still learning about deeper Python concepts.

Instead of indexing the return value of inspect.stack(), one could use the method inspect.currentframe(), which avoids the indexing.

prev_frame = inspect.currentframe().f_back
the_class = prev_frame.f_locals["self"].__class__
the_method = prev_frame.f_code.co_name

Python 3.8

import inspect


class B:
    def __init__(self):
        if (parent := inspect.stack()[1][0].f_locals.get('self', None)) and isinstance(parent, A):
            parent.print_coin()


class A:
    def __init__(self, coin):
        self.coin: str = coin
        B()

    def print_coin(self):
        print(f'Coin name: {self.coin}')


A('Bitcoin')

To store class instance name from the stack to class variable:

import inspect

class myClass():

    caller = ""

    def __init__(self):
        s = str(inspect.stack()[1][4]).split()[0][2:]
        self.caller = s

    def getInstanceName(self):
        return self.caller

This

myClassInstance1 = myClass()
print(myClassInstance1.getInstanceName())

will print:

myClassInstance1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top