views:

316

answers:

5

When debugging, I like to print out all the inputs and outputs of a function (I know I need a better IDE, but humour me, this could be used for error reporting). So, I'd ideally like to have:

@debuggable
def myfunc(argA,argB,argC):
    return argB+1

and use a global variable to switch on or off debugging. No, you don't like globals either, I guessed.

The best I can come up with is:

DEBUG = True

def debuggable(func):
    if DEBUG:
        def decorated(*args):
            print "Entering ",func.func_name
            print "    args ",args
            ret = func(*args)
            print ret
            return ret
        return decorated
    else:
        return func

@debuggable
def myfunc(this,that):
    return this+that

And running:

>>> myfunc(1,3)
Entering  myfunc
   args  (1, 3)
4

How can I improve that?

+11  A: 
nosklo
+3  A: 

I agree with nosklo using a debugger is much better than writing your own. I'll post an improvement to your code. But I still think you should follow nosklo's advice.

Use decorator classes to make your debugger neater:

class Debugger(object):
    enabled = False
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        if self.enabled:
            print 'Entering', self.func.func_name 
            print '    args:', args, kwargs
        return self.func(*args, **kwargs)

Debugger.enabled = True

@Debugger
def myfunc(a, b, c, d):
    pass
Nadia Alramli
A: 

I second what nosklo said.

Another thing to notice is that your function is a bit dangerous:

b = myfunc(1,3)

In this case, "b" is None, because the decorated function doesn't return anything.

Renato Besen
+3  A: 

I think what you're after isn't really a debugging decorator, but more of a logging decorator.

It might make sense to use Python's logging module so you can have more fine grained control over the logging itself. For example you would be able to output to a file for later analysing the output.

The decorator might then look something more like:


import logging

logger = logging.getLogger('TraceLog')
# TODO configure logger to write to file/stdout etc, it's level etc


def logthis(level):
    def _decorator(fn):
        def _decorated(*arg,**kwargs):
            logger.log(level, "calling '%s'(%r,%r)", fn.func_name, arg, kwargs)
            ret=fn(*arg,**kwargs)
            logger.log(level, "called '%s'(%r,%r) got return value: %r", fn.func_name, arg, kwargs, ret)
            return ret
        return _decorated
    return _decorator

@logthis(logging.INFO)
def myfunc(this,that):
    return this+that

Then if you configure the logger to output to stderr you'd see:


>>> logger.setLevel(logging.INFO)
>>> handler=logging.StreamHandler()
>>> logger.addHandler(handler)
>>> myfunc(1,2)
calling 'myfunc'((1, 2),{})
called 'myfunc'((1, 2),{}) got return value: 3

John Montgomery
A: 

There's a fairly long blog post on the subject of tracing decorators at Word Aligned.

Jouni K. Seppänen