Showing posts with label Python-debugging. Show all posts
Showing posts with label Python-debugging. Show all posts

Friday, June 1, 2018

Improved simple Python debugging function

By Vasudev Ram

[ I rewrote parts of this post, which was originally published two days ago on my blog (but intentionally not to the Planet Python, earlier, because I did not set the python label that makes that happen), for more clarity and to give a standalone example of the use of the use of the debug1 function. ]

I had blogged earlier about this Python debugging function, vr_debug, that I created a while ago:

A simple Python debugging function

Some time later I created an improved version of it, that does not need the user to set an environment variable to turn the debugging on or off.

Here is the code for the new function, now called debug1, in module debug1:
# debug1.py

from __future__ import print_function

# A simple debugging function for Python programs.
# How to use it:
# If the -O option is not given on the Python command line (the more common case 
# during development), the in-built special variable __debug__ is defined as True, 
# and the debug1 function displays debugging messages, i.e. it prints the message 
# and all other (optional) values passed to it.
# If the -O option is given, the variable __debug__ is defined as False, 
# and the debug1 function does nothing is defined as a no-op, so does nothing.

import os

if __debug__:
    def debug1(message, *values):
        if len(values) == 0:
            print(message)
        else:
            print("{}:".format(message), end=" ")
            print(" ".join([repr(value) for value in values]))
else:
    def debug1(message, *values):
        # Do nothing.
        pass

def main():
    # Test the debug1 function with some calls.
    debug1('message only')
    debug1('message with int', 1)
    debug1('message with int, float', 1, 2.3)
    debug1('message with long, string', 4L, "hi")
    debug1('message with boolean, tuple, set', True, (1, 2), { 3, 4} )
    debug1('message with string, boolean, list', "hi", True, [2, 3])
    debug1('message with complex, dict', 1 + 2j, {'a': 'apple', 'b': 'banana'})
    class Foo: pass
    foo = Foo()
    debug1('message with object', foo)
    debug1('message with class', Foo)
    debug1('message with xrange', xrange(3))
    debug1('message with listiterator', iter(range(4)))

if __name__ == '__main__':
    main()

To use it in the normal way, import the debug1() function from the debug1 module into a Python file where you want to use it.
Then just call the function in your code at the places where you want to print some message, with or without the value of one or more variables. Here is an example:


# In your program, say factorial.py
from debug1 import debug1

def factorial(n):
    # This line prints the message and the value of n when debug1 is enabled.
    debug1("entered factorial, n", n)
    if n < 0:
        raise Exception("Factorial argument must be integer, 0 or greater.")
    if n == 0:
        return 1
    p = 1
    for i in range(1, n + 1):
        p *= i
        # This line prints the message and the changing values 
        # of i and p when debug1 is enabled.
        debug1("in for loop, i, p", i, p)
    return p

print "i\tfactorial(i)"
for i in range(6):
    print "{}\t{}".format(i, factorial(i))
Then to run factorial.py with debugging on, no specific enabling step is needed (unlike with the earlier version, vr_debug.py where you had to set the environment variable VR_DEBUG to 1 or some other non-null value). Just run your program as usual and debugging output will be shown:
$ python factorial.py
i       factorial(i)
0       1
in for loop, i, p: 1 1
1       1
in for loop, i, p: 1 1
in for loop, i, p: 2 2
2       2
in for loop, i, p: 1 1
in for loop, i, p: 2 2
in for loop, i, p: 3 6
3       6
in for loop, i, p: 1 1
in for loop, i, p: 2 2
in for loop, i, p: 3 6
in for loop, i, p: 4 24
4       24
in for loop, i, p: 1 1
in for loop, i, p: 2 2
in for loop, i, p: 3 6
in for loop, i, p: 4 24
in for loop, i, p: 5 120
5       120
Once you have debugged and fixed any bugs in your program, with the help of the debugging output, you can easily turn off debugging messages like this, by adding the -O option to the python command line, to get only the normal program output:
$ python -O factorial.py
i       factorial(i)
0       1
1       1
2       2
3       6
4       24
5       120
The debug1 module internally checks the value of the built-in Python variable __debug__, and conditionally defines the function debug1() as either the real function, or a no-op, based on __debug__'s value at runtime.

The __debug__ variable is normally set by the Python interpreter to True, unless you pass python the -O option, which sets it to False.

Know of any different or better debugging functions? Feel free to mention them in the comments. Like I said in the previous post about the first version of this debug function (linked above), I've never been quite satisfied with the various attempts I've made to write debugging functions of this kind.

Of course, Python IDEs like Wing IDE or PyCharm can be used, which have features like stepping through (or over, in the case of functions) the code, setting breakpoints and watches, etc., but sometimes the good old debugging print statement technique is more suitable, particularly when there are many iterations of a loop, in which case the breakpoint / watch method becomes tedious, unless conditional breakpoints or suchlike are supported.

There are also scenarios where IDE debugging does not work well or is not supported, like in the case of web development. Although some IDEs have made attempts in this direction, sometimes it is only available in a paid or higher version.


Interested in learning Python programming by email? Contact me for the course details (use the Gmail id at that preceding link).
Enjoy.

- Vasudev Ram - Online Python training and consulting

Get updates (via Gumroad) on my forthcoming apps and content.

Jump to posts: Python * DLang * xtopdf

Subscribe to my blog by email

