breakpoint()
Replaces the import pdb; pdb.set_trace() incantation, and unlike it can be switched off or redirected by an environment variable.
Common call
breakpoint()
Returns
enters pdb by default; the return value is rarely used
Replaces
import pdb; pdb.set_trace()
Watch out
PYTHONBREAKPOINT=0 disables it entirely — no debugger, no warning
breakpoint(*args, **kwargs)
→ Any
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| *args | Any | no | Passed straight through to sys.breakpointhook. The default pdb hook ignores them. |
| **kwargs | Any | no | Also forwarded. Third-party debuggers use these; pdb does not. |
Return value
Any — Whatever sys.breakpointhook returns — None for the default pdb hook, which enters the debugger before returning.
Examples
1. Pause here
def f(x):
breakpoint()
return x * 2
Returns
enters pdb at that line2. Disable everywhere
PYTHONBREAKPOINT=0 python app.py
Returns
every call becomes a no-op3. Use another debugger
PYTHONBREAKPOINT=IPython.terminal.debugger.set_trace python app.py
Returns
enters ipdb instead4. Redirect in code
import sys
sys.breakpointhook = my_hook
Returns
all later calls go to my_hook5. The old way
import pdb; pdb.set_trace()
Returns
equivalent, but not configurable6. Conditional pause
if value < 0:
breakpoint()
Returns
only stops when the condition holdsPitfalls
1. PYTHONBREAKPOINT=0 silently disables it
Set in a shell profile or a CI config, every breakpoint becomes a no-op. The debugger simply never opens, which reads like the line was not reached.
Nothing happens
PYTHONBREAKPOINT=0 python app.py
runs straight through
Check the variable
import os print(os.environ.get("PYTHONBREAKPOINT"))
'0' explains the silence
2. Left in committed code it hangs production
A stray breakpoint in a server or batch job waits forever for input that never comes. With no TTY it may instead raise on read, which is just as bad and harder to trace.
Hangs the job
def handler(req): breakpoint() ...
the request never returns
Lint for it
ruff rule T100 / flake8-debugger
fails CI instead
3. It stops the whole process, not one thread
pdb reads from stdin, so in a threaded or async program the interaction competes with everything else. Output interleaves and the prompt can become unusable.
Garbled prompt
breakpoint() # inside a worker thread
pdb output tangled with other threads
Remote debugger
use debugpy or pdb-attach for threaded code
a dedicated channel
4. Python 3.7 and newer only
On older interpreters it is a NameError, since it is a builtin rather than an import. Code meant to run on 3.6 still needs the pdb form.
Fails on 3.6
breakpoint()
NameError: name 'breakpoint' is not defined
Old form
import pdb; pdb.set_trace()
works everywhere
When to use
Use it
- Pausing to inspect local state during development
- Conditional debugging — call it only when a condition holds
- Projects standardising on a debugger via PYTHONBREAKPOINT
Reach for something else
- Anything that will be committed — lint it out
- Production, batch jobs and CI, where there is no interactive terminal
- Long-running loops → logging tells you more with less friction
Notes
Complexity
Not meaningful — hands control to sys.breakpointhook
Return
Whatever the hook returns; the default pdb hook returns None
CPython impl
Python/bltinmodule.c :: builtin_breakpoint, dispatching to sys.breakpointhook
Memory
Negligible; the debugger itself holds frame references while active
Thread-safe
No — pdb competes for stdin and stdout across threads
FAQ
Set PYTHONBREAKPOINT to the import path of any callable, or assign sys.breakpointhook at runtime. That indirection is the main reason breakpoint exists rather than calling pdb directly.
PYTHONBREAKPOINT=IPython.terminal.debugger.set_trace python app.py
History
3.7
breakpoint and sys.breakpointhook added by PEP 553, replacing the pdb.set_trace idiom.