
Table of Contents
Recommended Course
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 anyvariable 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 onGitHub.
At the end of this tutorial, there is a quick reference forEssential 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.
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:
importpdb;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 functionbreakpoint(), which makes entering the debugger easy and consistent:
breakpoint()By default,breakpoint() willimportpdb and callpdb.set_trace(), as shown above. However, usingbreakpoint() is more flexible and allows you to control debugging behavior via its API and use of the environment variablePYTHONBREAKPOINT. For example, settingPYTHONBREAKPOINT=0 in your environment will completely disablebreakpoint(), thus disabling debugging. If you’re using Python 3.7 or later, I encourage you to usebreakpoint() instead ofpdb.set_trace().
You can also break into the debugger, without modifying the source and usingpdb.set_trace() orbreakpoint(), by running Python directly from the command-line and passing the option-m pdb. If your application acceptscommand-line arguments, pass them as you normally would after the filename. For example:
$python3-mpdbapp.pyarg1arg2There are a lot of pdb commands available. At the end of this tutorial, there is a list ofEssential pdb Commands. For now, let’s use thep command to print a variable’s value. Enterp variable_name at the(Pdb) prompt to print its value.
Let’s look at the example. Here’s theexample1.py source:
#!/usr/bin/env python3filename=__file__importpdb;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, readHow Do I Make My Own Command-Line Commands Using Python? If you’re on Windows, check thePython Windows FAQ.
Now enterp 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 line5 inexample1.py, from the> line above.(Pdb) is pdb’s prompt. It’s waiting for a command.Use the commandq to quit debugging and exit.
When using the print commandp, 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 functionget_path() is called. To inspect what’s happening in this function, I’ve inserted a call topdb.set_trace() to pause execution just before it returns:
#!/usr/bin/env python3importosdefget_path(filename):"""Return file's path or empty string if no path."""head,tail=os.path.split(filename)importpdb;pdb.set_trace()returnheadfilename=__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.py on line10 in the functionget_path(). This is the frame of reference thep command 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 line10 inexample2.py in the functionget_path(), from the> line above.Let’s print some expressions to look at the current state of the application. I use the commandll (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 top 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 commandpp (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.
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 sectionContinuing Execution.
The difference betweenn (next) ands (step) is where pdb stops.
Usen (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”.
Uses (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--.
Bothn ands 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 theexample3.py source:
#!/usr/bin/env python3importosdefget_path(filename):"""Return file's path or empty string if no path."""head,tail=os.path.split(filename)returnheadfilename=__file__importpdb;pdb.set_trace()filename_path=get_path(filename)print(f'path ={filename_path}')If you run this from your shell and entern, 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)Withn (next), we stopped on line15, the next line. We “stayed local” in<module>() and “stepped over” the call toget_path(). The function is<module>() since we’re currently at module level and not paused inside another function.
Let’s trys:
$./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)Withs (step), we stopped on line6 in the functionget_path() since it was called on line14. Notice the line--Call-- after thes command.
Conveniently, pdb remembers your last command. If you’re stepping through a lot of code, you can just pressEnter to repeat the last command.
Below is an example of using boths andn to step through the code. I enters initially because I want to “step into” the functionget_path() and stop. Then I entern once to “stay local” or “step over” any other function calls and just pressEnter to repeat then 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) ands (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:
--Return--> /code/example3.py(9)get_path()->'.'-> return head(Pdb)When pdb stops at the end of a function before it returns, it also prints the return value for you. In this example it’s'.'.
Don’t forget the commandll (longlist: list the whole source code for the current function or frame). It’s really helpful when you’re stepping through unfamiliar code or you just want to see the entire function for context.
Here’s an example:
$./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)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 return head(Pdb)To see a shorter snippet of code, use the commandl (list). Without arguments, it will print 11 lines around the current line or continue the previous listing. Pass the argument. to always list 11 lines around the current line:l .
$./example3.py> /code/example3.py(14)<module>()-> filename_path = get_path(filename)(Pdb)l 9 return head 10 11 12 filename = __file__ 13 import pdb; pdb.set_trace() 14 -> filename_path = get_path(filename) 15 print(f'path = {filename_path}')[EOF](Pdb)l[EOF](Pdb)l . 9 return head 10 11 12 filename = __file__ 13 import pdb; pdb.set_trace() 14 -> filename_path = get_path(filename) 15 print(f'path = {filename_path}')[EOF](Pdb)Breakpoints are very convenient and can save you a lot of time. Instead of stepping through dozens of lines you’re not interested in, simply create a breakpoint where you want to investigate. Optionally, you can also tell pdb to break only when a certain condition is true.
Use the commandb (break) to set a breakpoint. You can specify a line number or a function name where execution is stopped.
The syntax for break is:
b(reak) [ ([filename:]lineno | function) [, condition] ]Iffilename: is not specified before the line numberlineno, then the current source file is used.
Note the optional 2nd argument tob:condition. This is very powerful. Imagine a situation where you wanted to break only if a certain condition existed. If you pass a Python expression as the 2nd argument, pdb will break when the expression evaluates to true. We’ll do this in an example below.
In this example, there’s a utility moduleutil.py. Let’s set a breakpoint to stop execution in the functionget_path().
Here’s the source for the main scriptexample4.py:
#!/usr/bin/env python3importutilfilename=__file__importpdb;pdb.set_trace()filename_path=util.get_path(filename)print(f'path ={filename_path}')Here’s the source for the utility moduleutil.py:
defget_path(filename):"""Return file's path or empty string if no path."""importoshead,tail=os.path.split(filename)returnheadFirst, let’s set a breakpoint using the source filename and line number:
$./example4.py> /code/example4.py(7)<module>()-> filename_path = util.get_path(filename)(Pdb)b util:5Breakpoint 1 at /code/util.py:5(Pdb)c> /code/util.py(5)get_path()-> return head(Pdb)p filename, head, tail('./example4.py', '.', 'example4.py')(Pdb)The commandc (continue) continues execution until a breakpoint is found.
Next, let’s set a breakpoint using the function name:
$./example4.py> /code/example4.py(7)<module>()-> filename_path = util.get_path(filename)(Pdb)b util.get_pathBreakpoint 1 at /code/util.py:1(Pdb)c> /code/util.py(3)get_path()-> import os(Pdb)p filename'./example4.py'(Pdb)Enterb with no arguments to see a list of all breakpoints:
(Pdb)bNum Type Disp Enb Where1 breakpoint keep yes at /code/util.py:1(Pdb)You can disable and re-enable breakpoints using the commanddisable bpnumber andenable bpnumber.bpnumber is the breakpoint number from the breakpoints list’s 1st columnNum. Notice theEnb column’s value change:
(Pdb)disable 1Disabled breakpoint 1 at /code/util.py:1(Pdb)bNum Type Disp Enb Where1 breakpoint keep no at /code/util.py:1(Pdb)enable 1Enabled breakpoint 1 at /code/util.py:1(Pdb)bNum Type Disp Enb Where1 breakpoint keep yes at /code/util.py:1(Pdb)To delete a breakpoint, use the commandcl (clear):
cl(ear) filename:linenocl(ear) [bpnumber [bpnumber...]]Now let’s use a Python expression to set a breakpoint. Imagine a situation where you wanted to break only if your troubled function received a certain input.
In this example scenario, theget_path() function is failing when it receives a relative path, i.e. the file’s path doesn’t start with/. I’ll create an expression that evaluates to true in this case and pass it tob as the 2nd argument:
$./example4.py> /code/example4.py(7)<module>()-> filename_path = util.get_path(filename)(Pdb)b util.get_path, not filename.startswith('/')Breakpoint 1 at /code/util.py:1(Pdb)c> /code/util.py(3)get_path()-> import os(Pdb)afilename = './example4.py'(Pdb)After you create the breakpoint above and enterc to continue execution, pdb stops when the expression evaluates to true. The commanda (args) prints the argument list of the current function.
In the example above, when you’re setting the breakpoint with a function name rather than a line number, note that the expression should use only function arguments orglobal variables that are available at the time the function is entered. Otherwise, the breakpoint will stop execution in the function regardless of the expression’s value.
If you need to break using an expression with a variable name located inside a function, i.e. a variable name not in the function’s argument list, specify the line number:
$./example4.py> /code/example4.py(7)<module>()-> filename_path = util.get_path(filename)(Pdb)b util:5, not head.startswith('/')Breakpoint 1 at /code/util.py:5(Pdb)c> /code/util.py(5)get_path()-> return head(Pdb)p head'.'(Pdb)afilename = './example4.py'(Pdb)You can also set a temporary breakpoint using the commandtbreak. It’s removed automatically when it’s first hit. It uses the same arguments asb.
So far, we’ve looked at stepping through code withn (next) ands (step) and using breakpoints withb (break) andc (continue).
There’s also a related command:unt (until).
Useunt to continue execution likec, but stop at the next line greater than the current line. Sometimesunt is more convenient and quicker to use and is exactly what you want. I’ll demonstrate this with an example below.
Let’s first look at the syntax and description forunt:
| Command | Syntax | Description |
|---|---|---|
unt | unt(il) [lineno] | Withoutlineno, continue execution until the line with a number greater than the current one is reached. Withlineno, continue execution until a line with a number greater or equal to that is reached. In both cases, also stop when the current frame returns. |
Depending on whether or not you pass the line number argumentlineno,unt can behave in two ways:
lineno, continue execution until the line with a number greater than the current one is reached. This is similar ton (next). It’s an alternate way to execute and “step over” code. The difference betweenn andunt is thatunt stops only when a line with a number greater than the current one is reached.n will stop at the next logically executed line.lineno, continue execution until a line with a number greater or equal to that is reached. This is likec (continue) with a line number argument.In both cases,unt stops when the current frame (function) returns, just liken (next) ands (step).
The primary behavior to note withunt is that it will stop when a line numbergreater or equal to the current or specified line is reached.
Useunt when you want to continue execution and stop farther down in the current source file. You can treat it like a hybrid ofn (next) andb (break), depending on whether you pass a line number argument or not.
In the example below, there is a function with a loop. Here, you want to continue execution of the code and stop after the loop, without stepping through each iteration of the loop or setting a breakpoint:
Here’s the example source forexample4unt.py:
#!/usr/bin/env python3importosdefget_path(fname):"""Return file's path or empty string if no path."""importpdb;pdb.set_trace()head,tail=os.path.split(fname)forcharintail:pass# Check filename charreturnheadfilename=__file__filename_path=get_path(filename)print(f'path ={filename_path}')And the console output usingunt:
$./example4unt.py> /code/example4unt.py(9)get_path()-> head, tail = os.path.split(fname)(Pdb)ll 6 def get_path(fname): 7 """Return file's path or empty string if no path.""" 8 import pdb; pdb.set_trace() 9 -> head, tail = os.path.split(fname) 10 for char in tail: 11 pass # Check filename char 12 return head(Pdb)unt> /code/example4unt.py(10)get_path()-> for char in tail:(Pdb)> /code/example4unt.py(11)get_path()-> pass # Check filename char(Pdb)> /code/example4unt.py(12)get_path()-> return head(Pdb)p char, tail('y', 'example4unt.py')Thell command was used first to print the function’s source, followed byunt. pdb remembers the last command entered, so I just pressedEnter to repeat theunt command. This continued execution through the code until a source line greater than the current line was reached.
Note in the console output above that pdb stopped only once on lines10 and11. Sinceunt was used, execution was stopped only in the 1st iteration of the loop. However, each iteration of the loop was executed. This can be verified in the last line of output. Thechar variable’s value'y' is equal to the last character intail’s value'example4unt.py'.
Similar to printing expressions withp andpp, you can use the commanddisplay [expression] to tell pdb to automatically display the value of an expression, if it changed, when execution stops. Use the commandundisplay [expression] to clear a display expression.
Here’s the syntax and description for both commands:
| Command | Syntax | Description |
|---|---|---|
display | display [expression] | Display the value ofexpression if it changed, each time execution stops in the current frame. Withoutexpression, list all display expressions for the current frame. |
undisplay | undisplay [expression] | Do not displayexpression any more in the current frame. Withoutexpression, clear all display expressions for the current frame. |
Below is an example,example4display.py, demonstrating its use with a loop:
$./example4display.py> /code/example4display.py(9)get_path()-> head, tail = os.path.split(fname)(Pdb)ll 6 def get_path(fname): 7 """Return file's path or empty string if no path.""" 8 import pdb; pdb.set_trace() 9 -> head, tail = os.path.split(fname) 10 for char in tail: 11 pass # Check filename char 12 return head(Pdb)b 11Breakpoint 1 at /code/example4display.py:11(Pdb)c> /code/example4display.py(11)get_path()-> pass # Check filename char(Pdb)display chardisplay char: 'e'(Pdb)c> /code/example4display.py(11)get_path()-> pass # Check filename chardisplay char: 'x' [old: 'e'](Pdb)> /code/example4display.py(11)get_path()-> pass # Check filename chardisplay char: 'a' [old: 'x'](Pdb)> /code/example4display.py(11)get_path()-> pass # Check filename chardisplay char: 'm' [old: 'a']In the output above, pdb automatically displayed the value of thechar variable because each time the breakpoint was hit its value had changed. Sometimes this is helpful and exactly what you want, but there’s another way to usedisplay.
You can enterdisplay multiple times to build a watch list of expressions. This can be easier to use thanp. After adding all of the expressions you’re interested in, simply enterdisplay to see the current values:
$./example4display.py> /code/example4display.py(9)get_path()-> head, tail = os.path.split(fname)(Pdb)ll 6 def get_path(fname): 7 """Return file's path or empty string if no path.""" 8 import pdb; pdb.set_trace() 9 -> head, tail = os.path.split(fname) 10 for char in tail: 11 pass # Check filename char 12 return head(Pdb)b 11Breakpoint 1 at /code/example4display.py:11(Pdb)c> /code/example4display.py(11)get_path()-> pass # Check filename char(Pdb)display chardisplay char: 'e'(Pdb)display fnamedisplay fname: './example4display.py'(Pdb)display headdisplay head: '.'(Pdb)display taildisplay tail: 'example4display.py'(Pdb)c> /code/example4display.py(11)get_path()-> pass # Check filename chardisplay char: 'x' [old: 'e'](Pdb)displayCurrently displaying:char: 'x'fname: './example4display.py'head: '.'tail: 'example4display.py'In this last section, we’ll build upon what we’ve learned so far and finish with a nice payoff. I use the name “caller ID” in reference to the phone system’s caller identification feature. That is exactly what this example demonstrates, except it’s applied to Python.
Here’s the source for the main scriptexample5.py:
#!/usr/bin/env python3importfileutildefget_file_info(full_fname):file_path=fileutil.get_path(full_fname)returnfile_pathfilename=__file__filename_path=get_file_info(filename)print(f'path ={filename_path}')Here’s the utility modulefileutil.py:
defget_path(fname):"""Return file's path or empty string if no path."""importosimportpdb;pdb.set_trace()head,tail=os.path.split(fname)returnheadIn this scenario, imagine there’s a large code base with a function in a utility module,get_path(), that’s being called with invalid input. However, it’s being called from many places in different packages.
How do you find who the caller is?
Use the commandw (where) to print a stack trace, with the most recent frame at the bottom:
$./example5.py> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)w /code/example5.py(12)<module>()-> filename_path = get_file_info(filename) /code/example5.py(7)get_file_info()-> file_path = fileutil.get_path(full_fname)> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)Don’t worry if this looks confusing or if you’re not sure what a stack trace or frame is. I’ll explain those terms below. It’s not as difficult as it might sound.
Since the most recent frame is at the bottom, start there and read from the bottom up. Look at the lines that start with->, but skip the 1st instance since that’s wherepdb.set_trace() was used to enter pdb in the functionget_path(). In this example, the source line that called the functionget_path() is:
-> file_path = fileutil.get_path(full_fname)The line above each-> contains the filename, line number (in parentheses), and function name the source line is in. So the caller is:
/code/example5.py(7)get_file_info()-> file_path = fileutil.get_path(full_fname)That’s no surprise in this small example for demonstration purposes, but imagine a large application where you’ve set a breakpoint with a condition to identify where a bad input value is originating.
Now we know how to find the caller.
But what about this stack trace and frame stuff?
Astack trace is just a list of all the frames that Python has created to keep track of function calls. A frame is a data structure Python creates when a function is called and deletes when it returns. The stack is simply an ordered list of frames or function calls at any point in time. The (function call) stack grows and shrinks throughout the life of an application as functions are called and then return.
When printed, this ordered list of frames, the stack, is called astack trace. You can see it at any time by entering the commandw, as we did above to find the caller.
See thiscall stack article on Wikipedia for details.
To understand better and get more out of pdb, let’s look more closely at the help forw:
(Pdb)h ww(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.What does pdb mean by “current frame”?
Think of the current frame as the current function where pdb has stopped execution. In other words, the current frame is where your application is currently paused and is used as the “frame” of reference for pdb commands likep (print).
p and other commands will use the current frame for context when needed. In the case ofp, the current frame will be used for looking up and printing variable references.
When pdb prints a stack trace, an arrow> indicates the current frame.
How is this useful?
You can use the two commandsu (up) andd (down) to change the current frame. Combined withp, this allows you to inspect variables and state in your application at any point along the call stack in any frame.
Here’s the syntax and description for both commands:
| Command | Syntax | Description |
|---|---|---|
u | u(p) [count] | Move the current framecount (default one) levels up in the stack trace (to an older frame). |
d | d(own) [count] | Move the current framecount (default one) levels down in the stack trace (to a newer frame). |
Let’s look at an example using theu andd commands. In this scenario, we want to inspect the variablefull_fname that’s local to the functionget_file_info() inexample5.py. In order to do this, we have to change the current frame up one level using the commandu:
$./example5.py> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)w /code/example5.py(12)<module>()-> filename_path = get_file_info(filename) /code/example5.py(7)get_file_info()-> file_path = fileutil.get_path(full_fname)> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)u> /code/example5.py(7)get_file_info()-> file_path = fileutil.get_path(full_fname)(Pdb)p full_fname'./example5.py'(Pdb)d> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)p fname'./example5.py'(Pdb)The call topdb.set_trace() is infileutil.py in the functionget_path(), so the current frame is initially set there. You can see it in the 1st line of output above:
> /code/fileutil.py(5)get_path()To access and print the local variablefull_fname in the functionget_file_info() inexample5.py, the commandu was used to move up one level:
(Pdb)u> /code/example5.py(7)get_file_info()-> file_path = fileutil.get_path(full_fname)Note in the output ofu above that pdb printed the arrow> at the beginning of the 1st line. This is pdb letting you know the frame was changed and this source location is now the current frame. The variablefull_fname is accessible now. Also, it’s important to realize the source line starting with-> on the 2nd line has been executed. Since the frame was moved up the stack,fileutil.get_path() has been called. Usingu, we moved up the stack (in a sense, back in time) to the functionexample5.get_file_info() wherefileutil.get_path() was called.
Continuing with the example, afterfull_fname was printed, the current frame was moved to its original location usingd, and the local variablefname inget_path() was printed.
If we wanted to, we could have moved multiple frames at once by passing thecount argument tou ord. For example, we could have moved to module level inexample5.py by enteringu 2:
$./example5.py> /code/fileutil.py(5)get_path()-> head, tail = os.path.split(fname)(Pdb)u 2> /code/example5.py(12)<module>()-> filename_path = get_file_info(filename)(Pdb)p filename'./example5.py'(Pdb)It’s easy to forget where you are when you’re debugging and thinking of many different things. Just remember you can always use the aptly named commandw (where) to see where execution is paused and what the current frame is.
Once you’ve spent a little time with pdb, you’ll realize a little knowledge goes a long way. Help is always available with theh command.
Just enterh orhelp <topic> to get a list of all commands or help for a specific command or topic.
For quick reference, here’s a list of essential commands:
| Command | Description |
|---|---|
p | Print the value of an expression. |
pp | Pretty-print the value of an expression. |
n | Continue execution until the next line in the current function is reached or it returns. |
s | Execute the current line and stop at the first possible occasion (either in a function that is called or in the current function). |
c | Continue execution and only stop when a breakpoint is encountered. |
unt | Continue execution until the line with a number greater than the current one is reached. With a line number argument, continue execution until a line with a number greater or equal to that is reached. |
l | List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing. |
ll | List the whole source code for the current function or frame. |
b | With no arguments, list all breaks. With a line number argument, set a breakpoint at this line in the current file. |
w | 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. |
u | Move the current frame count (default one) levels up in the stack trace (to an older frame). |
d | Move the current frame count (default one) levels down in the stack trace (to a newer frame). |
h | See a list of available commands. |
h <topic> | Show help for a command or topic. |
h pdb | Show the full pdb documentation. |
q | Quit the debugger and exit. |
In this tutorial, we covered a few basic and common uses of pdb:
n (next) ands (step)unt (until)I hope it’s been helpful to you. If you’re curious about learning more, see:
(Pdb) h pdbThe source code used in the examples can be found on the associatedGitHub repository. Be sure to check out our printable pdb Command Reference, which 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.
Also, if you’d like to try a GUI-based Python debugger, read ourPython IDEs and Editors Guide to see what options will work best for you. Happy Pythoning!
Recommended Course
🐍 Python Tricks 💌
Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

AboutNathan Jennings
Nathan is a member of the Real Python tutorial team who started his programmer career with C a long time ago, but eventually found Python. From web applications and data collection to networking and network security, he enjoys all things Pythonic.
» More about NathanMasterReal-World Python Skills With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
MasterReal-World Python Skills
With Unlimited Access to Real Python
Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:
What Do You Think?
What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.
Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.
Keep Learning
Already have an account?Sign-In
Almost there! Complete this form and click the button below to gain instant access:

Get The "pdb Command Reference" (Printable PDF)
Get aPython Cheat Sheet (PDF) and learn the basics of Python, like working with data types, dictionaries, lists, and Python functions:
