21

Consider the following python code:

def function():
    "Docstring"

    name = ???
    doc = ???

    return name, doc

>>> function()
"function", "Docstring"

What do I need to replace the question marks with so that I get the name and the docstring of the function from inside the same function?

EDIT: Most of the answers so far explicitly hardcode the name of the function inside its definition. Is it possible do something like below where a new function get_name_doc would access the function from the outer frame from which it is called, and return its name and doc?

def get_name_doc():
    ???

def function():
    "Docstring"

    name, doc = get_name_doc()

    return name, doc

>>> function()
"function", "Docstring"
D R
  • 21,936
  • 38
  • 112
  • 149

13 Answers13

19

This is not possible to do cleanly in a consistent way because names can be changed and reassigned.

However, you can use this so long as the function isn't renamed or decorated.

>>> def test():
...     """test"""
...     doc = test.__doc__
...     name = test.__name__
...     return doc, name
... 
>>> test()
('test', 'test')
>>> 

It's not at all reliable. Here's an example of it going wrong.

>>> def dec(f):
...     def wrap():
...         """wrap"""
...         return f()
...     return wrap
... 
>>> @dec
... def test():
...     """test"""
...     return test.__name__, test.__doc__
... 
>>> test()
('wrap', 'wrap')
>>> 

This is because the name test isn't defined at the time that the function is actually created and is a global reference in the function. It hence gets looked up in the global scope on every execution. So changes to the name in the global scope (such as decorators) will break your code.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
  • 1
    In that case, is there a way to get the `correct` doc string? Any work around solution to the problem? – George Apr 06 '12 at 00:49
  • @George, see the bottom of my post for an idea. I've played around with stuff like that just hacking around but have never been able to get it working at a level that I regard as production ready. – aaronasterling Apr 06 '12 at 00:51
  • About the `inspect.stack()` on this link: http://stackoverflow.com/questions/900392/getting-the-caller-function-name-inside-another-function-in-python, how does he know which index to access the stack? I assume that is not a guess right? I am interested if you can dynamically figure out which `stack[x][y]` to access? – George Apr 06 '12 at 00:56
  • @George, The first frame is the frame of the one that calls it. It has index 0. The second frame is the frame that called the caller. That's index 1 which is what he goes for. Here, you'd use index 0. Then play around with the second index to see what you get or read the source. – aaronasterling Apr 06 '12 at 01:03
  • 3
    For what it's worth, if you use `@wraps` in your decorator (https://docs.python.org/3/library/functools.html#functools.wraps), you can preserve the docstring. – Jack O'Connor Apr 14 '15 at 22:47
9

The code below solves the problem for the name of the function. However, it fails to detect the correct docstring for the example given by aaronasterling. I wonder if there is a way to get back to the abstract syntax tree associated with a bytecode object. Then it would be quite easy to read the docstring.

import inspect

def get_name_doc():
    outerframe = inspect.currentframe().f_back
    name = outerframe.f_code.co_name
    doc = outerframe.f_back.f_globals[name].__doc__    
    return name, doc

if __name__ == "__main__":

    def function():
        "Docstring"

        name, doc = get_name_doc()

        return name, doc

    def dec(f):
        def wrap():
           """wrap"""
           return f()
        return wrap

    @dec
    def test():
        """test"""
        return get_name_doc()

    assert function() == ('function', "Docstring")
    #The assertion below fails:. It gives: ('test', 'wrap')
    #assert test() == ('test', 'test')
D R
  • 21,936
  • 38
  • 112
  • 149
3

How about this:

import functools

def giveme(func):
    @functools.wraps(func)
    def decor(*args, **kwargs):
        return func(decor, *args, **kwargs)
    return decor

@giveme
def myfunc(me):
    "docstr"
    return (me.__name__, me.__doc__)

# prints ('myfunc', 'docstr')
print myfunc()

Shortly, the giveme decorator adds the (decorated) function object as the first argument. This way the function can access its own name and docstring when it is called.

Due to decoration, the original myfunc function is replaced by decor. To make the first argument be exactly the same as myfunc, what is passed into the function is decor and not func.

The functools.wraps decorator is used to give decor the properties (name, docstring, etc.) of the original myfunc function.

yak
  • 8,851
  • 2
  • 29
  • 23
3
>>> import inspect
>>> def f():
...     """doc"""
...     name = inspect.getframeinfo(inspect.currentframe()).function
...     doc = eval(name + '.__doc__')
...     return name, doc
... 
>>> f()
('f', 'doc')
>>> class C:
...     def f(self):
...         """doc"""
...         name = inspect.getframeinfo(inspect.currentframe()).function
...         doc = eval(name + '.__doc__')
...         return name, doc
... 
>>> C().f()
('f', 'doc')
dhinson919
  • 61
  • 4
