45

I feel like I should know this, but I haven't been able to figure it out...

I want to get the name of a method--which happens to be an integration test--from inside it so it can print out some diagnostic text. I can, of course, just hard-code the method's name in the string, but I'd like to make the test a little more DRY if possible.

Daryl Spitzer
  • 143,156
  • 76
  • 154
  • 173
  • possible duplicate of [How to get the function name as string in Python?](http://stackoverflow.com/questions/251464/how-to-get-the-function-name-as-string-in-python) – DarenW Dec 28 '12 at 18:56
  • 1
    @DarenW It's not. Requesting the name of a function from a variable containing a function object and requesting the name of a function from within are two very different questions. – Pavel Šimerda Feb 12 '16 at 15:27

6 Answers6

57

This seems to be the simplest way using module inspect:

import inspect
def somefunc(a,b,c):
    print "My name is: %s" % inspect.stack()[0][3]

You could generalise this with:

def funcname():
    return inspect.stack()[1][3]

def somefunc(a,b,c):
    print "My name is: %s" % funcname()

Credit to Stefaan Lippens which was found via google.

dcharles
  • 4,822
  • 2
  • 32
  • 29
mhawke
  • 84,695
  • 9
  • 117
  • 138
24

The answers involving introspection via inspect and the like are reasonable. But there may be another option, depending on your situation:

If your integration test is written with the unittest module, then you could use self.id() within your TestCase.

spiv
  • 3,142
  • 1
  • 18
  • 8
  • 1
    I just discovered that self.id() works in the unittest setUp() method (it returns the name of the test, not "setUp" itself), unlike the other solutions. Therefore, I'm changing this to the accepted answer for this particular question. – Daryl Spitzer Oct 29 '08 at 21:51
15

This decorator makes the name of the method available inside the function by passing it as a keyword argument.

from functools import wraps
def pass_func_name(func):
    "Name of decorated function will be passed as keyword arg _func_name"
    @wraps(func)
    def _pass_name(*args, **kwds):
        kwds['_func_name'] = func.func_name
        return func(*args, **kwds)
    return _pass_name

You would use it this way:

@pass_func_name
def sum(a, b, _func_name):
    print "running function %s" % _func_name
    return a + b

print sum(2, 4)

But maybe you'd want to write what you want directly inside the decorator itself. Then the code is an example of a way to get the function name in a decorator. If you give more details about what you want to do in the function, that requires the name, maybe I can suggest something else.

nosklo
  • 217,122
  • 57
  • 293
  • 297
  • 2
    +1 on using a decorator over relying on implementation details – Matthew Trevor Oct 29 '08 at 04:07
  • 2
    @MatthewTrevor To be honest I see a decorater relying on implementation details. According to http://stackoverflow.com/questions/251464/how-to-get-a-function-name-as-a-string-in-python it should be better to use `.__name__` over `.func_name`. I agree with the basic idea, though. – Pavel Šimerda Feb 12 '16 at 15:29
10
# file "foo.py" 
import sys
import os

def LINE( back = 0 ):
    return sys._getframe( back + 1 ).f_lineno
def FILE( back = 0 ):
    return sys._getframe( back + 1 ).f_code.co_filename
def FUNC( back = 0):
    return sys._getframe( back + 1 ).f_code.co_name
def WHERE( back = 0 ):
    frame = sys._getframe( back + 1 )
    return "%s/%s %s()" % ( os.path.basename( frame.f_code.co_filename ),     
                            frame.f_lineno, frame.f_code.co_name )

def testit():
   print "Here in %s, file %s, line %s" % ( FUNC(), FILE(), LINE() )
   print "WHERE says '%s'" % WHERE()

testit()

Output:

$ python foo.py
Here in testit, file foo.py, line 17
WHERE says 'foo.py/18 testit()'

Use "back = 1" to find info regarding two levels back down the stack, etc.

Kevin Little
  • 12,436
  • 5
  • 39
  • 47
2

I think the traceback module might have what you're looking for. In particular, the extract_stack function looks like it will do the job.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
0

To elaborate on @mhawke's answer:

Rather than

def funcname():
    return inspect.stack()[1][3]

You can use

def funcname():
    frame = inspect.currentframe().f_back
    return inspect.getframeinfo(frame).function

Which, on my machine, is about 5x faster than the original version according to timeit.

A Sz
  • 984
  • 7
  • 19