PYTHONBREAKPOINT Environment Variable

The PYTHONBREAKPOINT environment variable gives you runtime control over how Python's built-in breakpoint() function behaves -- without touching a single line of source code. You can silence all breakpoints in production, route them to a third-party debugger, or implement a completely custom hook, all from the command line or your shell profile.

Before Python 3.7, dropping into a debugger required writing import pdb; pdb.set_trace() inline -- two statements on one line that linters complained about, and cleanup that was easy to forget. PEP 553 replaced all of that with a single built-in: breakpoint(). What makes it genuinely useful is the environment variable that controls it. PYTHONBREAKPOINT lets you change debugger behavior from outside the code entirely, which is a meaningful improvement for teams working across different environments.

How breakpoint() and PYTHONBREAKPOINT Work Together

When you call breakpoint() in your code, Python does not jump straight into pdb. It first calls sys.breakpointhook(), which is where the actual debugger invocation lives. The default implementation of sys.breakpointhook() reads PYTHONBREAKPOINT every time it runs -- not just once at startup. That matters: you can change the variable mid-execution and the next breakpoint() call will reflect the new value.

The variable is read fresh on each call, so its value at the moment breakpoint() executes is what determines behavior. This design also means the debugger is swappable at the environment level rather than the code level.

Note

breakpoint() accepts *args and **kwargs, which are passed straight through to sys.breakpointhook(). If the callable you have configured does not accept those arguments, Python will raise a TypeError. Keep that in mind when using custom hooks.

The Three Core Values

PYTHONBREAKPOINT accepts three categories of values, each producing a distinct outcome.

Set to 0 -- Disable all breakpoints

Setting the variable to 0 tells sys.breakpointhook() to return None immediately. Execution continues as if the breakpoint() call was never there. This is the cleanest way to run code that still contains breakpoint statements without entering the debugger -- useful in CI pipelines, staging servers, or any situation where you want the code unchanged but debugging suppressed.

# Run your script with all breakpoints silenced
PYTHONBREAKPOINT=0 python my_script.py

Empty string or unset -- Default pdb behavior

An empty string value is identical to not setting the variable at all. In both cases, sys.breakpointhook() falls back to pdb.set_trace(), which opens an interactive debugging session at the call site. This is the default experience most Python developers are familiar with.

# These three are equivalent
python my_script.py
PYTHONBREAKPOINT= python my_script.py
unset PYTHONBREAKPOINT && python my_script.py

A dotted callable path -- Custom debugger

When the value is a dotted Python path like module.callable, sys.breakpointhook() imports the specified module and calls the named object. This is how you route breakpoint() to any debugger or custom function without editing your source files. If the import fails or the callable does not exist on the module, Python issues a RuntimeWarning and skips the breakpoint rather than raising an exception.

# Route to IPython's debugger
PYTHONBREAKPOINT=IPython.core.debugger.set_trace python my_script.py

# Route to ipdb
PYTHONBREAKPOINT=ipdb.set_trace python my_script.py

# Even a built-in works -- this calls int() at every breakpoint (unusual but valid)
PYTHONBREAKPOINT=int python my_script.py
Pro Tip

The callable does not have to be a debugger. You could point PYTHONBREAKPOINT at any importable function -- a logger, a profiler hook, or a custom inspection utility. The variable is just a mechanism for dependency injection into the debugger slot.

Plugging In a Third-Party Debugger

One of the practical benefits of PYTHONBREAKPOINT is that it decouples debugger choice from code. The same codebase can use pdb on a remote server, ipdb on a developer's local machine with IPython installed, and pudb in a terminal environment where a full-screen TUI is preferred -- all without a single conditional import in the source.

Using ipdb

ipdb wraps pdb with IPython's tab completion, syntax highlighting, and smarter introspection. Install it first, then point the variable at its entry point.

pip install ipdb

# Per-run
PYTHONBREAKPOINT=ipdb.set_trace python my_script.py

Using pudb

pudb provides a full-screen terminal UI with a variable inspector panel, a source view, and a stack trace pane. It is particularly useful when you need to track many variables across multiple frames at once.

pip install pudb

PYTHONBREAKPOINT=pudb.set_trace python my_script.py

Using web-pdb

web-pdb launches a local web server and opens the debugging session in a browser tab. This can be convenient in environments where a terminal-based UI is awkward, or when you want to share a debugging session.

pip install web-pdb

PYTHONBREAKPOINT=web_pdb.set_trace python my_script.py
# Then open your browser to http://localhost:5555
Warning

If the module path you provide is invalid or the callable cannot be found, Python issues a RuntimeWarning and silently skips the breakpoint -- it does not crash. This means a misconfigured PYTHONBREAKPOINT value can go unnoticed. Test your configuration on a simple script before relying on it during a real debugging session.

Setting It Persistently in Your Shell Profile

Setting PYTHONBREAKPOINT inline on every command is tedious. For your local development machine, the better approach is to export it from your shell profile so it applies to every Python session automatically.

# Add to ~/.bashrc, ~/.zshrc, or ~/.profile
export PYTHONBREAKPOINT="ipdb.set_trace"

After reloading your shell (source ~/.bashrc or opening a new terminal), every breakpoint() call will route to ipdb without any per-run flag. Your production deployments can still override this with PYTHONBREAKPOINT=0 in their process environment, keeping the two contexts cleanly separated.

"This environment variable allows external processes to control how breakpoints are handled." — PEP 553

That framing -- external processes -- is the key insight. Build systems, test runners, and deployment tooling can all set this variable for their own execution context, leaving developer environments free to use whatever debugger they prefer.

The -E Flag and When PYTHONBREAKPOINT Is Ignored

When Python is started with the -E flag, all PYTHON* environment variables are ignored, including PYTHONBREAKPOINT. In that mode, the interpreter falls back to default pdb behavior regardless of what the variable is set to. This is consistent with how -E treats all other Python environment variables.

# -E ignores PYTHONBREAKPOINT entirely
# This will use pdb even though the variable says 0
PYTHONBREAKPOINT=0 python -E my_script.py

This behavior was deliberate. PEP 553 considered whether PYTHONBREAKPOINT=0 should remain active under -E -- since disabling breakpoints might seem like a safety concern rather than a configuration preference -- but the decision was to treat it uniformly with all other PYTHON* variables. No special case.

Note

PYTHONBREAKPOINT requires Python 3.7 or later. If you are working with an older interpreter, the built-in breakpoint() function does not exist, and neither does the environment variable hook.

Key Takeaways

  1. Zero disables, empty enables: PYTHONBREAKPOINT=0 silences all breakpoints entirely. An unset variable or empty string restores the default pdb behavior.
  2. Dotted paths load any callable: Set the variable to any importable dotted path and breakpoint() will call it instead of pdb.set_trace() -- no code changes required.
  3. It is read on every call: The value is consulted each time sys.breakpointhook() runs, so it can change during execution and the next breakpoint will reflect the new setting.
  4. Misconfiguration fails silently: An invalid module path produces a RuntimeWarning and skips the breakpoint rather than raising an exception -- test your configuration before relying on it.
  5. The -E flag overrides everything: Running Python with -E ignores PYTHONBREAKPOINT along with all other PYTHON* variables, and the interpreter falls back to default pdb behavior.

PYTHONBREAKPOINT is a small feature with a large practical impact. It makes breakpoint() genuinely environment-aware: the same call in source code can invoke pdb, ipdb, a web interface, or nothing at all, depending entirely on where and how the code is run. That flexibility, without any conditional logic in the source, is exactly what the feature was designed to provide.

back to articles