My ActiveState Code recipes

Follow me on: LinkedIn * Twitter

Are you a blogger with some traffic? Get Convertkit:

Email marketing for professional bloggers


Saturday, November 30, 2013

Using inspect.getargvalues to debug Python programs

By Vasudev Ram


I was looking through the Python docs recently, for ideas for any other useful debugging techniques, some time after I wrote this post about a simple Python debugging function.

Though I initially tried something else, I later came up with this technique, which may be helpful to debug Python programs. It uses the getargvalues function of Python's inspect module.

Here is a test program showing the use of inspect.getargvalues(); note that you have to pass the return value of inspect.currentframe() to inspect.getargvalues():
# test_getargvalues.py
# Author: Vasudev Ram - http://dancingbison.com

from debug1 import debug1
import inspect

def foo(arg1, arg2=None):
    print "In foo()"
    a = 1
    b = "2"
    c = True
    d = [ 3, "4" ]
    e = { 5: "five", "six": 6 }
    argvalues = inspect.getargvalues(inspect.currentframe())
    debug1("argvalues.args", argvalues.args)
    debug1("argvalues.varargs", argvalues.varargs)
    debug1("argvalues.keywords", argvalues.keywords)
    debug1("argvalues.locals", argvalues.locals)
    debug1("locals()", locals())

def bar(arg1, arg2, *args, **kwds):
    print "In bar()"
    argvalues = inspect.getargvalues(inspect.currentframe())
    debug1("argvalues.args", argvalues.args)
    debug1("argvalues.varargs", argvalues.varargs)
    debug1("argvalues.keywords", argvalues.keywords)
    debug1("argvalues.locals", argvalues.locals)
    debug1("locals()", locals())

def main():
    foo(1, 2)
    bar(1, 2, 3, 4, five=5, six=6)

main()
Run the program with:

python test_getargvalues.py

Here is its output:
In foo()
argvalues.args : ['arg1', 'arg2']
argvalues.varargs : None
argvalues.keywords : None
argvalues.locals : {'a': 1, 'c': True, 'b': '2', 'e': {'six': 6, 5: 'five'}, 'd': [3, '4'], 'arg1': 1, 'arg2': 2}
locals() : {'a': 1, 'c': True, 'b': '2', 'e': {'six': 6, 5: 'five'}, 'd': [3, '4'], 'arg1': 1, 'arg2': 2, 'argvalues': ArgInfo(args=['arg1', 'arg2'], varargs=None, keywords=None, locals={...})}
In bar()
argvalues.args : ['arg1', 'arg2']
argvalues.varargs : 'args'
argvalues.keywords : 'kwds'
argvalues.locals : {'arg1': 1, 'arg2': 2, 'args': (3, 4), 'kwds': {'six': 6, 'five': 5}}
locals() : {'arg1': 1, 'arg2': 2, 'args': (3, 4), 'kwds': {'six': 6, 'five': 5}, 'argvalues': ArgInfo(args=['arg1', 'arg2'], varargs='args', keywords='kwds', locals={...})}
Note that for comparison, I also printed the value of the Python built-in locals(), and found that the output of locals() is almost the same as, but a subset, of the output of getargvalues() - at least when the function which you are debugging has varargs and keyword arguments.

Read other posts about Python on jugad2.
- Vasudev Ram - Dancing Bison Enterprises
Contact Page


O'Reilly 50% Ebook Deal of the Day


Wednesday, April 10, 2013

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

By Vasudev Ram

Python's sys._current_frames() function/method and the traceback module of Python can be useful for debugging your Python code.

In the example below, I've used py, the Python Launcher for Windows. It comes with Python 3.3. If you're on Python 2, you can download the py launcher for Python 2 here. Use either of the versions (32-bit or 64-bit, as appropriate) called launcher*, not launchwin*, for the commands below.

The example below works on both Python 2 and Python 3.

#--------------------------------------------------------
# test_current_frames.py 

import sys, traceback

def foo():

    for thread, frame in sys._current_frames().items():
        print('Thread 0x%x' % thread)
        traceback.print_stack(frame)

def bar():
    foo()

def baz():
    bar()

baz()
#--------------------------------------------------------

Run the program with any of the following 3 commands:
py test_current_frames.py

or

py -2 test_current_frames.py

or

py -3 test_current_frames.py
You should get output similar to this:
Thread 0x17dc
  File "test_current_frames.py", line 17, in 
    baz()
  File "test_current_frames.py", line 15, in baz
    bar()
  File "test_current_frames.py", line 12, in bar
    foo()
  File "test_current_frames.py", line 9, in foo
    traceback.print_stack(frame)

Also read more about the traceback module on Doug Hellmann's Python Module Of The Week (PyMOTW) site, a.k.a. PyMOTW.

- Vasudev Ram - Dancing Bison Enterprises

Saturday, November 24, 2012

show, a Python debugging tool

By Vasudev Ram

Just saw this:

show, a debugging tool for Python. Looks like it can be useful. I'm interested in this area since I have felt the need for such tools and have written a few small tools for this myself.

Show is billed as "Debug print statements, done right. E.g. show(x)".

It also has some other features that could be useful, e.g.:

show.watch()

will display the value of local variables. When invoked again, only those variables that have changed (since the last show.watch() in the same context) will be displayed.



- Vasudev Ram - Dancing Bison Enterprises