PEP 553 in 90 seconds — breakpoint() and the customisable debug hook
One builtin replaces `import pdb; pdb.set_trace()` — and tests can mute it.
Pre-3.7, dropping into the debugger meant a two-step incantation everyone got wrong: ```python import pdb; pdb.set_trace() ``` Typing `pbd` instead of `pdb`, importing the wrong `pdb` (some shops use `ipdb` or `pudb`), or forgetting the import line entirely — all real footguns. PEP 553 (Python 3.7, 2018) shipped `breakpoint()` as a true builtin.
Two benefits that aren't obvious from "easier to type": 1. **Customisable** — `sys.breakpointhook` is the function `breakpoint()` actually calls. Tests can replace it with a no-op, IDEs can wire it to their own UIs, you can replace it with `ipdb.set_trace` once for the whole process. 2. **Environment-controllable** — set `PYTHONBREAKPOINT=0` and every `breakpoint()` call is silently skipped (PEP 553 honors this). `PYTHONBREAKPOINT=ipdb.set_trace` and the builtin drops into `ipdb` automatically. You can leave `breakpoint()` calls in code committed to CI as long as `PYTHONBREAKPOINT=0` is set in CI's env.
def calc(x, y):
result = x * y
# Drop into the debugger:
import pdb; pdb.set_trace()
return result + 1
# Or for ipdb (richer prompt):
def calc2(x, y):
import ipdb; ipdb.set_trace()
return x * y
# Pain points:
# - Easy typo: `import pbd`
# - Tests run this for real unless you remember to strip it
# - No env-var way to disable globallydef calc(x, y):
result = x * y
breakpoint(label="after_mul", value=result) # args passed to hook
return result + 1
# In tests, mute every breakpoint() in one place:
import sys
sys.breakpointhook = lambda *a, **kw: None # no-op
# Or globally via env (no code change):
# PYTHONBREAKPOINT=0 python prog.py # skip every breakpoint
# PYTHONBREAKPOINT=ipdb.set_trace ... # use ipdb instead of pdb🎯 Predict the output
What does this print? `sys.breakpointhook` is the seam — replacing it lets you observe what `breakpoint(...)` was called with.
import sys
events = []
sys.breakpointhook = lambda *a, **kw: events.append((a, kw))
def calc(x, y):
result = x * y
breakpoint(label="after_mul", value=result)
return result + 1
print(calc(3, 4))
print(events)