bdb
The Python bdb module provides a generic debugger framework for tracing and controlling Python program execution at the source-code level. It supplies the low-level machinery for setting breakpoints, stepping through code, and managing trace events so that higher-level tools like pdb can focus on user interaction.
Here’s a minimal custom debugger built on bdb:
line_tracer.py
import bdb
class LineTracer(bdb.Bdb):
def user_line(self, frame):
print(f"line {frame.f_lineno}: {frame.f_code.co_name}")
tracer = LineTracer()
tracer.run("x = 1 + 1\ny = x * 3")
Run it on the command line:
$ python line_tracer.py
line 1: <module>
line 2: <module>
The tracer calls .user_line() each time execution moves to a new source line.
Key Features
- Provides a
Bdbbase class to subclass when building custom debuggers - Runs Python statements, callables, and expressions under full debugger control via
Bdb.run(),Bdb.runcall(), andBdb.runeval() - Manages breakpoints through the
Breakpointclass, with support for temporary, conditional, and function-specific breakpoints - Supports two tracing backends:
settrace(default) andmonitoring(Python 3.14+, lower overhead) - Allows module-level skip patterns to avoid stepping into third-party or standard-library code
- Raises
BdbQuitto signal a clean exit from the debugging session
Frequently Used Classes and Functions
| Object | Type | Description |
|---|---|---|
bdb.Bdb |
Class | Serves as the generic debugger base class |
bdb.Breakpoint |
Class | Represents a single breakpoint with hit counts, conditions, and enable/disable support |
bdb.BdbQuit |
Exception | Signals a clean exit from the debugging session when raised |
bdb.Bdb.run() |
Method | Runs a string of Python statements under debugger control |
bdb.Bdb.runcall() |
Method | Calls a function with arguments under debugger control |
bdb.Bdb.set_break() |
Method | Sets a breakpoint at a given filename and line number |
bdb.Bdb.set_step() |
Method | Steps into the next statement, including any function calls |
bdb.Bdb.set_continue() |
Method | Continues execution until the next breakpoint is hit |
bdb.set_trace() |
Function | Starts debugging from the caller’s frame using a default Bdb instance |
Examples
Tracing return values from function calls:
return_demo.py
import bdb
class ReturnTracer(bdb.Bdb):
def user_return(self, frame, return_value):
if frame.f_code.co_name != "<module>":
print(
f"{frame.f_code.co_name} returned {return_value!r}"
)
tracer = ReturnTracer()
tracer.run("def double(n):\n return n * 2\ndouble(5)")
Run it on the command line:
$ python return_demo.py
double returned 10
user_return() intercepts every return event, making it straightforward to log or inspect return values without modifying the code under test.
Common Use Cases
The most common tasks for bdb include:
- Building custom debuggers by subclassing
Bdband implementinguser_line(),user_call(),user_return(), anduser_exception() - Running a specific function, expression, or code string under full debugger control with
Bdb.runcall(),Bdb.runeval(), orBdb.run() - Setting temporary breakpoints that are removed automatically after being hit once
- Applying conditional breakpoints that only pause execution when a given expression is true
- Skipping standard library or third-party modules during a debugging session
- Tracing program execution to record call graphs or gather profiling data
Real-World Example
The following example creates a call tracer that logs every function entry and return value during a debugging run:
call_tracer.py
import bdb
class CallTracer(bdb.Bdb):
def user_call(self, frame, argument_list):
name = frame.f_code.co_name
file = frame.f_code.co_filename
print(f"Calling: {name} in {file}")
self.set_step()
def user_return(self, frame, return_value):
name = frame.f_code.co_name
print(f"Returning: {name} -> {return_value!r}")
self.set_step()
def add(a, b):
return a + b
tracer = CallTracer()
tracer.runcall(add, 3, 4)
Run it on the command line:
$ python call_tracer.py
Returning: add -> 7
bdb.Bdb.runcall() runs the target function under debugger control and intercepts return events without modifying the original code. Note that user_call() fires for functions called within the target, not for the direct target itself.
Related Resources
Tutorial
Python Debugging With Pdb
In this hands-on tutorial, you'll learn the basics of using pdb, Python's interactive source code debugger. Pdb is a great tool for tracking down hard-to-find bugs and allows you to fix faulty code more quickly.
For additional information on related topics, take a look at the following resources:
- How to Debug Common Python Errors (Tutorial)
- Profiling in Python: How to Find Performance Bottlenecks (Tutorial)
- Python's assert: Debug and Test Your Code Like a Pro (Tutorial)
- Debugging in Python With pdb (Course)
- How to Debug Common Python Errors (Quiz)
- Profiling Performance in Python (Course)
- Using Python's assert to Debug and Test Your Code (Course)
By Leodanis Pozo Ramos • Updated Feb. 23, 2026