Friday, October 25, 2013

A simple Python debugging function


By Vasudev Ram


[ Update: I do know about Python's logging module but felt it was overkill for my current needs, and also wanted more flexibility. ]

[ Update 2: Made some minor improvement to the code for better output. You can now print a debug message without any associated values. ]

I just whipped up this simple debugging function to help me debug my Python programs:
# vr_debug.py

# Debug utility functions by VR.
# Author: Vasudev Ram - http://www.dancingbison.com
# Description: Simple utility functions for debugging Python programs.

import sys
import os

def vr_debug(message, *values):
    vr_debugging = os.getenv("VR_DEBUG")
    if vr_debugging is None:
        # Debugging is off, do nothing.
        return
    if len(values) == 0:
        print message,
    else:
        print message, ":", 
    #print "len(values):", len(values)
    for value in values:
        #print repr(value), " ",
        print repr(value),
    print

def main():
    # Test the vr_debug function with some calls.
    vr_debug('z') # Debug message without any associated values.
    # Debug messages with values:
    vr_debug('a', 1)
    vr_debug('b', 1, "hi")
    vr_debug('c', 1, "hi", True)
    vr_debug('d', 1, "hi", True, [2, 3])
    vr_debug('d', 1, "hi", True, [2, 3], {'a': 'apple', 'b': 'banana'})

if __name__ == '__main__':
    main()

# EOF

To use the vr_debug function (as shown in the above program), you have to set an environment variable VR_DEBUG to some non-null value, say 1:
C:\>set VR_DEBUG=1
Then you can run the program to test the debug function, with:
C:\>python vr_debug.py
Here is its output:
z
a : 1
b : 1 'hi'
c : 1 'hi' True
d : 1 'hi' True [2, 3]
d : 1 'hi' True [2, 3] {'a': 'apple', 'b': 'banana'}
To turn off debugging, just set VR_DEBUG to a null value:
C:\>set VR_DEBUG=

Note: The above environment variable settings were for Windows. For Linux/UNIX, using bash, you would do this:
$ export VR_DEBUG=1
to set the environment variable, but the unset command to unset it:
$ unset VR_DEBUG

The above program included both the vr_debug function and a few tests of it. To use the vr_debug function in your own code, just copy the file vr_debug.py to somewhere on your PYTHONPATH and then import it into any Python files in which you want to use it:
from vr_debug import vr_debug
Now you can call it just as in the above program.

This is just a simple debug function, improvements are possible, such as passing an output destination argument, with a default of sys.stderr (so that output is un-buffered, unlike with the print command), and also so that a different destination can be given, such as a file opened for writing. To do that, you would have to change all the print statements to something like:
out_fil.write(...)
, and add
out_fil=sys.stderr
as another argument to the vr_debug function.

I've written a few different variations of such debugging functions over the years, in Python as well as other languages, but have somehow never seemed to arrive at a solution that satisfies me.

On the other hand, there is something to be said for KISS ...

For another Python debugging technique, see my earlier post:

Using sys._current_frames() and the Python traceback module for debugging

- Vasudev Ram - Dancing Bison Enterprises

Contact me

7 comments:

Grant said...

Do you really want to check the VR_DEBUG environment variable every time vr_debug() is called? For that matter, do you really want to incur a check on every call even if debugging is turned off? How about something like this:

if os.getenv("VR_DEBUG"):
def vr_debug(message, *values):
if len(values) == 0:
print message,
else:
print message, ":",
#print "len(values):", len(values)
for value in values:
#print repr(value), " ",
print repr(value),
print
else:
def vr_debug(message, *values):
pass


(Sorry about the formatting, I couldn't figure out an HTML tag that would be accepted.)

Vasudev Ram said...

Thanks for your version.

Yes, I did realize that the environment variable gets checked in each call to the vr_debug function, but the overhead of the os.getenv() call is quite low, because there is no disk access; it just gets the value from memory:

http://docs.python.org/2/library/os.html#os.environ

But your version avoids even that low overhead. I hadn't thought of defining alternative versions of the function within the if and else clauses, though I knew that was possible in Python, in general. A useful technique ...

As for the formatting, yes, the indentation is gone when I published your comment, but no problem. FYI, I used the pre HTML tag in the original post, which works there. Let me check if it works in the comments by posting your code as a new comment below, using the pre tag.

Vasudev Ram said...

Nope, pre tag didn't work - Blogger gave a message that it is not allowed.

Anyway, another minor improvement to the code, though I'm not posting it again - I moved the for loop to be under the else clause, since there is no sense running it if len(values) is 0.

Brendan said...

You should check out the python logging module. It does all this and more and is part of the standard library.

Vasudev Ram said...

I do know about Python's logging module, and have also used Java's logging modules, such as log4j and another, and have extended them in the past.

See my update near the top of my post, that starts with "I do know about Python's logging module".

Like I said there, I wanted flexibility. One thing I didn't mention in the post, is that I plan to add more features to the debugging function over time. I could do that with Python's logging module, of course, but chose to roll my own for my current requirement.

Piotr Dobrogost said...

Have you seen q - Quick-and-dirty debugging output for tired programmers (https://pypi.python.org/pypi/q/)?

Vasudev Ram said...

No, hadn't seen it earlier. Thanks, will have a look.