PDB Invocation and Wrapping
Purpose
This subtopic addresses the integration of Python's built-in debugger, **pdb**, within pytest's test execution environment by wrapping pdb classes and managing input/output capturing during debugging sessions. Its goal is to ensure that when a user invokes the debugger—either automatically on test failures or manually via command line options—pytest properly suspends output capturing, presents clear debugging prompts, and handles pdb commands gracefully without interfering with pytest’s internal state or test output.
Unlike the parent topic that broadly covers pdb integration and command line control, this subtopic specifically focuses on the underlying mechanics of invoking pdb, wrapping pdb classes to extend behavior, and managing IO capturing so that interactive debugging sessions work seamlessly within pytest’s capturing and reporting framework.
Functionality
Wrapping pdb Classes for Enhanced Behavior
The core mechanism dynamically imports and wraps the pdb class specified by the user or defaults to
pdb.Pdb.The wrapper class (
PytestPdbWrapper) extends the original pdb class to:Manage pytest’s IO capturing by suspending and resuming it appropriately when entering and continuing from pdb.
Customize the behavior of debugger commands like
continueandquitto coordinate with pytest's test runner and output.Track recursive debugger invocations to avoid repeated capture suspension or confusing output.
Adjust stack frame inspection to skip hidden frames marked with
__tracebackhide__.
This wrapping ensures that pdb debugging sessions respect pytest's output capturing and lifecycle, providing a smooth user experience.
Managing IO Capturing During Debugging
Before starting a pdb session (e.g., set_trace or post-mortem), pytest suspends all output capturing (stdout, stderr, logs) to allow the user to interact directly with the debugger.
When continuing from pdb (
do_continue), pytest resumes capturing and notifies the user via terminal output that capturing has resumed.This careful capture management prevents captured output from interfering with the interactive debugger prompt and keeps test logs consistent.
Invocation Hooks and Session Control
The subtopic provides a class
PdbInvokethat implements hooks to trigger pdb on test exceptions or internal errors.It also registers a
PdbTraceplugin that can wrap test functions to start pdb immediately on each test run when the--traceoption is used.The
pytestPDBclass centralizes pdb initialization and invocation, exposing a set_trace method to replace the standard pdb.set_trace with a pytest-aware version.Cleanup handlers restore original pdb behavior after pytest finishes.
Example Code Snippet: Wrapping pdb class
def _get_pdb_wrapper_class(cls, pdb_cls, capman: CaptureManager | None):
class PytestPdbWrapper(pdb_cls):
_pytest_capman = capman
_continued = False
def do_continue(self, arg):
ret = super().do_continue(arg)
if cls._recursive_debug == 0:
# Resume capturing output after continue
if self._pytest_capman:
self._pytest_capman.resume()
return ret
def do_quit(self, arg):
ret = super().do_quit(arg)
if cls._recursive_debug == 0:
outcomes.exit("Quitting debugger")
return ret
return PytestPdbWrapper
This snippet shows how the wrapper intercepts `continue` and `quit` commands to integrate with pytest’s capture manager and test lifecycle.
Integration
With Parent Topic: This subtopic is the foundation for pdb integration within pytest. It implements the mechanics that allow the parent topic's features—such as invoking pdb on failures or via command line options—to function correctly within pytest’s environment.
With Output and Log Capture: It interacts closely with pytest's capturing system (
CaptureManager) to pause and resume capturing around debugging sessions, ensuring users see real-time debugger output.With Test Execution and Reporting: By hooking into exception handling and test call phases, it triggers pdb precisely when needed, such as on test errors or when requested by users.
With Plugin System: The pdb invocation functionality is registered as plugins (
PdbInvoke,PdbTrace) via pytest’s plugin manager, allowing it to be enabled or disabled based on user options (--pdb,--trace).Distinction from Other Subtopics: While the parent topic describes pdb integration at a high level and other subtopics cover command line options or breakpoint handling, this subtopic uniquely manages the internal pdb class wrapping and IO coordination that makes interactive debugging viable within pytest.
Diagram
sequenceDiagram
participant User
participant Pytest CLI
participant PluginManager
participant pytestPDB
participant CaptureManager
participant pdb
User->>Pytest CLI: Run tests with --pdb or --trace
Pytest CLI->>PluginManager: Register PdbInvoke or PdbTrace plugin
PluginManager->>pytestPDB: Initialize with config and capture manager
pytestPDB->>CaptureManager: Suspend capturing for pdb session
pytestPDB->>pdb: Import and wrap pdb class (PytestPdbWrapper)
User->>pdb: Interact with pdb session (commands like continue, quit)
pdb->>pytestPDB: On continue, call do_continue override
pytestPDB->>CaptureManager: Resume capturing output
pdb->>pytestPDB: On quit, raise exit outcome to end test run
This sequence diagram visualizes the key flow of invoking pdb within pytest, highlighting how capturing is managed and how pdb is wrapped and controlled.
By wrapping pdb and managing IO capturing, pytest ensures that debugging sessions integrate smoothly into test runs, preserving output consistency and providing users with an interactive debugging experience tailored for automated testing workflows.