Skip to main content

PDB++

PDB++ (pdbpp) is a powerful, drop-in replacement for Python's built-in PDB debugger. It exists to make command-line debugging in Python faster, more productive, and more pleasant—without requiring you to learn a new tool or change your workflow. By extending the standard PDB with features like colorful tab completion, syntax highlighting, sticky mode, and smarter navigation, PDB++ helps developers debug complex problems with greater clarity and efficiency.

Unlike many alternatives, PDB++ is designed for seamless adoption: just install it, and your existing import pdb; pdb.set_trace() calls instantly gain new capabilities. Whether you're stepping through tricky bugs, inspecting variables, or navigating deep call stacks, PDB++ provides a modern, ergonomic experience for both beginners and advanced Python developers.

PDB++ is ideal for developers who want to debug Python code interactively in the terminal, with modern conveniences and minimal setup.

Basics

Installation and Setup

Installing PDB++

Install pdb++ using pip:

pip install pdbpp

Important: The package name is pdbpp, but you import and use it as pdb:

import pdb
pdb.set_trace() # This now uses pdb++ automatically

Automatic Replacement

Once installed, pdb++ automatically replaces the standard PDB module. No code changes are required:

# All of these now use pdb++ instead of standard pdb
import pdb; pdb.set_trace()
breakpoint() # Python 3.7+
python -m pdb script.py
pytest --pdb

Verification

To verify pdb++ is installed and working:

import pdb
print(pdb.__file__)
# Should show path containing 'pdbpp' in site-packages

Enhanced Debugging Workflow

Basic Usage

PDB++ maintains full compatibility with standard PDB commands while adding enhancements:

def calculate_average(numbers):
import pdb; pdb.set_trace() # Breakpoint with pdb++

total = 0
count = 0

for num in numbers:
total += num
count += 1

return total / count

# Test the function
numbers = [1, 2, 3, 4, 5]
result = calculate_average(numbers)
print(f"Average: {result}")

When the debugger starts, you'll notice immediate visual improvements:

> /path/to/script.py(3)calculate_average()
-> total = 0
(Pdb++)

Notice the colorful prompt (Pdb++) indicating pdb++ is active.

PDB++ Commands 🔖

# Enhanced pdb++ specific commands
sticky # Toggle sticky mode (visual step-by-step debugging)
longlist / ll # Show entire current function with syntax highlighting
display <expr> # Add expression to display list (auto-monitor variables)
undisplay <num> # Remove from display list
interact # Start interactive Python interpreter in current scope

# Navigation and flow control
n / next # Execute next line (don't step into functions)
s / step # Step into function calls
c / continue # Continue execution until next breakpoint
r / return # Continue until current function returns
f / finish # Continue until current function returns (alias for r)
u / up # Move up one stack frame
d / down # Move down one stack frame
j <line> / jump # Jump to specific line number
until <line> # Continue until line number
unt # Continue until line greater than current

# Breakpoints (enhanced in pdb++)
b # List all breakpoints
b <line> # Set breakpoint at line number in current file
b <file:line> # Set breakpoint at specific file and line
b <function> # Set breakpoint at function entry
b <line>, <condition> # Set conditional breakpoint
tbreak <line> # Set temporary breakpoint (removed after first hit)
cl # Clear all breakpoints
cl <number> # Clear specific breakpoint by number
condition <number> <condition> # Add/change condition for breakpoint

# Information and inspection (enhanced output)
l / list # Show current code (with syntax highlighting)
ll / longlist # Show entire function (enhanced in pdb++)
w / where # Show stack trace (enhanced formatting)
bt # Show stack trace (alias)
p <expr> # Print expression (with highlighting)
pp <expr> # Pretty-print expression (enhanced colors)
a / args # Show function arguments
whatis <expr> # Show type of expression
source <expr> # Show source code of object
rv / retval # Show return value

# Variable commands (enhanced)
!<statement> # Execute Python statement
interact # Start interactive interpreter (pdb++ feature)
alias <name> <command> # Create command alias
unalias <name> # Remove alias
display <expr> # Auto-display expression (enhanced in pdb++)
undisplay <expr> # Stop auto-displaying

# Object and namespace inspection (enhanced output)
locals # Print all local variables (with highlighting)
globals # Print all global variables (with highlighting)
dir() # List names in current namespace
dir(<object>) # List attributes of object
vars() # Show local variables as dictionary
vars(<object>) # Show object's attributes as dictionary

