How do I debug efficiently with Spyder in Python?

Solution 1:

(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:

  1. 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 then Continue.

  2. 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.

  3. This is fixed now. Also, to avoid clashes between Python code and Pdb commands, if you have (for instance) a variable called n and write n in the prompt to see its value, we will show it instead of running the n Pdb command. To run that command instead, you have to prefix it with an exclamation mark, like this: !n

  4. This is fixed too. You can set breakpoints in IPdb and they will be taken into account in your current session.

Solution 2:

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 specify
  • pp : pretty prints
  • args: if you are inside a function it prints the arguments
  • pp 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 in h
  • 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: enter image description here

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:

enter image description here

(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:

enter image description here

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.

enter image description here

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

Solution 3:

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/

Solution 4:

Here is how I debug in Spyder in order to avoid freezing the IDE. I do this if I alter the script while in debugging mode.

  1. I close out the current IPython (debugging) console [x]
  2. Open a new one [Menu bar-> Consoles-> Open an IPython Console]
  3. Enter debug mode again [blue play pause button].

Still a bit annoying, but it has the added benefit of clearing (resetting) variable list.