3

For my personal projects, I developed function name and __doc__ recovery techniques for functions and class methods. These are implemented in an importable module (SelfDoc.py) which has its own self test in its __main__. It is included below. This code executes as is in Python 2.7.8 on linux and MacOS. It is in active use.

#!/usr/bin/env python

from inspect import (getframeinfo, currentframe, getouterframes)

class classSelfDoc(object):

    @property
    def frameName(self):
        frame = getframeinfo(currentframe().f_back)
        return str(frame.function)

    @property
    def frameDoc(self):
        frame = getframeinfo(currentframe().f_back)
        doc = eval('self.'+str(frame.function)+'.__doc__')
        return doc if doc else 'undocumented'

def frameName():
    return str(getframeinfo(currentframe().f_back).function)

def frameDoc():
    doc = eval(getframeinfo(currentframe().f_back).function).__doc__
    return doc if doc else 'undocumented'

if __name__ == "__main__":

    class aClass(classSelfDoc):
        "class documentation"

        def __init__(self):
            "ctor documentation"
            print self.frameName, self.frameDoc

        def __call__(self):
            "ftor documentation"
            print self.frameName, self.frameDoc

        def undocumented(self):
            print self.frameName, self.frameDoc

    def aDocumentedFunction():
        "function documentation"
        print frameName(), frameDoc()

    def anUndocumentedFunction():
        print frameName(), frameDoc()

    anInstance = aClass()
    anInstance()
    anInstance.undocumented()

    aDocumentedFunction()
    anUndocumentedFunction()
martineau
  • 119,623
  • 25
  • 170
  • 301
jlettvin
  • 1,113
  • 7
  • 13
2

This will find the name and the doc of a function calling get_doc. In my sense, get_doc should have the function as argument (that would have made it really easier, but way less fun to achieve ;))

import inspect

def get_doc():
    """ other doc
    """
    frame = inspect.currentframe()

    caller_frame = inspect.getouterframes(frame)[1][0]
    caller_name = inspect.getframeinfo(caller_frame).function
    caller_func = eval(caller_name)

    return caller_name, caller_func.__doc__


def func():
    """ doc string """
    print get_doc()
    pass


def foo():
    """ doc string v2 """
    func()

def bar():
    """ new caller """
    print get_doc()

func()
foo()
bar()
Jordi Riera
  • 101
  • 1
  • 5
  • need to test if the function calling is decorated, but should not be an issue as the decorator should be the next frame, and not the previous – Jordi Riera Apr 06 '12 at 01:04
  • If you don't `del` frame, it creates a cycle. – aaronasterling Apr 06 '12 at 01:13
  • @also, it fails with decorators because you're just using `eval` to evaluate the name in the global scope. So this is the same answer as everyone else gave except you use `inspect` to get a name to `eval` in the global scope and assume that the function is even there and not, e.g, in a module. – aaronasterling Apr 06 '12 at 01:18
  • 1
    What if caller_name is not visible from inside the scope of get_doc? – D R Apr 06 '12 at 01:41
  • @aaronasterling true. And That is even more true as this kind of function would be in. It is frustrating the farm doesn't have the address of the function instead of its name. – Jordi Riera Apr 06 '12 at 01:48
1

You have to use the name of the function to get it:

def function():
    "Docstring"

    name = function.__name__
    doc = function.__doc__

    return name, doc

There is also a module called inspect: http://docs.python.org/library/inspect.html. This is useful to get further information about the function (or any python object).

Dave Halter
  • 15,556
  • 13
  • 76
  • 103
1
def function():
    "Docstring"

    name = function.__name__
    doc = function.__doc__

    return name, doc    

This should do it, use the function's name, in your case, function.

Here is a very nice tutorial that talks about it: http://epydoc.sourceforge.net/docstrings.html

And of course: http://docs.python.org/tutorial/controlflow.html#documentation-strings

Edit: Refer to your edited version of the question, I think you might have to mess with inspect.stack() from this SO question. Ayman Hourieh's answer gives a small example.

Community
  • 1
  • 1
George
  • 4,514
  • 17
  • 54
  • 81
1
>>> def function():
        "Docstring"

        name = function.__name__
        doc = function.__doc__

        return name, doc

>>> function()
('function', 'Docstring')
jamylak
  • 128,818
  • 30
  • 231
  • 230
1

for a hard-coded version that works decently with 'well behaved' decorators. It must be declared after function. if function gets rebound later the changes updated here.