# System commands
q / quit # Quit debugger and terminate program
exit # Same as q
restart # Restart the debugged program
run [args] # Restart program with optional command line arguments

# Hidden frame management (pdb++ feature)
hf_hide # Hide frames marked as hidden
hf_unhide # Show all frames including hidden ones

# Help and information (enhanced formatting)
h # Show help message (enhanced in pdb++)
h <command> # Show help for specific command
? # Same as h
? <command> # Same as h <command>

# Special variables
__return__ # Return value of current function (when at return)
__exception__ # Current exception being handled
__args__ # Arguments passed to current function

# Enhanced features shortcuts
<Tab> # Enhanced tab completion with syntax highlighting
<Enter> # Repeat last command
sticky # Toggle visual step-by-step mode
ll # Show full function context
interact # Enter interactive Python shell

Key Enhanced Features

Enhanced Tab Completion

PDB++ provides intelligent tab completion with syntax highlighting:

# Example debugging session
def process_data(data_dict):
import pdb; pdb.set_trace()

result = {}
for key, value in data_dict.items():
result[key] = value * 2

return result

data = {"a": 1, "b": 2, "c": 3}
process_data(data)

In the debugger:

(Pdb++) data_d<TAB>
# Shows: data_dict with syntax highlighting

(Pdb++) data_dict.<TAB>
# Shows available methods: items(), keys(), values(), etc.
# With colorful syntax highlighting

(Pdb++) data_dict.k<TAB>
# Completes to: data_dict.keys()

Smart Command Parsing

PDB++ intelligently handles commands vs. variable names. Consider this example:

def example_function():
import pdb; pdb.set_trace()

# Variables that conflict with pdb commands
c = "some string" # 'c' is also a pdb command (continue)
r = 42 # 'r' is also a pdb command (return)
l = [1, 2, 3] # 'l' is also a pdb command (list)

return c, r, l

example_function()

In standard PDB, typing c would continue execution. In PDB++:

(Pdb++) c
# Shows the variable value: 'some string'

(Pdb++) !c
# Executes the continue command (explicit command)

(Pdb++) pp c
# Pretty-prints the variable (alternative)

Colorful Output

PDB++ provides syntax highlighting for code listings and variable values:

def complex_function():
import pdb; pdb.set_trace()

data = {
"users": [
{"name": "Alice", "age": 30, "active": True},
{"name": "Bob", "age": 25, "active": False},
],
"settings": {
"theme": "dark",
"notifications": True
}
}

return data

complex_function()

Commands with enhanced output:

(Pdb++) pp data
# Shows pretty-printed data with syntax highlighting

(Pdb++) l
# Shows code listing with syntax highlighting

(Pdb++) data['users'][0]
# Shows dictionary access with highlighted output

Sticky Mode

Sticky mode provides visual step-by-step execution:

def fibonacci(n):
import pdb; pdb.set_trace()

if n <= 1:
return n

a, b = 0, 1
for i in range(2, n + 1):
a, b = b, a + b

return b

fibonacci(10)

In the debugger:

(Pdb++) sticky
# Enables sticky mode - screen repaints on each step

(Pdb++) s
# Step into - screen shows entire function with current line highlighted

(Pdb++) s
# Next step - screen updates to show new position

Longlist Command

The longlist (or ll) command shows the entire current function:

def process_order(order_data):
import pdb; pdb.set_trace()

# Validate order
if not order_data.get('items'):
raise ValueError("Order must contain items")

# Calculate total
total = 0
for item in order_data['items']:
total += item['price'] * item['quantity']

# Apply discount
discount = order_data.get('discount', 0)
total *= (1 - discount)

# Add tax
tax_rate = 0.08
total *= (1 + tax_rate)

return {
'total': total,
'items_count': len(order_data['items']),
'discount_applied': discount > 0
}

order = {
'items': [
{'price': 10.00, 'quantity': 2},
{'price': 15.50, 'quantity': 1}
],
'discount': 0.1
}

process_order(order)

In the debugger:

(Pdb++) ll
# Shows the entire function with syntax highlighting

(Pdb++) l
# Shows only a few lines around current position (standard behavior)

Configuration

Customize your PDB++ debugging experience with advanced configuration options, syntax highlighting, and personalized settings.

Basic Configuration with .pdbrc

Creating a .pdbrc File

Create a .pdbrc file in your home directory or project root:

