Mistakes in your code are known as “bugs”. You will make mistakes. You will make many mistakes, and that’s totally fine. Most of the time, they will be simple mistakes such as typos. But since computers are very literal, even typos prevent your code from working as intended. So they need to be fixed. The process of fixing your mistakes in programming is known as debugging.
The Python programming language comes with its own built-in debugger called pdb
. You can use pdb
on the command line or import it as a module. The name, pdb
, is short for “Python debugger”.
Here is a link to the full documentation for pdb
:
In this article, you will familiarize yourself with the basics of using pdb
. Specifically, you will learn the following:
- Starting
pdb
in the REPL - Starting
pdb
on the Command Line - Stepping Through Code
- Adding Breakpoints in
pdb
- Creating a Breakpoint with
set_trace()
- Using the built-in
breakpoint()
Function - Getting Help
While pdb
is handy, most Python editors have debuggers with more features. You will find the debugger in PyCharm or WingIDE to have many more features, such as auto-complete, syntax highlighting, and a graphical call stack.
A call stack is what your debugger will use to keep track of function and method calls. When possible, you should use the debugger that is included with your Python IDE as it tends to be a little easier to understand.
However, there are times where you may not have your Python IDE, for example when you are debugging remotely on a server. It is those times when you will find pdb
to be especially helpful.
Let’s get started!
Starting pdb
in the REPL
The best way to start is to have some code that you want to run pdb
on. Feel free to use your own code or a code example from another article on this blog.
Or you can create the following code in a file named debug_code.py
:
# debug_code.py def log(number): print(f'Processing {number}') print(f'Adding 2 to number: {number + 2}') def looper(number): for i in range(number): log(i) if __name__ == '__main__': looper(5)
There are several ways to start pdb
and use it with your code. For this example, you will need to open up a terminal (or cmd.exe
if you’re a Windows user). Then navigate to the folder that you saved your code to.
Now start Python in your terminal. This will give you the Python REPL where you can import your code and run the debugger, pdb
. Here’s how:
>>> import debug_code >>> import pdb >>> pdb.run('debug_code.looper(5)') > <string>(1)<module>() (Pdb) continue Processing 0 Adding 2 to number: 2 Processing 1 Adding 2 to number: 3 Processing 2 Adding 2 to number: 4 Processing 3 Adding 2 to number: 5 Processing 4 Adding 2 to number: 6
The first two lines of code import your code and pdb
. To run pdb
against your code, you need to use pdb.run()
and tell it what to do. In this case, you pass in debug_code.looper(5)
as a string. When you do this, the pdb
module will transform the string into an actual function call of debug_code.looper(5)
.
The next line is prefixed with (Pdb)
. That means you are now in the debugger. Success!
To run your code in the debugger, type continue
or c
for short. This will run your code until one of the following happens:
- The code raises an exception
- You get to a breakpoint (explained later on in this article)
- The code finishes
In this case, there were no exceptions or breakpoints set, so the code worked perfectly and finished execution!
Starting pdb
on the Command Line
An alternative way to start pdb
is via the command line. The process for starting pdb
in this manner is similar to the previous method. You still need to open up your terminal and navigate to the folder where you saved your code.
But instead of opening Python, you will run this command:
python -m pdb debug_code.py
When you run pdb
this way, the output will be slightly different:
> /python101code/chapter26_debugging/debug_code.py(1)<module>() -> def log(number): (Pdb) continue Processing 0 Adding 2 to number: 2 Processing 1 Adding 2 to number: 3 Processing 2 Adding 2 to number: 4 Processing 3 Adding 2 to number: 5 Processing 4 Adding 2 to number: 6 The program finished and will be restarted > /python101code/chapter26_debugging/debug_code.py(1)<module>() -> def log(number): (Pdb) exit
The 3rd line of output above has the same (Pdb) prompt that you saw in the previous section. When you see that prompt, you know you are now running in the debugger. To start debugging, enter the continue
command.
The code will run successfully as before, but then you will see a new message:
The program finished and will be restarted
The debugger finished running through all your code and then started again from the beginning! That is handy for running your code multiple times! If you do not wish to run through the code again, you can type exit
to quit the debugger.
Stepping Through Code
Stepping through your code is when you use your debugger to run one line of code at a time. You can use pdb
to step through your code by using the step
command, or s
for short.
Following is the first few lines of output that you will see if you step through your code with pdb
:
$ python -m pdb debug_code.py > /python101code/chapter26_debugging/debug_code.py(3)<module>() -> def log(number): (Pdb) step > /python101code/chapter26_debugging/debug_code.py(8)<module>() -> def looper(number): (Pdb) s > /python101code/chapter26_debugging/debug_code.py(12)<module>() -> if __name__ == '__main__': (Pdb) s > /python101code/chapter26_debugging/debug_code.py(13)<module>() -> looper(5) (Pdb)
The first command that you pass to pdb
is step
. Then you use s
to step through the following two lines. You can see that both commands do exactly the same, since “s” is a shortcut or alias for “step”.
You can use the next
(or n
) command to continue execution until the next line within the function. If there is a function call within your function, next
will step over it. What that means is that it will call the function, execute its contents, and then continue to the next line in the current function. This, in effect, steps over the function.
You can use step
and next
to navigate your code and run various pieces efficiently.
If you want to step into the looper()
function, continue to use step
. On the other hand, if you don’t want to run each line of code in the looper()
function, then you can use next
instead.
You should continue your session in pdb
by calling step
so that you step into looper()
:
(Pdb) s --Call-- > /python101code/chapter26_debugging/debug_code.py(8)looper() -> def looper(number): (Pdb) args number = 5
When you step into looper()
, pdb
will print out --Call--
to let you know that you called the function. Next you used the args
command to print out all the current args in your namespace. In this case, looper()
has one argument, number
, which is displayed in the last line of output above. You can replace args
with the shorter a
.
The last command that you should know about is jump
or j
. You can use this command to jump to a specific line number in your code by typing jump
followed by a space and then the line number that you wish to go to.
Now let’s learn how you can add a breakpoint!
Adding Breakpoints in pdb
A breakpoint is a location in your code where you want your debugger to stop so you can check on variable states. What this allows you to do is to inspect the callstack, which is a fancy term for all variables and function arguments that are currently in memory.
If you have PyCharm or WingIDE, then they will have a graphical way of letting you inspect the callstack. You will probably be able to mouse over the variables to see what they are set to currently. Or they may have a tool that lists out all the variables in a sidebar.
Let’s add a breakpoint to the last line in the looper()
function which is line 10.
Here is your code again:
# debug_code.py def log(number): print(f'Processing {number}') print(f'Adding 2 to number: {number + 2}') def looper(number): for i in range(number): log(i) if __name__ == '__main__': looper(5)
To set a breakpoint in the pdb
debugger, you can use the break
or b
command followed by the line number you wish to break on:
$ python3.8 -m pdb debug_code.py > /python101code/chapter26_debugging/debug_code.py(3)<module>() -> def log(number): (Pdb) break 10 Breakpoint 1 at /python101code/chapter26_debugging/debug_code.py:10 (Pdb) continue > /python101code/chapter26_debugging/debug_code.py(10)looper() -> log(i) (Pdb)
Now you can use the args
command here to find out what the current arguments are set to. You can also print out the value of variables, such as the value of i
, using the print
(or p
for short) command:
(Pdb) print(i) 0
Now let’s find out how to add a breakpoint to your code!
Creating a Breakpoint with set_trace()
The Python debugger allows you to import the pbd
module and add a breakpoint to your code directly, like this:
# debug_code_with_settrace.py def log(number): print(f'Processing {number}') print(f'Adding 2 to number: {number + 2}') def looper(number): for i in range(number): import pdb; pdb.set_trace() log(i) if __name__ == '__main__': looper(5)
Now when you run this code in your terminal, it will automatically launch into pdb
when it reaches the set_trace()
function call:
$ python3.8 debug_code_with_settrace.py > /python101code/chapter26_debugging/debug_code_with_settrace.py(12)looper() -> log(i) (Pdb)
This requires you to add a fair amount of extra code that you’ll need to remove later. You can also have issues if you forget to add the semi-colon between the import and the pdb.set_trace()
call.
To make things easier, the Python core developers added breakpoint()
which is the equivalent of writing import pdb; pdb.set_trace()
.
Let’s discover how to use that next!
Using the built-in breakpoint()
Function
Starting in Python 3.7, the breakpoint()
function has been added to the language to make debugging easier. You can read all about the change here:
Go ahead and update your code from the previous section to use breakpoint()
instead:
# debug_code_with_breakpoint.py def log(number): print(f'Processing {number}') print(f'Adding 2 to number: {number + 2}') def looper(number): for i in range(number): breakpoint() log(i) if __name__ == '__main__': looper(5)
Now when you run this in the terminal, Pdb will be launched exactly as before.
Another benefit of using breakpoint()
is that many Python IDEs will recognize that function and automatically pause execution. This means you can use the IDE’s built-in debugger at that point to do your debugging. This is not the case if you use the older set_trace()
method.
Getting Help
This chapter doesn’t cover all the commands that are available to you in pdb
. So to learn more about how to use the debugger, you can use the help
command within pdb
. It will print out the following:
(Pdb) help Documented commands (type help <topic>): ======================================== EOF c d h list q rv undisplay a cl debug help ll quit s unt alias clear disable ignore longlist r source until args commands display interact n restart step up b condition down j next return tbreak w break cont enable jump p retval u whatis bt continue exit l pp run unalias where Miscellaneous help topics: ========================== exec pdb
If you want to learn what a specific command does, you can type help
followed by the command.
Here is an example:
(Pdb) help where w(here) Print a stack trace, with the most recent frame at the bottom. An arrow indicates the "current frame", which determines the context of most commands. 'bt' is an alias for this command.
Go give it a try on your own!
Wrapping Up
Being able to debug your code successfully takes practice. It is great that Python provides you with a way to debug your code without installing anything else. You will find that using breakpoint()
to enable breakpoints in your IDE is also quite handy.
In this article you learned about the following:
- Starting
pdb
in the REPL - Starting
pdb
on the Command Line - Stepping Through Code
- Creating a Breakpoint with
set_trace()
- Adding Breakpoints in
pdb
- Using the built-in
breakpoint()
Function - Getting Help
You should go and try to use what you have learned here in your own code. Adding intentional errors to your code and then running them through your debugger is a great way to learn how things work!