Debugging is an important part of software development, and having the right tools can significantly improve efficiency and accuracy in identifying issues.
ipdb is a debugging tool for Python that extends the functionality of the built-in Python debugger (pdb
).
It integrates features from IPython, such as syntax highlighting, tab completion, and an enhanced command set, making debugging more interactive and user-friendly.
With ipdb
, you can inspect variables, set breakpoints, and step through code more effectively.
This article'll explore how to leverage ipdb
to debug Python applications efficiently.
Prerequisites
Before getting started, ensure you have:
- The latest version of Python (3.13+) installed on your system.
- A basic understanding of building applications with Python.
Setting up the project directory
To keep things organized, you’ll set up a dedicated project directory and a virtual environment for your debugging examples.
Create a new directory for our debugging project and move into it:
mkdir python_debugging && cd python_debugging
Create a virtual environment:
python3 -m venv venv
Activate the virtual environment to ensure that any installed packages are contained within this project:
source venv/bin/activate
Now that you have your virtual environment activated, install ipdb
:
pip install ipdb
This isolates your debugging dependencies from your system Python installation and makes package management cleaner.
Debugging Python using ipdb from the command-line
The ipdb
package enhances Python's built-in debugger (pdb
) with features from IPython, including tab completion, syntax highlighting, and better tracebacks. It's particularly useful when you need to debug scripts directly from the command line.
Let's create a simple Python script that you'll use for our debugging examples. Save the following code as fibonacci.py
in your project directory:
def fibonacci(n):
"""Generate Fibonacci sequence up to n"""
fib_sequence = [0, 1]
while fib_sequence[-1] + fib_sequence[-2] <= n:
next_fib = fib_sequence[-1] + fib_sequence[-2]
fib_sequence.append(next_fib)
return fib_sequence
print(fibonacci(100))
When you run this script normally, it will calculate and print the Fibonacci sequence up to 100:
python fibonacci.py
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
To debug this script using ipdb
, you can invoke Python with the -m ipdb
flag followed by your script name:
python -m ipdb fibonacci.py
> /path/to/fibonacci.py(1)<module>()
----> 1 def fibonacci(n):
2 """Generate Fibonacci sequence up to n"""
3 fib_sequence = [0, 1]
ipdb>
When you launch ipdb
this way, several things happen:
- The debugger starts before any code executes
- Execution pauses at the first line of your script
- The
(Pdb+)
prompt appears, indicating that ipdb is ready to accept commands - The current line is displayed with an arrow (
->
) showing where execution will continue
From this point, you can control the execution flow using various debugger commands:
n
ornext
: Execute the current line and move to the next line in the same functions
orstep
: Step into a function callc
orcontinue
: Continue execution until the next breakpoint or end of programq
orquit
: Exit the debuggerl
orlist
: Show the code context around the current linep <expression>
: Print the value of an expressionpp <expression>
: Pretty print the value (useful for complex data structures)h
orhelp
: Display help about available commands
These commands form the foundation of your debugging toolkit. Let's explore how to use them effectively by setting a breakpoint inside the fibonacci
function using the b
or break
command:
(Pdb+) b 5
Breakpoint 1 at /path/to/fibonacci.py:5
Now you can continue execution until this breakpoint is hit:
(Pdb+) c
> path/to/fibonacci.py(5)fibonacci()
4
1---> 5 while fib_sequence[-1] + fib_sequence[-2] <= n:
6 next_fib = fib_sequence[-1] + fib_sequence[-2]
At this point, you can inspect variables using the p
command:
(Pdb+) p fib_sequence
[0, 1]
(Pdb+) p n
100
One of ipdb's strengths is the ability to enter IPython-style commands directly. For instance, you can use tab completion to explore available attributes or use ?
to get help on objects:
(Pdb+) fib_sequence.
Pressing tab here will show available methods on the list.
(Pdb+) fib_sequence?
This will display documentation about the list object.
To see all available local variables, you can use:
(Pdb+) locals()
{'n': 100, 'fib_sequence': [0, 1]}
To execute the next iteration of the loop, type n
:
(Pdb+) n
> /path/to/fibonacci.py(6)fibonacci()
1 5 while fib_sequence[-1] + fib_sequence[-2] <= n:
----> 6 next_fib = fib_sequence[-1] + fib_sequence[-2]
7 fib_sequence.append(next_fib)
You can then execute another step:
(Pdb+) n
> /path/to/fibonacci.py(7)fibonacci()
6 next_fib = fib_sequence[-1] + fib_sequence[-2]
----> 7 fib_sequence.append(next_fib)
8
And check the value of next_fib
:
(Pdb+) p next_fib
1
If you want to set a condition for your breakpoint to trigger only in specific situations, you can use a conditional breakpoint:
(Pdb+) b 5, next_fib > 50
Breakpoint 2 at /path/to/fibonacci.py:5
To see all active breakpoints, use:
(Pdb+) b
Num Type Disp Enb Where
1 breakpoint keep yes at /path/to/fibonacci.py:5
2 breakpoint keep yes at /path/to/fibonacci.py:5
stop only if next_fib > 50
Num Type Disp Enb Where
1 breakpoint keep yes at /path/to/fibonacci.py:5
breakpoint already hit 1 time
2 breakpoint keep yes at /path/to/fibonacci.py:5
stop only if next_fib > 50
You can remove a breakpoint using the clear
command followed by the breakpoint number:
(Pdb+) clear 1
Deleted breakpoint 1 at path/to/fibonacci.py
Now that you can use ipdb
to debug Python scripts from the command line, you will set up breakpoints in your code next.
Using ipdb
directly in your code
Instead of launching the debugger from the command line, you can insert breakpoints directly in your code using the set_trace()
function. This approach is particularly useful when:
- You already know where the problem might be
- You only want to examine a specific section of code
- You need to debug code that's called from another script
- You're working with a more complex application architecture
Let's see how to integrate ipdb directly into your Python code:
import ipdb
def fibonacci(n):
"""Generate Fibonacci sequence up to n"""
fib_sequence = [0, 1]
ipdb.set_trace() # Execution will pause here when running the script
while fib_sequence[-1] + fib_sequence[-2] <= n:
next_fib = fib_sequence[-1] + fib_sequence[-2]
fib_sequence.append(next_fib)
return fib_sequence
print(fibonacci(100))
When you run this script normally:
python fibonacci_with_breakpoint.py
The program execution will pause at the set_trace()
line, and you'll be dropped into the ipdb prompt:
> /path/to/fibonacci_with_breakpoint.py(9)fibonacci()
7
----> 8 ipdb.set_trace() # Execution will pause here when running the script
9
ipdb>
Notice a few differences compared to launching from the command line:
- The arrow
---->
shows the exact line that will execute next (this IPython-style formatting is more visually informative than standard pdb) - You see line numbers and surrounding code context automatically
- The debugger prompt is labeled
ipdb>
rather than(Pdb+)
(though functionally they're identical)
Using the built-in breakpoint()
function
Since Python 3.7, there's an even simpler way to add debugging breakpoints to your code using the built-in breakpoint()
function:
# remove import ipdb
def fibonacci(n):
"""Generate Fibonacci sequence up to n"""
fib_sequence = [0, 1]
breakpoint() # This will use ipdb if installed, or pdb by default
while fib_sequence[-1] + fib_sequence[-2] <= n:
# ...code continues...
This approach has several advantages:
- No imports required: You don't need to explicitly import ipdb
- Environment-aware: Uses whatever debugger is configured in your environment
- Easier to find: When scanning code,
breakpoint()
stands out as an intentional debugging statement - Quick to disable: You can disable all breakpoints by setting the
PYTHONBREAKPOINT=0
environment variable
When running the script with breakpoint()
, the execution pauses at the specified line, allowing you to inspect the current state of variables and interact with the debugger:
python fibonacci_with_breakpoint.py
> path/to/fibonacci_with_breakpoint.py(5)fibonacci()
-> breakpoint()
You can also disable all breakpoint()
calls:
PYTHONBREAKPOINT=0 python fibonacci_with_breakpoint.py
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
This flexibility makes breakpoint()
the preferred method for adding debugging points in modern Python code.
Handling exceptions with post-mortem debugging
Another handy feature of ipdb
is post-mortem debugging, enabling you to analyze errors after an exception. This is especially useful for diagnosing issues in production code or investigating unexpected failures.
Let's create a deliberately buggy script to demonstrate this technique:
def buggy_fibonacci(n):
"""A buggy Fibonacci implementation"""
fib_sequence = [] # Bug: empty list instead of [0, 1]!
while fib_sequence[-1] + fib_sequence[-2] <= n: # Will raise IndexError
next_fib = fib_sequence[-1] + fib_sequence[-2]
fib_sequence.append(next_fib)
return fib_sequence
print(buggy_fibonacci(100))
When you run this script, you'll get an error due to the empty list:
python buggy_fibonacci.py
File "/path/to/python_debugging/buggy_fibonacci.py", line 12, in <module>
print(buggy_fibonacci(100))
~~~~~~~~~~~~~~~^^^^^
File "/path/to/python_debugging/buggy_fibonacci.py", line 5, in buggy_fibonacci
while fib_sequence[-1] + fib_sequence[-2] <= n: # Will raise IndexError
~~~~~~~~~~~~^^^^
IndexError: list index out of range
Instead of modifying the code and running it repeatedly, you can examine the state at the point of failure using ipdb
's post-mortem capability:
python -m ipdb buggy_fibonacci.py
> /path/to/python_debugging/buggy_fibonacci.py(1)<module>()
----> 1 def buggy_fibonacci(n):
2 """A buggy Fibonacci implementation"""
3 fib_sequence = [] # Bug: empty list instead of [0, 1]!
ipdb> c
After pressing c
to continue execution, the error occurs and ipdb
automatically enters post-mortem mode, placing you at the exact point where the error happened:
Traceback (most recent call last):
...
IndexError: list index out of range
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /path/to/python_debugging/buggy_fibonacci.py(5)buggy_fibonacci()
4
----> 5 while fib_sequence[-1] + fib_sequence[-2] <= n: # Will raise IndexError
6 next_fib = fib_sequence[-1] + fib_sequence[-2]
ipdb>
You can examine variables, step through code, and determine what caused the error from this point. In this case, you'd discover that fib_sequence
is an empty list that needs proper initialization with the first two Fibonacci numbers [0, 1]
before attempting to access elements.
The power of post-mortem debugging is that we're now positioned exactly at the point of failure, with all variables in the state they were in when the error occurred. you can inspect the program's state to understand what went wrong:
ipdb> p fib_sequence
[]
ipdb> p n
100
The problem is now apparent: fib_sequence
is an empty list, but our algorithm is trying to access elements that don't exist. This insight would allow you to fix the list initialization in a real debugging scenario.
.
Creating command aliases
When debugging with ipdb
, you may repeatedly run the same commands or evaluate similar expressions. To simplify your workflow, ipdb
allows you to create command aliases, which act as shortcuts for frequently used commands.
Aliases help reduce typing effort and improve efficiency, especially when dealing with complex expressions or multi-step debugging processes. You can define an alias using the alias
command followed by the shortcut name and the command you want it to execute.
For example, if you frequently check the length of fib_sequence
, you can create an alias like this:
ipdb> alias pl p len(fib_sequence) # Create an alias for printing sequence length
ipdb> pl # Use the alias
0
Using aliases can make debugging smoother by reducing repetitive typing and improving efficiency.
Final thoughts
This article explored how ipdb
enhances Python debugging with a more interactive and efficient approach. You can simplify issue diagnosis by setting breakpoints, stepping through code, and using post-mortem debugging.
For more features and best practices, visit the official ipdb
documentation.
Make your mark
Join the writer's program
Are you a developer and love writing and sharing your knowledge with the world? Join our guest writing program and get paid for writing amazing technical guides. We'll get them to the right readers that will appreciate them.
Write for us
Build on top of Better Stack
Write a script, app or project on top of Better Stack and share it with the world. Make a public repository and share it with us at our email.
community@betterstack.comor submit a pull request and help us build better products for everyone.
See the full list of amazing projects on github