Hiển thị các bài đăng có nhãn debugging. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn debugging. Hiển thị tất cả bài đăng

Thứ Sáu, 24 tháng 8, 2018

Automatically enter interactive mode after Python exception

By Vasudev Ram



Bug image attribution

Hi, readers,

Here's a Python command-line option that can facilitate debugging, when your program raises an exception:

It's the Python interpreter's -i option.

Here is a small Python program (ent-inter.py, for enter interactive mode) to show how the option can be used:
$ type ent-inter.py

from __future__ import print_function

print("before for loop")
for divisor in (1, 0, -1):
print("1/{} = {}".format(divisor, 1/divisor))
print("after for loop")
If I run it in the normal way (without the -i option), I get:
$ python ent-inter.py
before for loop
1/1 = 1
Traceback (most recent call last):
File "ent-inter.py", line 6, in
print("1/{} = {}".format(divisor, 1/divisor))
ZeroDivisionError: integer division or modulo by zero
The error message does tell us that there was a division by zero. But it doesn't tell us what exactly caused it.

Okay, in this particular case, from the stack trace (in particular, from the print statement), we can figure out that the variable divisor must have been zero. That was because I kept the code small and simple, though, for illustrative purposes.

But suppose that the cause of the error had been a more complex arithmetic expression, say with multiple division operations, or some other kind of statement (or sequence of statements). In that case, just the stack trace alone might not be enough for us to figure out the root cause of the error.

If we could immediately be launched into a Python interactive session with the current state (i.e. the variables) of the crashed program still available to inspect, that would likely help us to find the root cause. This is what the -i option helps with.

Let's see how:
$ python -i ent-inter.py
before for loop
1/1 = 1
Traceback (most recent call last):
File "ent-inter.py", line 6, in
print("1/{} = {}".format(divisor, 1/divisor))
ZeroDivisionError: integer division or modulo by zero
>>> divisor
0
>>>
I ran the same program again, but this time with the -i option given.

After the program crashed due to the ZeroDivisionError, an interactive Python shell was automatically launched (as we can see from the Python prompt shown).

I typed "divisor" at the prompt, and the shell printed that variable's current value, 0.
From this (plus the stack trace), we can see that this value is the cause of the error. Then we can look one line above, in the program's code (at the for statement) and see that one of the items in the tuple is a zero.

Of course, we can run the program under the control of a command-line debugger (like pdb) or single-step through the code in an IDE, to find the error. But that will only work if the error occurs during one of those runs. If the error is intermittent, running the program multiple times using the debugger or an IDE, will be tedious and time-consuming.

Instead, with this -i option, we can run the program as many times as we want, and for the times when it works properly, we don't waste any time stepping through the code. But when it does give an error like the above one, we are launched into the interactive mode, with the state of the crashed program available for inspection, to help debug the issue.

Another advantage of this approach is that we may not need to replicate the problem, because we have it right there in front of us (due to use of the -i option), and also, replicating the exact conditions that cause a bug is not always easy.

Note: I ran this program with the -i option on both Python 2.7 and Python 3.7 [1] on Windows, and on Python 2.7 on Linux. Got the same results in all 3 cases, except that in Python 3, the error message is slightly different:

ZeroDivisionError: division by zero

[1] Running it with Python 3 (on Windows) can be done in the usual way, by changing your PATH to point to Python 3 (if it is not already set to that), or by using Py, the Python launcher for Windows, like this:
$ py -3 -i ent-inter.py
So, overall, Python's -i option is useful.

Here is an excerpt from the output of "python -h" (the python command's help option):
-i     : inspect interactively after running script;
The picture at the top of the post is of one of the first software bugs recorded.

Read that story here on Wikipedia: Software bug

Enjoy.

Interested in a standard or customized Python course? Contact me

- Vasudev Ram - Online Python training and consulting

Hit the ground running with my vi quickstart tutorial.

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



Thứ Năm, 31 tháng 5, 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


Chủ Nhật, 22 tháng 1, 2017

To log or not to log, that is the question

By Vasudev Ram


Hamlet image attribution

I was teaching some students about debugging print statements, so thought of doing a Google search for them.

Here is the search:

https://www.google.com/search?q=debugging+print+statements

Viewed a few of the search results. One, from the site softwareengineering.stackexchange.com, had an interesting discussion about the pros and cons of debugging print statements vs. logging vs. using a debugger:

Is printing to console/stdout a good debugging strategy?

And there are other interesting results of the search.

The title of this post is, of course, a word play on the famous quote:

To be, or not to be

from the play Hamlet by Shakespeare.

And the image at the top is of the actor Edwin Booth playing Hamlet.

- 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

Managed WordPress Hosting by FlyWheel