# ~/.pdbrc - Global configuration
# or
# ./.pdbrc - Project-specific configuration

# Import necessary modules
import pdb
import sys
from pprint import pprint

# Enable automatic sticky mode
pdb.Pdb.do_sticky = lambda self, arg: self.sticky_toggle()

# Custom aliases for common commands
alias pd pp %1 # Pretty print shorthand
alias st sticky # Sticky mode toggle
alias ll longlist # Long list alias
alias i interact # Interactive shell
alias dt display # Display variable
alias ut undisplay # Remove from display

# Auto-import commonly used modules
import os
import json
import datetime as dt
import collections

# Custom pretty printer for complex objects
def pp_custom(obj):
"""Enhanced pretty printer with type information."""
print(f"Type: {type(obj).__name__}")
if hasattr(obj, '__dict__'):
print("Attributes:")
for key, value in obj.__dict__.items():
print(f" {key}: {repr(value)}")
else:
pprint(obj)

# Make it available in debugging sessions
alias ppc pp_custom(%1)

Syntax Highlighting Configuration

Pygments Configuration

Configure syntax highlighting colors and styles:

# ~/.pdbrc - Syntax highlighting configuration

# Configure Pygments for better syntax highlighting
import pygments
from pygments.formatters import TerminalFormatter, Terminal256Formatter
from pygments.styles import get_style_by_name

# Set preferred color scheme
# Available styles: monokai, solarized-dark, solarized-light,
# github, vim, emacs, default, etc.
PREFERRED_STYLE = 'monokai'

try:
# Try to use 256-color terminal if available
import termcolor
formatter = Terminal256Formatter(style=PREFERRED_STYLE)
except ImportError:
# Fallback to basic terminal colors
formatter = TerminalFormatter(style=PREFERRED_STYLE)

# Function to highlight code with custom style
def highlight_code(code, lexer_name='python'):
"""Highlight code with custom styling."""
from pygments import highlight
from pygments.lexers import get_lexer_by_name

lexer = get_lexer_by_name(lexer_name)
return highlight(code, lexer, formatter)

# Make available in debugging sessions
alias hl highlight_code(%1)

Display List Configuration

Automatic Variable Monitoring

Configure automatic display of important variables:

# ~/.pdbrc - Display list configuration

# Function to automatically set up common display variables
def auto_display_setup():
"""Set up common variables for automatic display."""
local_vars = locals()

# Common variable names to auto-display
common_vars = ['self', 'request', 'response', 'data', 'result',
'items', 'user', 'obj', 'instance']

for var_name in common_vars:
if var_name in local_vars:
print(f"Auto-displaying: {var_name}")
# Note: actual display command needs to be run manually
print(f"Run: display {var_name}")

alias autodisp auto_display_setup()

# Function to clear all displays
def clear_displays():
"""Clear all display expressions."""
# This needs to be implemented based on current display list
print("Use 'undisplay' command to clear individual displays")
print("or restart debugger to clear all displays")

alias cleardisp clear_displays()

Environment Variables

Configure PDB++ behavior through environment variables:

# ~/.bashrc or ~/.zshrc

# Enable syntax highlighting by default
export PDBPP_SYNTAX_HIGHLIGHTING=1

# Set default color scheme
export PDBPP_COLOR_SCHEME=monokai

# Configure tab completion
export PDBPP_TAB_COMPLETION=enhanced

# Set sticky mode as default
export PDBPP_STICKY_MODE=1

# Configure history size
export PDBPP_HISTORY_SIZE=10000

# Set custom pdbrc location
export PDBRC_PATH=~/.config/pdb/pdbrc

Complete Development Configuration

A comprehensive .pdbrc for general development:

# ~/.pdbrc - Complete development configuration

import os
import sys
import json
from pprint import pprint
from datetime import datetime

# ============================================================================
# ALIASES - Common command shortcuts
# ============================================================================

# Navigation aliases
alias u up
alias d down
alias st sticky
alias ll longlist
alias i interact

# Display aliases
alias pd pp %1
alias dt display %1
alias ut undisplay %1

# Inspection aliases
alias vars pp(vars())
alias locals pp(locals())
alias globals pp(globals())
alias modules pp(list(sys.modules.keys()))

# Utility aliases
alias time print(datetime.now())
alias pwd print(os.getcwd())
alias env pp(dict(os.environ))

# ============================================================================
# UTILITY FUNCTIONS
# ============================================================================

def inspect_object(obj):
"""Comprehensive object inspection."""
print(f"\n=== Object Inspection ===")
print(f"Type: {type(obj)}")
print(f"Class: {obj.__class__.__name__}")
print(f"Module: {obj.__class__.__module__}")

if hasattr(obj, '__dict__'):
print(f"\nAttributes ({len(obj.__dict__)}):")
for key, value in obj.__dict__.items():
print(f" {key}: {type(value).__name__} = {repr(value)[:50]}")

print(f"\nMethods:")
methods = [method for method in dir(obj) if callable(getattr(obj, method))]
for method in methods[:10]: # Show first 10 methods
print(f" {method}()")
if len(methods) > 10:
print(f" ... and {len(methods) - 10} more methods")

alias inspect inspect_object(%1)

def show_frame_info():
"""Show current frame information."""
frame = sys._getframe(1) # Get caller's frame
print(f"\n=== Frame Information ===")
print(f"Function: {frame.f_code.co_name}")
print(f"File: {frame.f_code.co_filename}")
print(f"Line: {frame.f_lineno}")
print(f"Local variables: {len(frame.f_locals)}")
print(f"Global variables: {len(frame.f_globals)}")

alias frameinfo show_frame_info()

def quick_test():
"""Quick test current function with sample data."""
print("Quick testing functionality:")
print("1. Check current variables")
print("2. Test with simple inputs")
print("3. Verify expected outputs")
print("\nUse 'interact' for interactive testing")

alias qtest quick_test()

# ============================================================================
# STARTUP MESSAGE
# ============================================================================

print("\n" + "="*60)
print("PDB++ Enhanced Debugger - Custom Configuration Loaded")
print("="*60)
print("Available aliases:")
print(" Navigation: u, d, st, ll, i")
print(" Display: pd, dt, ut")
print(" Inspection: inspect, frameinfo, vars, locals, globals")
print(" Utilities: time, pwd, env, qtest")
print("="*60)

Tutorials

Master PDB++ through hands-on tutorials that demonstrate enhanced debugging features with practical, real-world examples.

Tutorial 1: Getting Started with Enhanced Debugging

Learn the fundamental differences between standard PDB and PDB++ through a practical debugging session.

Scenario: Debugging a Data Processing Pipeline

We'll debug a data processing function that has several issues:

# tutorial1_data_processor.py
def process_user_data(users_data):
"""Process user data and calculate statistics."""
import pdb; pdb.set_trace() # PDB++ breakpoint

processed_users = []
total_age = 0
active_users = 0

for user in users_data:
# Process each user
user_info = {
'id': user['id'],
'name': user['name'].title(),
'age': user['age'],
'is_active': user.get('active', False)
}

# Calculate running totals
total_age += user['age']
if user_info['is_active']:
active_users += 1

processed_users.append(user_info)

# Calculate final statistics
average_age = total_age / len(users_data)
activity_rate = active_users / len(users_data) * 100

return {
'users': processed_users,
'statistics': {
'total_users': len(users_data),
'average_age': round(average_age, 2),
'activity_rate': round(activity_rate, 2),
'active_users': active_users
}
}

# Test data with intentional issues
test_users = [
{'id': 1, 'name': 'alice smith', 'age': 25, 'active': True},
{'id': 2, 'name': 'bob jones', 'age': 30}, # Missing 'active' key
{'id': 3, 'name': 'charlie brown', 'age': 35, 'active': False},
{'id': 4, 'name': 'diana prince', 'age': 28, 'active': True}
]

if __name__ == "__main__":
result = process_user_data(test_users)
print("Processing complete!")
print(f"Total users: {result['statistics']['total_users']}")
print(f"Average age: {result['statistics']['average_age']}")
print(f"Activity rate: {result['statistics']['activity_rate']}%")

Debugging Session Walkthrough

Run the script and follow along with the debugging session:

python tutorial1_data_processor.py

Step 1: Initial Breakpoint

> tutorial1_data_processor.py(4)process_user_data()
-> processed_users = []
(Pdb++)

Notice the colorful (Pdb++) prompt indicating enhanced PDB is active.

Step 2: Examine Input Data with Enhanced Tab Completion

(Pdb++) users_data<TAB>
# Shows: users_data (with syntax highlighting)

(Pdb++) pp users_data
# Pretty-printed output with syntax highlighting:
[{'active': True, 'age': 25, 'id': 1, 'name': 'alice smith'},
{'age': 30, 'id': 2, 'name': 'bob jones'},
{'active': False, 'age': 35, 'id': 3, 'name': 'charlie brown'},
{'active': True, 'age': 28, 'id': 4, 'name': 'diana prince'}]

Step 3: Use Enhanced Navigation

(Pdb++) ll
# Shows entire function with syntax highlighting
# Much better than standard 'l' command

(Pdb++) l
# Shows traditional limited view for comparison

Step 4: Smart Command Parsing

(Pdb++) n
# Step to next line

(Pdb++) n
# Step to next line

(Pdb++) n
# Now we're in the for loop

(Pdb++) user
# In standard PDB, this might conflict with commands
# In PDB++, it shows the variable value with highlighting
{'id': 1, 'name': 'alice smith', 'age': 25, 'active': True}

Step 5: Set Up Display List

(Pdb++) display len(processed_users)
display len(processed_users): 0

(Pdb++) display total_age
display total_age: 0

(Pdb++) display active_users
display active_users: 0

(Pdb++) n
# As you step, watch the display values update automatically

Step 6: Interactive Exploration

(Pdb++) interact
# Starts interactive Python shell with current scope

>>> user.get('active', 'MISSING')
True
>>> len(users_data)
4
>>> [u.get('active', 'MISSING') for u in users_data]
[True, 'MISSING', False, True]
>>> exit() # Return to debugger

(Pdb++)

Tutorial 2: Using Sticky Mode for Visual Debugging

Learn to use sticky mode for visual, step-by-step debugging that shows your entire function context.

Scenario: Debugging a Recursive Algorithm

We'll debug a recursive function with a subtle bug:

# tutorial2_fibonacci.py
def fibonacci_with_cache(n, cache=None):
"""Calculate Fibonacci number with memoization."""
import pdb; pdb.set_trace()

# Initialize cache if not provided
if cache is None:
cache = {}

# Base cases
if n <= 1:
return n

# Check cache first
if n in cache:
print(f"Cache hit for n={n}")
return cache[n]

# Calculate recursively
print(f"Calculating fib({n})")
result = fibonacci_with_cache(n-1, cache) + fibonacci_with_cache(n-2, cache)

# Store in cache
cache[n] = result
return result

def test_fibonacci():
"""Test the fibonacci function."""
print("Testing Fibonacci calculation...")

# Test several values
test_values = [0, 1, 2, 3, 5, 8]

for n in test_values:
result = fibonacci_with_cache(n)
print(f"fib({n}) = {result}")

if __name__ == "__main__":
test_fibonacci()

Sticky Mode Debugging Session

Step 1: Enable Sticky Mode

python tutorial2_fibonacci.py

When the debugger starts:

(Pdb++) sticky
# Sticky mode enabled - screen will repaint on each step

Step 2: Visual Step-by-Step Execution

With sticky mode enabled, each step will show the entire function with your current position highlighted:

(Pdb++) s
# Screen shows entire function with current line highlighted
# Much easier to follow execution flow

(Pdb++) s
# Screen updates to show new position
# You can see the complete context at all times

Step 3: Monitor Recursive Calls

(Pdb++) display n
display n: 0

(Pdb++) display cache
display cache: {}

(Pdb++) c
# Continue to next breakpoint (recursive call)
# Watch how n and cache change between calls

Tutorial 3: Interactive Interpreter and Variable Inspection

Learn to leverage PDB++'s interactive interpreter for complex debugging scenarios and advanced variable analysis.

Interactive Debugging Session

# tutorial3_data_analysis.py
import json
from datetime import datetime, timedelta
from collections import defaultdict

class DataAnalyzer:
"""Analyzes user activity data."""

def __init__(self):
self.raw_data = []
self.processed_data = {}
self.analysis_results = {}

def analyze_patterns(self):
"""Analyze user activity patterns."""
import pdb; pdb.set_trace()

self.analysis_results = {}

for user_id, activities in self.processed_data.items():
user_analysis = {
'total_activities': len(activities),
'activity_types': {},
'hourly_distribution': defaultdict(int),
'daily_distribution': defaultdict(int),
'most_active_day': None,
'most_active_hour': None
}

# Analyze activity types
for activity in activities:
activity_type = activity['activity']
user_analysis['activity_types'][activity_type] = \
user_analysis['activity_types'].get(activity_type, 0) + 1

