Skip to content

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:

Python 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:

Shell
$ 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 Bdb base class to subclass when building custom debuggers
  • Runs Python statements, callables, and expressions under full debugger control via Bdb.run(), Bdb.runcall(), and Bdb.runeval()
  • Manages breakpoints through the Breakpoint class, with support for temporary, conditional, and function-specific breakpoints
  • Supports two tracing backends: settrace (default) and monitoring (Python 3.14+, lower overhead)
  • Allows module-level skip patterns to avoid stepping into third-party or standard-library code
  • Raises BdbQuit to 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:

Python 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:

Shell
$ 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 Bdb and implementing user_line(), user_call(), user_return(), and user_exception()
  • Running a specific function, expression, or code string under full debugger control with Bdb.runcall(), Bdb.runeval(), or Bdb.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:

Python 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:

Shell
$ 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.

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.

intermediate stdlib tools

For additional information on related topics, take a look at the following resources:


By Leodanis Pozo Ramos • Updated Feb. 23, 2026