In general, bugs should be handled by the exceptions mechanism provided by Python and programmed (in your code). However, even using them, some unexpected bugs could exist, making that your module crashes. In other occasions, your program does not work as you expected and you want to figure out what is happenning.
Whatever the reason is (module crashings or algorithmic issues), Python provides a debugging system for making your programming experience happier. Basically, you need to run (importing it) the debugger when you are running your module. There exists two debugging modules: pdb
and ipdb
. The main difference between them is that the last one is more humman-friendly.
python -m pdb debug_me.py
Press h
for help. Commands:
[l]ist {<first line> {,<last line>}}
: lists the related portion of code.[w]here
: shows the execution stack.[b]reak {{{file:}<line> | <function>} {, <condition>}}
: without argument, shows the breakpoints; with argument, sets a (if specified, conditional) breakpoint.tbreak {{{file:}<line> | <function>} {, <condition>}}
: define a temporal breakpoint (only survives one hit).disable <breakpoint number> {<breakpoint number> ...}
: disable a breakpoint.enable <breakpoint number> {<breakpoint number> ...}
: enables a breakpoint.[cl]ear {<break point number> {<break point number> ...}}
: deletes a breakpoint.condition <breakpoint number> <condition>
: defines a condition for a breakpoint.ignore <breakpoint number> <count>
: ignores a breakpoint for a number of hits.[[c]ont]inue
: continues the execution.[n]ext
: runs the following instruction (don't enter functions).[s]tep
: runs the following instruction (enter functions).[unt]il
: continues until execution reaches a line in the same function with a line number higher than the current value.[u]p
: moves the current frame one level up in the stack trace (to an older frame).[d]own
: moves the current frame one level down in the stack trace (to a newer frame).[run] {args}
: (re)run the script.[p]rint (<object>)
: prints an object.[p]rety [p]rint (<object>)
: prints an object.[a]rgs
: prints the arguments of the current funcion.commands <breakpoint number> ... end
: define a set of debugging commands to be run each time a breakpoint is reached.alias {<name> {<command> {<parameter> <parameter> ...}}}
: show/define an alias for a pdb
command.!
: run a Python expression.
In [ ]:
! cat debug_me.py
Run it in a shell.
(yapt) $ python -m pdb debug_me.py
> /Users/vruiz/python-tutorial/debug_me.py(6)<module>()
-> def call_me():
(Pdb) c <------------------ YOUR INTERACTION HERE ------------------------
Debug me with "python -i debug_me.py"
After the run-time error, you should have access to the "a" variable
Traceback (most recent call last):
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/pdb.py", line 1661, in main
pdb._runscript(mainpyfile)
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/pdb.py", line 1542, in _runscript
self.run(statement)
File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/bdb.py", line 431, in run
exec(cmd, globals, locals)
File "<string>", line 1, in <module>
File "/Users/vruiz/python-tutorial/debug_me.py", line 6, in <module>
def call_me():
File "/Users/vruiz/python-tutorial/debug_me.py", line 11, in call_me
d=1/c
ZeroDivisionError: division by zero
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /Users/vruiz/python-tutorial/debug_me.py(11)call_me()
-> d=1/c
(Pdb) l <--------------------- YOUR INTERACTION HERE -----------------------
6 def call_me():
7 print("Debug me with \"python -i debug_me.py\"")
8 print("After the run-time error, you should have access to the \"a\" variable")
9 a=1
10 c=0
11 -> d=1/c
12 print("This code is never reached")
13 a=2
14
15 b=1
16
(Pdb) p c <--------------------- YOUR INTERACTION HERE ------------------------
0
(Pdb) quit <--------------------- YOUR INTERACTION HERE ------------------------
Post mortem debugger finished. The debug_me.py will be restarted
> /Users/vruiz/python-tutorial/debug_me.py(6)<module>()
-> def call_me():
(Pdb) quit <--------------------- YOUR INTERACTION HERE ------------------------
In [ ]:
! cat debug_me_2.py
Run me in a shell.
(yapy) $ python debug_me_2.py
Debug me with "python -i debug_me.py"
After the run-time error, you should have access to the "a" variable
> /Users/vruiz/YAPT/debug_me_2.py(12)call_me()
11 import ipdb; ipdb.set_trace()
---> 12 d=1/c
13 print("This code is never reached")
ipdb> p c <----------------- YOUR INTERACTION HERE -----------------
0
ipdb> quit <----------------- YOUR INTERACTION HERE -----------------
Exiting debugger.
"Post-mortem" debugging basically means that you want to debug after a module crash. Does not exist a pure post-mortem debugging in Python (in the sense you executed your code in the normal way, i.e. not expecting the bug, and after that, you must run the debugger). Basically, in Python you have two alternatives:
In [ ]:
!cat debug_me.py
Run:
(yapt) $ python -i debug_me.py
After the run-time error, you should have access to the "a" variable
Traceback (most recent call last):
File "debug_me.py", line 14, in <module>
call_me()
File "debug_me.py", line 8, in call_me
d=1/c
ZeroDivisionError: division by zero
>>> import pdb <------------------ YOUR INTERACTION HERE -------------------
>>> pdb.pm() <------------------ YOUR INTERACTION HERE -------------------
> /Users/vruiz/YAPT/debug_me.py(8)call_me()
-> d=1/c
(Pdb) p c <------------------ YOUR INTERACTION HERE -------------------
0
(Pdb) quit <------------------ YOUR INTERACTION HERE -------------------
>>> quit() <------------------ YOUR INTERACTION HERE -------------------
(yapt) $ python
>>> import pdb <------------------ YOUR INTERACTION HERE -------------------
>>> import debug_me <------------------ YOUR INTERACTION HERE -------------------
Debug me with "python -i debug_me.py"
After the run-time error, you should have access to the "a" variable
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/vruiz/YAPT/debug_me.py", line 14, in <module>
call_me()
File "/Users/vruiz/YAPT/debug_me.py", line 8, in call_me
d=1/c
ZeroDivisionError: division by zero
>>> pdb.pm() <------------------ YOUR INTERACTION HERE -------------------
> /Users/vruiz/YAPT/debug_me.py(8)call_me()
-> d=1/c
(Pdb) p c <------------------ YOUR INTERACTION HERE -------------------
0
(Pdb) quit <------------------ YOUR INTERACTION HERE -------------------
>>> quit() <------------------ YOUR INTERACTION HERE -------------------
In [ ]:
%run debug_me.py
In [ ]:
%debug
(write quit()
to exit the debugger)
In [ ]:
%run -d debug_me.py