Skip to content

Python breakpoint() for debugging instead of print()

I used to debug Python code the hard way. Every time something went wrong, I’d sprinkle print statements throughout my code, run it, check the output, add more print statements, run it again, and repeat until I found the problem.

messy_debug.py
def process_data(items):
print(f"Processing {len(items)} items")
results = []
for i, item in enumerate(items):
print(f"Item {i}: {item}")
processed = item * 2
print(f"Processed: {processed}")
results.append(processed)
print(f"Final results: {results}")
return results

This approach is tedious and clutters the code. Then I discovered Python’s built-in breakpoint() function.

The Old Way vs. The New Way

The print debugging workflow looked like this:

Add print statements → Run code → Read output → Add more prints → Run again → Remove prints later

With breakpoint(), I can pause execution at any point and interactively inspect what’s happening:

clean_debug.py
def process_data(items):
results = []
for i, item in enumerate(items):
processed = item * 2
breakpoint() # Pause here and investigate
results.append(processed)
return results

When execution hits breakpoint(), I get an interactive debugger prompt where I can examine any variable, step through code line by line, and understand exactly what’s happening.

How It Works

Python 3.7 introduced breakpoint() as a built-in function that drops you into the debugger. No imports needed:

simple_example.py
def calculate_total(prices, tax_rate):
subtotal = sum(prices)
tax = subtotal * tax_rate
breakpoint() # Check values here
total = subtotal + tax
return total
calculate_total([10, 20, 30], 0.08)

When I run this, execution pauses at the breakpoint and I see:

> /path/to/simple_example.py(5)calculate_total()
-> total = subtotal + tax
(Pdb)

At this prompt, I can type any variable name to see its value:

(Pdb) subtotal
60
(Pdb) tax
4.8
(Pdb) prices
[10, 20, 30]

I can even run arbitrary Python code:

(Pdb) subtotal * 0.1
6.0
(Pdb) len(prices)
3

Essential pdb Commands

Once inside the debugger, these commands become essential:

CommandAction
n (next)Execute current line and move to next
s (step)Step into function calls
c (continue)Continue execution until next breakpoint
p <var>Print variable value
l (list)Show current code context
h (help)Show all commands
q (quit)Exit debugger and program

For example, after hitting a breakpoint:

(Pdb) l
1 def calculate_total(prices, tax_rate):
2 subtotal = sum(prices)
3 tax = subtotal * tax_rate
4 breakpoint()
5 -> total = subtotal + tax
6 return total
(Pdb) n
> /path/to/simple_example.py(6)calculate_total()
-> return total
(Pdb) p total
64.8
(Pdb) c

Disabling Breakpoints in Production

I don’t want breakpoints triggering in production. Python makes this easy with an environment variable:

Terminal window
PYTHONBREAKPOINT=0 python my_script.py

All breakpoint() calls are ignored. No code changes needed.

I can also set it programmatically for specific sections:

conditional_debug.py
import os
def process_user_data(user_id):
# Only debug for specific user
if user_id == "problem_user_123":
breakpoint()
# Normal processing
return fetch_and_transform(user_id)
# Or disable globally
os.environ['PYTHONBREAKPOINT'] = '0'

A Real-World Example

I recently debugged a data processing pipeline where records were mysteriously disappearing. Print debugging would have taken forever with thousands of records. Instead:

pipeline_debug.py
def process_batch(records):
successful = []
failed = []
for record in records:
try:
result = transform(record)
# Debug: Why are some records disappearing?
if result is None:
breakpoint() # Inspect the problem record
successful.append(result)
except Exception as e:
failed.append((record, str(e)))
return successful, failed

When a record produced None, I could immediately inspect:

(Pdb) record
{'id': 4521, 'value': '', 'type': 'legacy'}
(Pdb) transform(record)
None
(Pdb) record['value']
''

Turns out empty string values were being converted to None. I found the bug in seconds instead of adding print statements everywhere.

Why breakpoint() Beats print()

  1. No code cleanup - Remove the print statements later? No need. Just keep or remove the breakpoint.

  2. Interactive exploration - I can check any variable, not just the ones I thought to print.

  3. Step through logic - Walk through code line by line to understand flow.

  4. Modify state - I can even change variable values mid-debug to test fixes:

(Pdb) record['value'] = 'default'
(Pdb) transform(record)
{'id': 4521, 'value': 'default', 'type': 'legacy'}
  1. Conditional debugging - Only break on specific conditions without modifying logic.

Summary

Since Python 3.7, breakpoint() has been my go-to debugging tool. The function is built-in, the pdb commands are simple to learn, and the interactive workflow beats print debugging every time.

Key points:

  • Use breakpoint() to pause execution and inspect variables
  • Essential commands: n (next), s (step), c (continue), p (print)
  • Disable with PYTHONBREAKPOINT=0 in production
  • No imports needed - it’s built into Python 3.7+

Next time something goes wrong in your Python code, try breakpoint() instead of reaching for print. Pause execution, look around, and understand exactly what your code is doing.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments