Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding: Python Debugging With pdb
Debugging applications can sometimes be an unwelcome activity. You’re busy working under a time crunch and you just want it to work. However, at other times, you might be learning a new language feature or experimenting with a new approach and want to understand more deeply how something is working.
Regardless of the situation, debugging code is a necessity, so it’s a good idea to be comfortable working in a debugger. In this tutorial, I’ll show you the basics of using pdb, Python’s interactive source code debugger.
I’ll walk you through a few common uses of pdb. You may want to bookmark this tutorial for quick reference later when you might really need it. pdb, and other debuggers, are indispensable tools. When you need a debugger, there’s no substitute. You really need it.
By the end of this tutorial, you’ll know how to use the debugger to see the state of any variable in your application. You’ll also be able to stop and resume your application’s flow of execution at any moment, so you can see exactly how each line of code affects its internal state.
This is great for tracking down hard-to-find bugs and allows you to fix faulty code more quickly and reliably. Sometimes, stepping through code in pdb and seeing how values change can be a real eye-opener and lead to “aha” moments, along with the occasional “face palm”.
pdb is part of Python’s standard library, so it’s always there and available for use. This can be a life saver if you need to debug code in an environment where you don’t have access to the GUI debugger you’re familiar with.
The example code in this tutorial uses Python 3.6. You can find the source code for these examples on GitHub.
At the end of this tutorial, there is a quick reference for Essential pdb Commands.
There’s also a printable pdb Command Reference you can use as a cheat sheet while debugging:
Free Bonus: Click here to get a printable "pdb Command Reference" (PDF) that you can keep on your desk and refer to while debugging.
Getting Started: Printing a Variable’s Value
In this first example, we’ll look at using pdb in its simplest form: checking the value of a variable.
Insert the following code at the location where you want to break into the debugger:
import pdb; pdb.set_trace()
When the line above is executed, Python stops and waits for you to tell it what to do next. You’ll see a (Pdb) prompt. This means that you’re now paused in the interactive debugger and can enter a command.
Starting in Python 3.7, there’s another way to enter the debugger. PEP 553 describes the built-in function breakpoint(), which makes entering the debugger easy and consistent:
breakpoint()
By default, breakpoint() will import pdb and call pdb.set_trace(), as shown above. However, using breakpoint() is more flexible and allows you to control debugging behavior via its API and use of the environment variable PYTHONBREAKPOINT. For example, setting PYTHONBREAKPOINT=0 in your environment will completely disable breakpoint(), thus disabling debugging. If you’re using Python 3.7 or later, I encourage you to use breakpoint() instead of pdb.set_trace().
You can also break into the debugger, without modifying the source and using pdb.set_trace() or breakpoint(), by running Python directly from the command-line and passing the option -m pdb. If your application accepts command-line arguments, pass them as you normally would after the filename. For example:
$ python3 -m pdb app.py arg1 arg2
There are a lot of pdb commands available. At the end of this tutorial, there is a list of Essential pdb Commands. For now, let’s use the p command to print a variable’s value. Enter p variable_name at the (Pdb) prompt to print its value.
Let’s look at the example. Here’s the example1.py source:
#!/usr/bin/env python3
filename = __file__
import pdb; pdb.set_trace()
print(f'path = {filename}')
If you run this from your shell, you should get the following output:
$ ./example1.py
> /code/example1.py(5)<module>()
-> print(f'path = {filename}')
(Pdb)
If you’re having trouble getting the examples or your own code to run from the command line, read How Do I Make My Own Command-Line Commands Using Python? If you’re on Windows, check the Python Windows FAQ.
Now enter p filename. You should see:
(Pdb) p filename
'./example1.py'
(Pdb)
Since you’re in a shell and using a CLI (command-line interface), pay attention to the characters and formatting. They’ll give you the context you need:
>starts the 1st line and tells you which source file you’re in. After the filename, there is the current line number in parentheses. Next is the name of the function. In this example, since we’re not paused inside a function and at module level, we see<module>().->starts the 2nd line and is the current source line where Python is paused. This line hasn’t been executed yet. In this example, this is line5inexample1.py, from the>line above.(Pdb)is pdb’s prompt. It’s waiting for a command.
Use the command q to quit debugging and exit.
Printing Expressions
When using the print command p, you’re passing an expression to be evaluated by Python. If you pass a variable name, pdb prints its current value. However, you can do much more to investigate the state of your running application.
In this example, the function get_path() is called. To inspect what’s happening in this function, I’ve inserted a call to pdb.set_trace() to pause execution just before it returns:
#!/usr/bin/env python3
import os
def get_path(filename):
"""Return file's path or empty string if no path."""
head, tail = os.path.split(filename)
import pdb; pdb.set_trace()
return head
filename = __file__
print(f'path = {get_path(filename)}')
If you run this from your shell, you should get the output:
$ ./example2.py
> /code/example2.py(10)get_path()
-> return head
(Pdb)
Where are we?
>: We’re in the source fileexample2.pyon line10in the functionget_path(). This is the frame of reference thepcommand will use to resolve variable names, i.e. the current scope or context.->: Execution has paused atreturn head. This line hasn’t been executed yet. This is line10inexample2.pyin the functionget_path(), from the>line above.
Let’s print some expressions to look at the current state of the application. I use the command ll (longlist) initially to list the function’s source:
(Pdb) ll
6 def get_path(filename):
7 """Return file's path or empty string if no path."""
8 head, tail = os.path.split(filename)
9 import pdb; pdb.set_trace()
10 -> return head
(Pdb) p filename
'./example2.py'
(Pdb) p head, tail
('.', 'example2.py')
(Pdb) p 'filename: ' + filename
'filename: ./example2.py'
(Pdb) p get_path
<function get_path at 0x100760e18>
(Pdb) p getattr(get_path, '__doc__')
"Return file's path or empty string if no path."
(Pdb) p [os.path.split(p)[1] for p in os.path.sys.path]
['pdb-basics', 'python36.zip', 'python3.6', 'lib-dynload', 'site-packages']
(Pdb)
You can pass any valid Python expression to p for evaluation.
This is especially helpful when you are debugging and want to test an alternative implementation directly in the application at runtime.
You can also use the command pp (pretty-print) to pretty-print expressions. This is helpful if you want to print a variable or expression with a large amount of output, e.g. lists and dictionaries. Pretty-printing keeps objects on a single line if it can or breaks them onto multiple lines if they don’t fit within the allowed width.
Stepping Through Code
There are two commands you can use to step through code when debugging:
| Command | Description |
|---|---|
n (next) |
Continue execution until the next line in the current function is reached or it returns. |
s (step) |
Execute the current line and stop at the first possible occasion (either in a function that is called or in the current function). |
There’s a 3rd command named
unt(until). It is related ton(next). We’ll look at it later in this tutorial in the section Continuing Execution.
The difference between n (next) and s (step) is where pdb stops.
Use n (next) to continue execution until the next line and stay within the current function, i.e. not stop in a foreign function if one is called. Think of next as “staying local” or “step over”.
Use s (step) to execute the current line and stop in a foreign function if one is called. Think of step as “step into”. If execution is stopped in another function, s will print --Call--.
Both n and s will stop execution when the end of the current function is reached and print --Return-- along with the return value at the end of the next line after ->.
Let’s look at an example using both commands. Here’s the example3.py source:
#!/usr/bin/env python3
import os
def get_path(filename):
"""Return file's path or empty string if no path."""
head, tail = os.path.split(filename)
return head
filename = __file__
import pdb; pdb.set_trace()
filename_path = get_path(filename)
print(f'path = {filename_path}')
If you run this from your shell and enter n, you should get the output:
$ ./example3.py
> /code/example3.py(14)<module>()
-> filename_path = get_path(filename)
(Pdb) n
> /code/example3.py(15)<module>()
-> print(f'path = {filename_path}')
(Pdb)
With n (next), we stopped on line 15, the next line. We “stayed local” in <module>() and “stepped over” the call to get_path(). The function is <module>() since we’re currently at module level and not paused inside another function.
Let’s try s:
$ ./example3.py
> /code/example3.py(14)<module>()
-> filename_path = get_path(filename)
(Pdb) s
--Call--
> /code/example3.py(6)get_path()
-> def get_path(filename):
(Pdb)
With s (step), we stopped on line 6 in the function get_path() since it was called on line 14. Notice the line --Call-- after the s command.
Conveniently, pdb remembers your last command. If you’re stepping through a lot of code, you can just press Enter to repeat the last command.
Below is an example of using both s and n to step through the code. I enter s initially because I want to “step into” the function get_path() and stop. Then I enter n once to “stay local” or “step over” any other function calls and just press Enter to repeat the n command until I get to the last source line.
$ ./example3.py
> /code/example3.py(14)<module>()
-> filename_path = get_path(filename)
(Pdb) s
--Call--
> /code/example3.py(6)get_path()
-> def get_path(filename):
(Pdb) n
> /code/example3.py(8)get_path()
-> head, tail = os.path.split(filename)
(Pdb)
> /code/example3.py(9)get_path()
-> return head
(Pdb)
--Return--
> /code/example3.py(9)get_path()->'.'
-> return head
(Pdb)
> /code/example3.py(15)<module>()
-> print(f'path = {filename_path}')
(Pdb)
path = .
--Return--
> /code/example3.py(15)<module>()->None
-> print(f'path = {filename_path}')
(Pdb)
Note the lines --Call-- and --Return--. This is pdb letting you know why execution was stopped. n (next) and s (step) will stop before a function returns. That’s why you see the --Return-- lines above.
Also note ->'.' at the end of the line after the first --Return-- above: