How do I debug efficiently with spyder in Python?
Debugging workflow
You have to understand that in fact you are using different integration of the Python debugger pdb
and ipdb
(which uses pdb
and which can be accessed using the module ipdb
). I hope this trivial example will help you with using it better.
Suppose you want to debug this code:
def Waiting_fun(): #1 line number one
for i in range(100): #2
pass #3
#4
def New_sum(lista, to_s = False): #5
result = 0 #6
print 1 #7
for i in lista: #8
print "summed" #9
result +=i #10
Waiting_fun() #11
if to_s: #12
result = str(result)
return result
a = New_sum([1,4,5,7,8])
b = New_sum([1,4],1)
c = 456
d = New_sum([6,8,9],1)
final_result = a*b*c*d
Out: Type error
Quick first debugging using iPython %debug
%debug
The first thing I do is to call pdb from iPython using the magic command %debug
, you can set it as a default mechanism using %pdb
.
%debug
> /home/opdate/Desktop/test.py(23)<module>()
19 a = New_sum([1,4,5,7,8])
20 b = New_sum([1,4],1)
21 c = 456
22 d = New_sum([6,8,9],1)
---> 23 final_result = a*b*c*d
Once you have lunch pdb
. You can find all command in the official docs or you can use the command h
to display them. In this stage the only commands that I use are:
p
: prints the variables that you specifypp
: pretty printsargs
: if you are inside a function it prints the argumentspp locals()
: can be useful to print all the variables but most of the times it is a mess!!
use it if you want to avoid conflicts with the commands listed inh
whatis
variable_name: equivalent of type(variable_name)u
: Move the current frame one level up in the stack trace (to an older frame).d
: Move the current frame one level down in the stack trace (to a newer frame).q
: when you finish you can use q for quitting
In our case:
ipdb> pp a,b,c,d
(25, '5', 456, '23')
Or ipdb> !a,b,c,d
(no space between esclamation mark and first value).
It's clear that b and d are strings in case we can use:
ipdb> whatis b
<type 'str'>
Going deeper using break-points
70% of the times %debug
points you to the solution. When you need more features like breakpoints is time to use Spyder. In this case, we want to understand why b
is a string we put a breakpoint next to it (double-clicking next to the line number in the editor window). I find much better to use the standard Python console instead of the IPython console for debugging so select the console before starting debugging:
Then open the variable explorer
if there are any variables delete them. I use Ctrl+F5 to start the debugging you can use the buttons on the top but I prefer to use their shortcuts shown below:
(Pdb) c # we go to the breakpoint
(Pdb) s # we step into the function
(Pdb) args # we see what parameters are inserted
(Pdb) s # going step-by-step
(Pdb) ⏎ # series of Enters go line by line quicker
#Here I'll use whatis command but in fact I just look to
# the type in variable explorer of spyder.
(Pdb) whatis result #check if result is still int
(Pdb) unt #or until -useful to exiting from loops see doc.
(Pdb) n # we don't enter to the Waiting_fun function
(Pdb) s # going step-by-step
(Pdb) whatis result #we find that there the int is converted
(Pdb) j 6 # for double checking we jump back to 6 were the result is assigned
# We may be tempted to j(ump) to line 12 but doing so we would skip all the code
#for avoiding a series of `s`,`unt` and `n` we can use this solution:
(Pdb) tbreak 12 #set a new temporary breakpoint. Also `b` it's ok most of the time
(Pdb) c # go to it
(Pdb) j 6 # we jump to 6 the code we jump is NOT executed
(Pdb) whatis result# we find that if we jump 12-13 result is still int
Now we have located the error. We can also test a solution we repeat the step until 12 and we set to_s = False
(Pdb) to_s = False #!to_s = False to be on the safe side
It works. One important feature using the standard pdb in the Python console, is that you have auto competition and you can use the variable explorer instead of using whatis
and pp
:
Using the variable explorer you can also change the value of the variables which makes the things even quicker.
Conditional breakpoints
Another more clever way to locate the error is to use conditional breakpoint (Shift+F12) a great advantage of Spyder is going to debug and use list breakpoints. Conditional breakpoints are activated when the condition is True
In our case, we want to locate where b becomes a string so the condition is: type(b) == str
. I usually place a lot of conditional breakpoints and see which ones meet the condition. For doing so don't use Shift+F12 but place normal breakpoints double-clicking next to the line and go to Debug->List breakpoints and copy and past the condition in the table to every breakpoint as shown in the figure below.
From here the commands to use are:
(Pdb) c # go to the first
(Pdb) u # it helps to understand when it happened
(Pdb) d # come back to the breakpoint
(Spyder maintainer here) After our 4.2.0 version, released in November 2020, the debugging experience in Spyder is quite good. What we provide now is what people coming from Matlab would expect from a debugger, i.e. something that works like IPython and lets you inspect and plot variables at the current breakpoint or frame.
Now about your points:
If there is a breakpoint present in the file you're trying to debug, then Spyder enters in debug mode and continues until the first breakpoint is met. If it's present in another file, then you still need to press first
Debug
and thenContinue
.IPdb
is the IPython debugger console. In Spyder 4.2.0 or above it comes with code completion, syntax highlighting, history browsing of commands with the up/down arrows (separate from the IPython history), multi-line evaluation of code, and inline and interactive plots with Matplotlib.This is fixed now. Also, to avoid clashes between Python code and Pdb commands, if you have (for instance) a variable called
n
and writen
in the prompt to see its value, we will show it instead of running then
Pdb command. To run that command instead, you have to prefix it with an exclamation mark, like this:!n
This is fixed too. You can set breakpoints in
IPdb
and they will be taken into account in your current session.
The pdb debugger works just fine with regular python. So in Spyder, I just switch to the python console whenever I want to debug interactively.
import pdb
def yourfunction():
# Interesting stuff done here
pdb.set_trace()
Nice intro to debugging with pdb https://pythonconquerstheuniverse.wordpress.com/category/python-debugger/