Debugging the Application - Python

Debugging a Python program is something that doesn't require too much work. The Standard Python Library comes with a debugger module called bdb that can be used by you to subclass your own debuggers. If you don't want to spend time writing your own debug, you can use the Python debugger (the pdb module), which is also part of the Python distribution. For those who need high-specialized debugging information, Python provides a disassembler module. And for those who only want to debug the value of variables, nothing works better than spreading a couple of print statements throughout your program.

If you decide to use the Python debugger, you will not regret it. This debugger allows you to set breakpoints, trace the values of local and global variables, step through the code, and many other attractive features.

Because it is written in Python, the debugger exemplifies a powerful feature of the Python language: the ability to create introspective applications, which means that we are able to write programs in Python that can handle and manipulate the execution of other programs.

The Base Debugger Module (bdb)

The bdb module exposes a framework for creating debuggers. This module provides a base class called bdb that allows you to create your own debuggers by subclassing the base class. The following methods are available in this class. Note that derived classes should override the following four methods to gain control of the application.

user_call(frame, argument_list)— This method is called when there is the remote possibility that we ever need to stop in this function pass.

user_line(frame)— This method is called when we stop or break at this line pass.

user_return(frame, return_value)— This method is called when a return trap is set here.

user_exception(frame, (exc_type, exc_value, exc_traceback))— This method is called if an exception occurs, but only if we are to stop at or just below this level pass.

The following methods can be called by the derived classes and by the clients in order to affect the stepping state:

set_step()— Stops after one line of code

set_next(frame)— Stops on the next line in or below the given frame

set_return(frame)— Stops when returning from the given frame

set_trace()— Starts debugging from here

set_continue()— Doesn't stop except at breakpoints or when finished

set_quit()— Quits the debugging process Derived classes and clients can call the following methods in order to manipulate breakpoints. These methods return an error message if something went wrong, and None if everything goes well.

set_break(filename, lineno, temporary=0, cond = None)— This method prints out the breakpoint line and filename:lineno.

clear_break(filename, lineno)— This method removes the breakpoint entry.

clear_bpbynumber(arg)— This method removes the breakpoint identified by the given number.

clear_all_file_breaks(filename)— This method removes all the breakpoints found in the given file.

clear_all_breaks()— This method removes all the active breakpoints from the current program.

get_break(filename, lineno)— This method returns true if the given file has a breakpoint in the given line number.

get_breaks(filename, lineno)— This method returns true if the given file has a breakpoint in the given line number.

get_file_breaks(filename)— This method returns a list of all breakpoints found in the given file.

get_all_breaks()— This method returns a list of all active breakpoints from the current program.

The following methods can be called by clients to use a debugger to debug a statement given as a string:

run(command, globals=None, locals=None)— Executes the string command, under the debugger control.

runeval(expr, globals=None, locals=None)— Evaluates the expression expr under the debugger control.

runcall(func, *args)— This method calls the single function func under the debugger control.

set_trace()— This method starts the debugger at the point at which this function is called. It is used to hard-code a debugger breakpoint into a specific code location.

The following example demonstrates how we can subclass the bdb class in order to design our own debug. This example is based on the testing routine included in the bdb module file.

import bdb
class Tdb(bdb.Bdb):
def user_call(self, frame, args):
name = frame.f_code.co_name
if not name:
name = '???'
print '+++ call', name, args
def user_line(self, frame):
import linecache, string
name = frame.f_code.co_name
if not name:
name = '???'
fn = self.canonic(frame.f_code.co_filename)
line = linecache.getline(fn, frame.f_lineno)
print '+++', fn, frame.f_lineno, name, ':', string.strip(line)
def user_return(self, frame, retval):
print '+++ return', retval
def user_exception(self, frame, exc_stuff):
print '+++ exception', exc_stuff
self.set_continue()
def factorials(n):
for f in xrange(n, 0, -1):
factorial = calc(f)
print 'The factorial of %d is %d'% (f, factorial)
def calc(f):
factorial = 1
for n in xrange(f, 1, -1):
factorial = factorial * n
return factorial
def main():
debug = Tdb()
debug.run('factorials(3)')
main()

The Python Debugger (pdb)

The Python debugger is directly based on the bdb class, as you can see when examining its source code. To start the Python debugger, you need to import the pdb module, and type one of the following commands:

run(), runeval(), runcall(), or set_trace().
import pdb
def myprog(n):
for l in xrange(n):
print l
debub=pdb.Pdb()
debub.runcall(myprog,10)
The debugger will then pop up a prompt. The debugger's prompt is '(Pdb) '.
To use the debugger in its simplest form, type
import pdb
pdb.run('<a statement>')
This will stop in the first function call in <a statement>.

Alternatively, if a statement terminated with an unhandled exception, you can use pdb's post-mortem facility to inspect the contents of the traceback:

>>><a statement>
<exception traceback>
>>> import pdb
>>> pdb.pm()

The commands recognized by the debugger are listed in the next section. Note that some commands have a short and a long form. The commands not recognized by the debugger are assumed to be Python commands, and are executed in the context of the program being debugged. Python statements can also be prefixed with an exclamation point (!).

This is a powerful way to inspect the program being debugged; it is even possible to change variables. When an exception occurs in such a statement, the exception name is printed, but the debugger's state is not changed.

The debugger supports aliases, which can save typing. And aliases can have parameters (see the alias help entry) that allow one a certain level of adaptability to the context under examination. Multiple commands can be entered on a single line, separated by the pair ;;. No intelligence is applied to separating the commands; the input is split at the first ;;, even if it is in the middle of a quoted string.