# Track hourly and daily patterns
user_analysis['hourly_distribution'][activity['hour']] += 1
user_analysis['daily_distribution'][activity['day_of_week']] += 1

self.analysis_results[user_id] = user_analysis

return self.analysis_results

Interactive Analysis Session:

(Pdb++) interact
# Start interactive interpreter

>>> # Explore user activities
>>> for user_id, activities in self.processed_data.items():
... print(f"User {user_id}: {len(activities)} activities")
User user_001: 3 activities
User user_002: 2 activities

>>> # Check activity types
>>> user_001_activities = self.processed_data['user_001']
>>> [activity['activity'] for activity in user_001_activities]
['login', 'page_view', 'logout']

>>> # Custom analysis function
>>> def analyze_user_behavior(user_id):
... """Custom analysis function for interactive debugging."""
... if user_id not in self.processed_data:
... return "User not found"
...
... activities = self.processed_data[user_id]
... timeline = sorted(activities, key=lambda x: x['timestamp'])
... return {'timeline': timeline, 'count': len(activities)}

>>> behavior = analyze_user_behavior('user_001')
>>> behavior['count']
3

>>> exit()

Integration

Integrate PDB++ into your development workflow with IDEs, frameworks, testing tools, and CI/CD pipelines for enhanced debugging productivity.

IDE Integration

Visual Studio Code Integration

PDB++ works seamlessly with VS Code's Python debugging capabilities and can be used alongside or instead of the built-in debugger.

Basic VS Code Setup:

  1. Install PDB++ in your environment:
pip install pdbpp
  1. Configure VS Code settings (.vscode/settings.json):
{
"python.terminal.activateEnvironment": true,
"python.debugging.console": "integratedTerminal",
"python.linting.enabled": true,
"terminal.integrated.env.osx": {
"PYTHONBREAKPOINT": "pdb.set_trace"
},
"terminal.integrated.env.linux": {
"PYTHONBREAKPOINT": "pdb.set_trace"
},
"terminal.integrated.env.windows": {
"PYTHONBREAKPOINT": "pdb.set_trace"
}
}
  1. Create launch configuration (.vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File with PDB++",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}",
"env": {
"PYTHONBREAKPOINT": "pdb.set_trace",
"PYTHONPATH": "${workspaceFolder}"
},
"justMyCode": false
}
]
}

PyCharm Integration

PyCharm can work with PDB++ through external debugging sessions.

PyCharm Setup:

  1. Configure external tools in PyCharm:
    • Go to File → Settings → Tools → External Tools
    • Add new tool:
      • Name: "Debug with PDB++"
      • Program: python
      • Arguments: -m pdb $FilePath$
      • Working directory: $ProjectFileDir$

Testing Integration

pytest Integration

Enhanced testing with PDB++ and pytest:

# conftest.py
import pytest
import os

# Ensure PDB++ is used for debugging tests
os.environ.setdefault('PYTHONBREAKPOINT', 'pdb.set_trace')

@pytest.fixture
def sample_data():
"""Sample data fixture for testing."""
return {
'users': [
{'id': 1, 'name': 'Alice', 'active': True},
{'id': 2, 'name': 'Bob', 'active': False}
],
'settings': {
'theme': 'dark',
'notifications': True
}
}

class DebugHelper:
"""Helper class for test debugging."""

@staticmethod
def debug_test_data(data, description="Test data"):
"""Debug test data with PDB++."""
import pdb; pdb.set_trace()
print(f"Debugging: {description}")
print(f"Data type: {type(data)}")
print(f"Data: {data}")

@pytest.fixture
def debug_helper():
"""Debug helper fixture."""
return DebugHelper()

Test Cases with PDB++:

# test_with_pdbpp.py
import pytest

def test_data_processing_with_debug(sample_data, debug_helper):
"""Test data processing with debugging support."""
import pdb; pdb.set_trace()

# Debug input data
users = sample_data['users']
active_users = [user for user in users if user['active']]

# Use debug helper
debug_helper.debug_test_data(active_users, "Active users")

assert len(active_users) == 1
assert active_users[0]['name'] == 'Alice'

def test_error_handling_with_debug():
"""Test error handling with debugging."""
def problematic_function(data):
import pdb; pdb.set_trace()

if not data:
raise ValueError("Data cannot be empty")

return len(data)

# Test normal case
result = problematic_function([1, 2, 3])
assert result == 3

