Breakpoint and Trace Options
Purpose
This subtopic enables users to control interactive debugging behavior in pytest through command line options, specifically `--pdb`, `--pdbcls`, and `--trace`. These options allow tests to automatically invoke the Python debugger (PDB) either immediately when running tests or upon encountering errors or interruptions. The goal is to provide flexible, customizable debugging entry points that integrate seamlessly with pytest's test execution flow.
Functionality
--pdb: When enabled, pytest starts an interactive debugging session immediately after a test fails due to an error or aKeyboardInterrupt. This facilitates direct investigation of failures without rerunning tests manually.--pdbcls: Extends--pdbby allowing the user to specify a custom debugger class instead of the default pdb.Pdb. The expected format ismodule:classname, enabling integration with enhanced debuggers likeIPython.terminal.debugger.TerminalPdb.--trace: Causes pytest to break into the debugger at the very start of each test, regardless of outcome. This is useful for stepping through test setup and execution interactively from the beginning.
Key workflows and methods:
Option Parsing and Registration
The command line options are added to pytest’s general option group bypytest_addoption. The--pdbclsoption validates the input formatmodulename:classnamebefore acceptance.Plugin Registration Based on Options
Duringpytest_configure, pytest registers internal pluginsPdbInvoke(for--pdb) andPdbTrace(for--trace) that implement the respective debugging behaviors.Debugger Initialization and Wrapping
The classpytestPDBmanages the lifecycle and invocation of the debugger. It imports the appropriate PDB class (default or custom), wraps it to integrate with pytest's output capturing system, and initializes debugging sessions that suspend pytest's standard IO capturing to avoid interference.Debugging on Failure (
--pdb)
ThePdbInvokeplugin listens for test failures via thepytest_exception_interacthook. When a failure occurs, it suspends IO capturing, displays captured output, and invokes the debugger post-mortem at the failure point.Immediate Breakpoint (
--trace)
ThePdbTraceplugin wraps test functions so that when a test starts, it immediately invokes the debugger’sruncallmethod, dropping into the debugging prompt at the first statement.Custom Debugger Class Support
The _import_pdb_cls method dynamically imports and resolves the debugger class specified by--pdbcls, falling back to the standard pdb.Pdb if none is given.IO Capturing Management
The wrapped debugger class (PytestPdbWrapper) carefully manages pytest’s output capturing—suspending it on debugger entry and resuming on continuation—to ensure clear visibility of debugging interaction.
Code Snippet: Adding Options and Registering Plugins
def pytest_addoption(parser):
group = parser.getgroup("general")
group.addoption("--pdb", action="store_true", help="Start debugger on errors")
group.addoption(
"--pdbcls",
metavar="modulename:classname",
type=_validate_usepdb_cls,
help="Custom debugger class for --pdb",
)
group.addoption("--trace", action="store_true", help="Break into debugger on each test")
def pytest_configure(config):
if config.getvalue("trace"):
config.pluginmanager.register(PdbTrace(), "pdbtrace")
if config.getvalue("usepdb"):
config.pluginmanager.register(PdbInvoke(), "pdbinvoke")
Code Snippet: Wrapping Test Function for --trace
def wrap_pytest_function_for_tracing(pyfuncitem):
_pdb = pytestPDB._init_pdb("runcall")
testfunction = pyfuncitem.obj
@functools.wraps(testfunction)
def wrapper(*args, **kwargs):
func = functools.partial(testfunction, *args, **kwargs)
_pdb.runcall(func)
pyfuncitem.obj = wrapper
Integration
This subtopic extends the **Debugger Integration** parent topic by providing concrete user-facing command line options and their implementations to invoke debugging sessions during test runs. It complements the "PDB Invocation and Wrapping" subtopic by:
Managing when and how the debugger is invoked (on error, immediately, or with custom classes).
Coordinating with pytest's output capturing system to ensure a smooth debugging experience.
Interacting with pytest's plugin manager to hook into test lifecycle events (
pytest_exception_interact,pytest_pyfunc_call).
Together, these breakpoints and trace options integrate tightly with pytest's test execution machinery to enhance developer productivity through immediate and flexible debugging capabilities.
Diagram
The following flowchart illustrates the core decision and invocation flow when running tests with breakpoint and trace options enabled:
flowchart TD
Start[Test Run Start]
CheckTrace{--trace enabled?}
WrapTest[Wrap test function to enter debugger at start]
RunTest[Run test function]
CheckError{Test failed or KeyboardInterrupt?}
CheckPdb{--pdb enabled?}
SuspendCapture[Suspend IO capturing]
ShowCapture[Show captured stdout/stderr/log]
EnterDebugger[Enter PDB post-mortem]
Continue[Continue test execution]
End[Test Run End]
Start --> CheckTrace
CheckTrace -- Yes --> WrapTest
CheckTrace -- No --> RunTest
WrapTest --> RunTest
RunTest --> CheckError
CheckError -- Yes --> CheckPdb
CheckError -- No --> Continue
CheckPdb -- Yes --> SuspendCapture
CheckPdb -- No --> Continue
SuspendCapture --> ShowCapture --> EnterDebugger --> Continue
Continue --> End
This flowchart highlights how the command line options influence test execution by injecting debugger sessions either immediately (`--trace`) or conditionally on failures (`--pdb`), managing IO capture around these interactions.