See All Titles |
![]() ![]() Debugging the ApplicationDebugging 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.
The following methods can be called by the derived classes and by the clients in order to affect the stepping state:
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.
The following methods can be called by clients to use a debugger to debug a statement given as a string:
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 CommandsWhen 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.
Note
Some Python IDE's, such as Pythonwin, implement derived debuggers, and Emacs'Grand Unified Debugger can use pdb. Disassembling Python BytecodesPython 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) 0 SET_LINENO 1 3 SET_LINENO 2 6 LOAD_CONST 1 (5) 9 STORE_FAST 0 (i) 12 SET_LINENO 3 15 SETUP_LOOP 33 (to 51) 18 LOAD_GLOBAL 1 (xrange) 21 LOAD_FAST 0 (i) 24 CALL_FUNCTION 1 27 LOAD_CONST 2 (0) >> 30 SET_LINENO 3 33 FOR_LOOP 14 (to 50) 36 STORE_FAST 1 (loop) 39 SET_LINENO 4 42 LOAD_CONST 3 ('Ni!') 45 PRINT_ITEM 46 PRINT_NEWLINE 47 JUMP_ABSOLUTE 30 >> 50 POP_BLOCK >> 51 LOAD_CONST 0 (None) 54 RETURN_VALUE
|
© 2002, O'Reilly & Associates, Inc. |