# Test error case with debugging
with pytest.raises(ValueError):
# Debugger will stop here to examine the error
problematic_function([])

Running Tests with PDB++:

# Run tests with PDB++ debugging
pytest --pdb # Drop into debugger on failures
pytest --pdbcls=pdb:Pdb # Explicitly use PDB++
pytest -s test_file.py # Show output (don't capture)
pytest --tb=short --pdb # Short traceback with debugger

# Run specific test with debugging
pytest -s -k "test_data_processing" --pdb

# Debug test setup/teardown
pytest --setup-show --pdb

CI/CD Integration

GitHub Actions with PDB++

Configure GitHub Actions for debugging with PDB++:

# .github/workflows/debug-tests.yml
name: Debug Tests with PDB++

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
debug-tests:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.9'

- name: Install dependencies
run: |
pip install --upgrade pip
pip install pdbpp pytest
pip install -r requirements.txt

- name: Run tests with debugging info
run: |
# Set environment for PDB++
export PYTHONBREAKPOINT=pdb.set_trace

# Run tests with verbose output
pytest -v --tb=short

# Run specific debug tests (non-interactive)
pytest tests/test_debug.py -v

- name: Debug on failure
if: failure()
run: |
echo "Tests failed - debugging information:"
python -c "
import sys
print('Python version:', sys.version)
import pdb
print('PDB module:', pdb.__file__)
try:
import pdbpp
print('PDB++ available')
except ImportError:
print('PDB++ not available')
"

Docker Integration

Dockerfile for development with PDB++:

# Dockerfile.debug
FROM python:3.9-slim

# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /app

# Copy requirements
COPY requirements.txt .
COPY requirements-dev.txt .

# Install Python dependencies including PDB++
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install --no-cache-dir -r requirements-dev.txt
RUN pip install pdbpp

# Set environment for debugging
ENV PYTHONBREAKPOINT=pdb.set_trace
ENV PYTHONUNBUFFERED=1

# Copy application code
COPY . .

# Expose port for web applications
EXPOSE 8000

# Default command
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

Docker Compose for debugging:

# docker-compose.debug.yml
version: '3.8'

services:
app:
build:
context: .
dockerfile: Dockerfile.debug
ports:
- "8000:8000"
volumes:
- .:/app
environment:
- PYTHONBREAKPOINT=pdb.set_trace
- DEBUG=1
stdin_open: true # Enable interactive debugging
tty: true # Allocate TTY for PDB++
command: python manage.py runserver 0.0.0.0:8000

db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
ports:
- "5432:5432"

Comparison with pdbp (Modern Alternative)

pdbp is a modern alternative to pdbpp that addresses some compatibility issues:

# pdbp_comparison.py
"""
Comparison between pdbpp and pdbp for modern Python development.
"""

# pdbp installation and usage
"""
# Install pdbp (modern alternative)
pip install pdbp

# Usage (same as pdbpp)
import pdb; pdb.set_trace() # Uses pdbp if installed

# Or explicitly
import pdbp; pdbp.set_trace()
"""

def demo_pdbp_features():
"""Demonstrate pdbp features."""
import pdb; pdb.set_trace() # Will use pdbp if installed

# pdbp features (similar to pdbpp):
# - Syntax highlighting
# - Tab completion
# - Sticky mode
# - Better Windows support (Python 3.11+)
# - Improved dependencies (tabcompleter vs fancycompleter)

data = {
'users': ['Alice', 'Bob', 'Charlie'],
'settings': {'theme': 'dark', 'debug': True}
}

# Use all the same commands as pdbpp
return data

Migration from pdbpp to pdbp:

# Uninstall pdbpp
pip uninstall pdbpp fancycompleter

# Install pdbp
pip install pdbp

# No code changes needed - same interface

Feature Comparison:

FeaturepdbpppdbpNotes
Syntax HighlightingBoth support colorful output
Tab Completionpdbp uses tabcompleter
Sticky ModeSame functionality
Windows Support⚠️pdbp better for Python 3.11+
Dependenciesfancycompletertabcompleterpdbp has fewer issues
Drop-in ReplacementBoth replace standard pdb
Active Development⚠️pdbp more actively maintained

Further Reading

Official Documentation

Features and Guides

Development Tools


PDB++ transforms terminal-based debugging with enhanced features and modern conveniences—master it to debug Python applications more effectively with colorful output, smart completion, and visual debugging modes.