views:

106

answers:

4

In C, I can put a log printf inside a function like this:

void xx_lock (int xx_flag)
{
  printf ("%s: START with %d\n", __FUNCTION__, xx_flag);
}

so I can copy the same line where I need in any function and it displays the function name in the log. I want something similar in Python. But if I use

__name__

the same way, it displays the module name, not the function name.

def xxx_lock(xx_flag=0)
    sys.stdout.write("%s: START with %d\n" % (__name__, xx_flag))

Is there some simple construct that I can use in that example to show the Python function name? To produce output like this:

xxx_lock: START with 1

Edited: added the parameter to the example.

+9  A: 

What you've asked for can't be done (nicely -- there are ways of doing it but they are nasty hacks). BUT: you don't really want to do that. Think at a higher level: you want an easy way to modify a function to log that it has started. Changing the source code of the function isn't a good way do to that -- after all, the logging has nothing to do with what the function is doing! Instead, you want to modify the function ex post facto.

def startLog( func ):
    def loggedFunc( *args, **kwargs ):
        print( "starting {0} with {1}".format( func.__name__, args[ 0 ] ) )
        return func( *args, **kwargs )

    return loggedFunc

@startLog
def theFunc( ):
    print( "running theFunc" )

theFunc( )
# starting theFunc
# running theFunc

This is an example of a Python decorator: it transforms a function at define-time into another one. It's syntactic sugar for:

def theFunc( ):
    print( "running theFunc" )
theFunc = startLog( theFunc )

In other words, you are taking the function object itself (remember -- functions are objects too, you can pass them around, modify them, look at their attributes, etc!) and redefining it. Looking at the source code, we see that startLog defines a new function (loggedFunc), which first prints a log trace and then runs the original function, passing through its arguments. Applying the decorator replaces theFunc with loggedFunc, so that the function bound to the name theFunc now logs itself!

Does that make sense? I'm happy to explain it in more detail.

Edit

As has been pointed out, this doesn't specifically answer your question; if you need any more functionality than this then use the logging module which does everything you'll ever need and then some. Walking the stack is just icky, as well as fragile, though =p.

katrielalex
Thanks. I am going to have to do some reading and testing to understand your answer. I'll be back.
codebunny
This doesn't answer the question; he wants to be able to make logs anywhere he needs to in a function and have the function name included automatically, not just log when he enters a function.
Glenn Maynard
He didn't say that! If that is needed then yes, this doesn't answer the question, but then inspecting the stack is never the right way to do it.
katrielalex
You're missing a comma in your "return func( *args **kwargs )" line.
robert
@robert: thanks!
katrielalex
It isn't quite what I wanted, but gave me a tremendous amount to think about, and will help me with some other stuff I was trying to do. Thanks.
codebunny
+2  A: 
import inspect
inspect.getframeinfo(inspect.currentframe()).function

This provides the functionality that most resembles C's _FUNCTION_, but as katrielalex said, it is a hack. The decorator is the better solution in the end.

Chris Connett
Remember that if you put this in a logging function you will get back the name of that function, not the one that called it. In that case you want to inspect the next frame up the stack, so replace `inspect.currentframe()` with `inspect.stack()[-2]`
Dave Kirby
+5  A: 

katrielalex above is right that you should not attempt to do this the "C way". Python has batteries included, so why not use them?

import logging, sys

logging.basicConfig(format="%(filename)s:%(funcName)s:%(message)s",level=logging.DEBUG,stream=sys.stderr)

def testFunc():
    logging.debug("entering")

testFunc()
loevborg
+1. The `logging` module is the correct way to do this. In C, a function is named at compile time so you know what it's referring to. With Python, the name of the function might change at runtime so it's not really reliable to use the special attributes.
Noufal Ibrahim
Er, `logging` uses the same frame and function information that you get from `inspect`. If one doesn't give you a useful function name, neither will.
Glenn Maynard
`logging` is smarter. Yes, it is possible to get the same information from `inspect`, but the logic is complicated; why reinvent the wheel?
katrielalex
@katriealex - to understand the wheel.
aaronasterling
+1  A: 
import inspect
def function_with_line(depth=1):
    stack = inspect.stack()
    frame = stack[depth][0]
    code = frame.f_code
    return "%s:%i" % (code.co_name, frame.f_lineno)

def foo():
    print "Currently in %s" % function_with_line()

I don't recommend using this from high-level code; embed it in the logger and increase depth to get the name of the correct stack frame. Also, be aware that this information won't be available in all implementations of Python. This may also be fairly slow in some implementations of Python; if logging performance matters to you, benchmark appropriately.

You're probably best off using logging, but if that's not an option for some reason (eg. integrating into another existing logging system), that's how to do it.

Glenn Maynard