Dropping into a debugger with the built-in debugger() function

One of the nice little things Python 3.7 brought us is the new built-in function breakpoint(), which was proposed in PEP 553. As its name suggests, it allows us to create a breakpoint in Python source code.

Now, creating breakpoints in the source is nothing new, but it was always a bit tedious:

name = input("Enter name: ")
import pdb; pdb.set_trace()
user = find_user(name=name)

Quite a lot of typing, and just like the author of the linked PEP, I also mistype it quite often. When deep into the bug tracking, such typos and script re-runs are moderately annoying and unnecessarily contribute to the overall cognitive load. Typing breakpoint() is slightly easier.

Additionally, if the project is set up to use auto code formatting tools such as black on every build, these tools are happy to refactor this debugging snippet to follow the style guide, adding insult to injury:

name = input("Enter name: ")
import pdb

pdb.set_trace()  # argh!!
user = find_user(name=name)

On the other hand, the following looks considerably cleaner and does not suffer from the same problem:

name = input("Enter name: ")
breakpoint()
user = find_user(name=name)

When called, the breakpoint() function calls the sys.breakpointhook() hook. The latter drops you into the build-in debugger pdb by default, but you can override the hook to do something else, such as invoking a completely different debugger or trolling:

import sys

def trolling():
    raise RuntimeError("Debugging not allowed!")

sys.breakpointhook = trolling  # for the lulz

...

breakpoint()  # RuntimeError: Debugging not allowed!

The default implementation of the hook also allows customizing the breakpoint() behavior through the PYTHONBREAKPOINT environment variable (provided that the hook was not overridden as above):

  • If PYTHONBREAKPOINT is not set or set to the empty string, pdb.set_trace() is called.
  • If set to "0", breakpoint() returns immediately and does not do anything. Useful for quickly disabling all breakpoints without modifying the source.
  • If set to anything else, e.g. "ipdb.set_trace", the value is treated as the name of the function to import and run. If importing fails, a warning is issued and the breakpoint is a no-op.

Useful? Tell me what you think!