If a file .pdbrc exists in the user's home directory or in the current directory, it is read in and executed as if it had been typed at the debugger prompt. This is particularly useful for aliases. If both files exist, the one in the home directory is read first and aliases defined there can be overriden by the local file. Aside from aliases, the debugger is not directly programmable; but it is implemented as a class from which you can derive your own debugger class, which you can make as fancy as you like.

You can also invoke the Python debugger as a main program, on a script. Just use the following structure to start up the debugger.

import pdb
def main():
# Add your code here
if __name__=='__main__':
pdb.run('main()')

Debugger Commands

When you are at the debugger prompt, you can type any one of the following commands. Note that some of them have an abbreviated version. Next to each command, enclosed in brackets, you will find the command's optional arguments. Except for the list command, all commands can be repeated by entering a blank line at the prompt.

h(elp)— Prints the list of available commands.

w(here)— Prints a stack trace, with the most recent frame at the bottom. An arrow indicates the current frame, which determines the context of most commands.

d(own)— Moves the current frame one level down in the stack trace (to an older frame).

u(p)— Moves the current frame one level up in the stack trace (to a newer frame).

b(reak) [ ([filename:]lineno | function) [, condition] ]— With a filename:line number argument, set a break there. If filename is omitted, use the current file. With a function name, set a break at the first executable line of that function. Without an argument, list all breaks. Each breakpoint is assigned a number to which all the other breakpoint commands refer. The condition argument, if present, is a string that must evaluate to true in order for the breakpoint to be honored.

tbreak [ ([filename:]lineno | function) [, condition] ]— Temporary breakpoint, which is removed automatically when it is first hit. The arguments are the same as break.

cl(ear) [bpnumber [bpnumber ...] ]— With a space separated list of breakpoint numbers, clear those breakpoints. Without an argument, clear all breaks (but first ask confirmation).

disable bpnumber [bpnumber ...]— Disables the breakpoints given as a space separated list of breakpoint numbers. Disabling a breakpoint means that it cannot cause the program to stop execution. ut unlike clearing a breakpoint, it remains in the list of breakpoints and can be (re-)enabled.

enable bpnumber [bpnumber ...]— Enables the breakpoints specified.

ignore bpnumber count— Sets the ignore count for the given breakpoint number. If the count is omitted, the ignore count is set to 0. A breakpoint becomes active when the ignore count is zero. Whennon -zero, the count is decremented each time the breakpoint is reached and the breakpoint is not disabled and any associated condition evaluates to true.

condition bpnumber condition— Condition is an expression that must evaluate to true before the breakpoint is honored. If condition is absent, any existing condition is removed; that is, the breakpoint is made unconditional.

s(tep)— Executes the current line and stops at the first possible occasion (either in a called function or in the current function).

n(ext)— Continues execution until the next line in the current function is reached or it returns.

r(eturn)— Continues execution until the current function returns.

c(ont(inue))— Continues execution, only stops when a breakpoint is encountered.

l(ist) [first [,last]]— Lists source code for the current file. Without arguments, lists 11 lines around the current line or continues the previous listing. With one argument, lists 11 lines starting at that line. With two arguments, lists the given range; if the second argument is less than the first, it is a count.

a(rgs)— Prints the argument list of the current function.

p expression— Prints the value of the expression.

(!) statement— Executes the (one-line) statement in the context of the current stack frame. The exclamation point can be omitted unless the first word of the statement resembles a debugger command.

To assign to a global variable, you must always prefix the command with a global command, for example

(Pdb) global list_options; list_options = ['-l']
(Pdb)

whatis arg— Prints the type of the argument.

alias [name [command]]— Creates an alias called name that executes command. The command must not be enclosed in quotes. Replaceable parameters can be indicated by %1, %2, and so on, whereas %* is replaced by all the parameters. If no command is given, the current alias for name is shown. If no name is given, all aliases are listed. Aliases might be nested and can contain anything thatcan be legally typed at the pdb prompt. Note that you can override internal pdb commands with aliases.

Those internal commands are then hidden until the alias is removed. Aliasing is recursively applied to the first word of the command line; all other words in the line are left alone. As an example, here are two useful aliases (especially when placed in the .pdbrc file):

#Print instance variables (usage "pi classInst")
alias pi for k in %1.__dict__.keys(): print"%1.",k,"=",%1.__dict__[k]
#Print instance variables in self alias ps pi self
unalias name— Deletes the specified alias.
q(uit)— Quit from the debugger. The program being executed is aborted.

Note: Some Python IDE's, such as Pythonwin, implement derived debuggers, and Emacs'Grand Unified Debugger can use pdb.

Disassembling Python Bytecodes

Python has a module called dis, which is used to disassemble Python bytecodes into mnemonics. This module exposes a function, which is also called dis() that is able to disassemble classes, methods, functions, or code. If you don't provide any argument to the function, it disassembles the last traceback.

>>> import dis
>>> def routine():
... i = 5
... for loop in xrange(i):
... print 'Ni!'
>>>
>>> dis.dis(routine) SET_LINENO 1
SET_LINENO 2
LOAD_CONST 1 (5)
STORE_FAST 0 (i)
SET_LINENO 3
SETUP_LOOP 33 (to 51)
LOAD_GLOBAL 1 (xrange)
LOAD_FAST 0 (i)
CALL_FUNCTION 1
LOAD_CONST 2 (0)
>>SET_LINENO 3
FOR_LOOP 14 (to 50)
STORE_FAST 1 (loop)
SET_LINENO 4
LOAD_CONST 3 ('Ni!')
PRINT_ITEM
PRINT_NEWLINE
JUMP_ABSOLUTE 30
>>POP_BLOCK
>>LOAD_CONST 0 (None)
RETURN_VALUE

All rights reserved © 2020 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Python Topics