6

I have trouble completing the following function:

def fullyQualifiedMethodNameInStack(depth=1):
    """
    The function should return <file>_<class>_<method> for the method in the 
    stack at specified depth.
    """
    fileName=inspect.stack()[depth][1]
    methodName=inspect.stack()[depth][3]
    class= """ please help!!! """
    baseName=os.path.splitext( os.path.basename( fileName ) )[0]
    return '{0}_{1}_{2}'.format( baseName, className, methodName )

As you can see I want the class name of the method being executed. The stack that inspect returns only has the method name, and I do not know how to find the class belonging to the method.

andreas buykx
  • 12,608
  • 10
  • 62
  • 76
  • 1) You can only see functions, not methods, 2) you can only make an educated guess as to the type of the instance. That is not the same thing as the class the method was originally defined on. Is that enough? – Martijn Pieters May 16 '13 at 14:06
  • Well I was hoping that there would be a possibility to find out which class the function is defined in without having to guess. What is the most educated guess I can take? – andreas buykx May 16 '13 at 14:08
  • @MartijnPieters I'm intrigued about point 2. `inspect.stack()` only seems to consistently return function names, not the function objects. – millimoose May 16 '13 at 14:10
  • @millimoose: Exactly, the stack only records the name of the currently executing function. That is also not necessarily the name under which it was accessed. – Martijn Pieters May 16 '13 at 14:12
  • @MartijnPieters That's kind of surprising, I'd have expected the actual function object to be there somewhere. Although even that probably wouldn't be perfect, especially considering that I assume the Python interpreter ends up "unwrapping" bound-method objects first. – millimoose May 16 '13 at 14:16
  • @millimoose: Added some more exposition about the nature of stack objects and functions and methods and instances . – Martijn Pieters May 16 '13 at 14:24

1 Answers1

3

You are always looking at a function context; the method context is already gone by the time the function executes. Technically speaking, functions act as descriptors when accessed as attributes on an instance (instance.method_name), which return a method object. The method object then, when called, in turn calls the original function with the instance as the first argument. Because this all happens in C code, no trace of the original method object remains on the Python stack.

The stack only needs to keep track of namespaces and the code to be executed for the local namespace. The original function object is no longer needed for these functions, only the attached code object still retains the name of the original function definition for traceback purposes.

If you know the function to be a method, you could search for a self local name. If present, type(self) is the class of the instance, which is not necessarily the class the method was defined on.

You would then have to search the class and it's bases (looping over the .__mro__ attribute) to try and locate what class defined that method.

There are a few more snags you need to take into account:

  • Naming the first argument of a method self is only a convention. If someone picked a different name, you won't be able to figure that out without parsing the Python source yourself, or by going up another step in the stack and trying to deduce from that line how the function was called, then retrieve the method and it's .im_self attribute.

  • You only are given the original name of the function, under which it was defined. That is not necessarily the name under which it was invoked. Functions are first-class objects and can be added to classes under different names.

  • Although a function was passed in self and was defined as part of a class definition, it could have been invoked by manually passing in a value for self instead of the usual route of the method (being the result of the function acting as a descriptor) passing in the self reference for the caller.

In Python 3.3 and up, the situation is marginally better; function objects have a new __qualname__ attribute, which would include the class name. The problem is then that you still need to locate the function object from the parent stack frame.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343