def get_name_doc():
    # global function # this is optional but makes your intent a bit more clear.
    return function.__name__, function.__doc__

This is a rather nasty hack, in that it abuses the way default args work. It will used whatever function is bound to at the time this function is 'initialized', and remember it even if the function gets rebound. Calling it with args is going to lead to interesting results.

def get_name_doc(fn=function):
    return fn.__name__, fn.__doc__

and a dynamic one that still hard coded but does update on function getting called with an argument of True. Basically this version will only update when told to do so.

def get_name_doc(update=False):
    global fn
    if update:
        fn = function
    return fn.__name__, fn.__doc__

Now of course there are decorator examples to this too.

@decorator  # applying the decorator decorator to make it well behaved
def print_name_doc(fn, *args, **kwargs):
    def inner(*args, **kwargs):
         print(fn.__doc__, fn.__name__)  # im assuming you just want to print in this case
         return fn(*args, **kwargs)
return inner

you should read on the decorator decorator (atleast). Look at NamedTuple source (from the collections module) as it involves noting being hard-coded. Sadly the named tuple code is rather weird. It is a string format used with eval rather then traditional code, but it works really neatly. This seems to be the most promising variant. You might be able to do this with metaclasess too, which leads to neat code, but rather nasty stuff hidden behind the scenes, which you need to code. This id advise against

Im suspecting that there probably is an easier way than going into inspection/reflection/templates/metaclasess by simply adding the following line at the end of the module.

help(<module>)

where is the name of the module you are working on (a string). Or even the variable __name__. This could be done in the __init__.py file too if working with multiple modules or on individual classes too i think.

1

As noted many times, using the function name inside the function is actually a dynamic lookup in the globals() of the current module. Using any kind of eval() is only a variation from it as its name resolution will work again with the globals() dictionary. Most of the examples will fail with a member function - you need to lookup the class name first from globals() and you can then access the member function from it. So actually

def function():
    """ foo """
     doc = function.__doc__

class Class:
    def function():
        """ bar """
        doc = Class.function.__doc__

is equivalent to

def function():
    """ foo """
     doc = globals()["function"].__doc__

class Class:
    def function():
        """ bar """
        doc = globals()["Class"].function.__doc__

In many cases this dynamic lookup will be enough. But actually you have to retype the function name inside the function. However if you write a helper function to find out the caller's doc string then you will face the fact that the helper function might live in a different module with a different globals() dictionary. So the only correct way would be to use the current frame info to find the function - but Python's frame object does not have a reference to the function object, it only carries a reference to the "f_code" code it uses. It needs to search through the referenced "f_globals" dictionary to find the mapping from f_code to the function object, for example like this:

import inspect

def get_caller_doc():
    frame = inspect.currentframe().f_back.f_back
    for objref in frame.f_globals.values():
        if inspect.isfunction(objref):
            if objref.func_code == frame.f_code:
                return objref.__doc__
        elif inspect.isclass(objref):
            for name, member in inspect.getmembers(objref):
                if inspect.ismethod(member):
                    if member.im_func.func_code == frame.f_code:
                        return member.__doc__

It is named get_caller_doc() instead of get_my_doc() because in the vast majority of cases you do want to have the doc string to hand it down as an argument to some helper function. But the helper function can easily get the doc string from its caller - I am using this in my unittest scripts where a helper function can use the doc string of the test to post it into some log or to use it as actual test data. That's why the presented helper only looks for doc strings of test functions and test member functions.

class MyTest:
    def test_101(self):
        """ some example test """
        self.createProject("A")
    def createProject(self, name):
        description = get_caller_doc()
        self.server.createProject(name, description)

It is left to the reader to expand the example for other use cases.

Guido U. Draheim
  • 3,038
  • 1
  • 20
  • 19
1

Reference http://stefaanlippens.net/python_inspect

import inspect
# functions
def whoami():
    return inspect.stack()[1][3]
def whocalledme():
    return inspect.stack()[2][3]
def foo():
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme())
    bar()
def bar():
    print "hello, I'm %s, daddy is %s" % (whoami(), whocalledme())
johny = bar
# call them!
foo()
bar()
johny()

Output:

hello, I'm foo, daddy is ?
hello, I'm bar, daddy is foo
hello, I'm bar, daddy is ?
hello, I'm bar, daddy is ?
rabin utam
  • 13,930
  • 3
  • 20
  • 15
0

You can try this:

import inspect


def my_function():
    """
    Hello World!
    """
    print(eval(inspect.currentframe().f_code.co_name).__doc__)


my_function()  # Hello World!
Dino
  • 7,779
  • 12
  • 46
  • 85
Carson
  • 6,105
  • 2
  • 37
  • 45