How to Debug a Python Module in Visual Studio Code by Configuring launch.json
To debug a Python module in VS Code, configure your launch.json with the "module" key instead of "program", which instructs the debugger to execute your code via python -m while preserving the full module import context and namespace.
When working with the python/cpython repository or any Python project structured as a package, you need to debug modules rather than standalone scripts. The official Python extension for VS Code uses debugpy to communicate with the CPython interpreter, and proper launch.json configuration ensures the debugger correctly initializes the module import machinery.
Understanding the Python Debugger Architecture in VS Code
How CPython Handles Breakpoints
The debugging workflow in VS Code ultimately relies on CPython's internal breakpoint implementation. When you set a breakpoint in the editor, the debugger interfaces with several key components of the CPython source:
Python/bltinmodule.c: Implements the built-inbreakpoint()function, which callssys.breakpointhook()by default.Python/sysmodule.c: Contains thesys.breakpointhookimplementation that routes topdb.set_trace().Lib/pdb.py: The pure-Python debugger that provides the interactive REPL used by VS Code when execution pauses.
The Role of debugpy in Module Execution
The debugpy engine launches the CPython interpreter as a subprocess and communicates via the Debug Adapter Protocol. When debugging a module, debugpy must invoke the interpreter with the -m flag to trigger the module import system (importlib._bootstrap._gcd_import) rather than treating the target as a file path. This distinction is critical for packages that rely on relative imports or specific __main__ block execution.
Configuring launch.json to Debug a Python Module
The Module Launch Configuration
To debug a Python module within Visual Studio Code using the python debugger vscode by configuring the launch dot json file effectively, you must specify the "module" attribute. This replaces the standard "program" attribute used for script debugging.
Essential Parameters for Module Debugging
The following configuration keys align your debugging session with CPython's internal architecture:
"type": "python"– Selects the debugpy adapter that interfaces with the CPython interpreter."request": "launch"– Starts a fresh CPython process, loading the module's__main__namespace."module"– Specifies the fully-qualified module name (e.g.,"mypackage.mymodule"), triggeringpython -mexecution."cwd"– Sets the working directory, affectingsys.pathand relative import resolution."justMyCode": false– Allows stepping into CPython standard library code (e.g.,Lib/pdb.py) rather than skipping it."console": "integratedTerminal"– Preserves terminal I/O behavior thatpdbexpects for interactive debugging.
Recommended launch.json Configuration
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Python Module",
"type": "python",
"request": "launch",
"module": "mypackage.mymodule",
"cwd": "${workspaceFolder}",
"env": {
"PYTHONPATH": "${workspaceFolder}"
},
"justMyCode": false,
"console": "integratedTerminal",
"stopOnEntry": false
}
]
}
Step-by-Step Implementation
Setting Up Your Workspace
Ensure your project structure follows standard Python package conventions with an __init__.py file in each directory. The PYTHONPATH environment variable in your launch.json should point to the workspace root so that importlib can locate your module during the debug session.
Creating the Debug Configuration
- Open the Run and Debug view in VS Code (Ctrl+Shift+D).
- Click "create a launch.json file" and select the Python environment.
- Replace the default configuration with the module-specific settings shown above, substituting
"mypackage.mymodule"with your actual module path.
Running the Debugger
With your configuration saved, place a breakpoint in your module code and press F5. The debugger will execute python -m mypackage.mymodule, initialize the CPython interpreter, and pause at your breakpoint. You can then inspect variables, step through the call stack, and even descend into the standard library implementation in Lib/pdb.py if "justMyCode" is disabled.
Advanced Configuration Options
Debugging into CPython Internals
Setting "justMyCode": false is essential when you need to trace execution into the Python standard library or investigate how breakpoint() behaves. This setting allows you to step through the implementation in Lib/pdb.py and observe how sys.breakpointhook (defined in Python/sysmodule.c) routes control to the debugger.
Custom Breakpoint Hooks
CPython supports custom breakpoint hooks via the PYTHONBREAKPOINT environment variable. You can configure this in your launch.json to override the default pdb.set_trace() behavior:
"env": {
"PYTHONBREAKPOINT": "custom_debug_hook"
}
Your custom function will receive control whenever breakpoint() is called, allowing you to log stack traces or implement conditional debugging logic before entering the interactive session.
Remote Debugging Considerations
For scenarios involving remote debugging, CPython provides low-level support in Python/remote_debugging.c and defines debug cookies in Include/internal/pycore_debug_offsets.h. While VS Code typically handles local debugging via debugpy, you can configure remote attachment by adding a "connect" block to your launch.json:
"connect": {
"host": "localhost",
"port": 5678
}
This connects to a CPython process already running with the debug server enabled, utilizing the same breakpoint infrastructure defined in bltinmodule.c and sysmodule.c.
Summary
- Use the
"module"key inlaunch.jsonto debug Python packages viapython -mexecution, ensuring proper import context and__main__block handling. - Set
"justMyCode": falseto step into CPython standard library code, including thepdb.pyimplementation andsys.breakpointhookinternals. - Configure environment variables like
PYTHONPATHandPYTHONBREAKPOINTin your launch configuration to match command-line behavior and customize debugging hooks. - Leverage
"console": "integratedTerminal"to preserve interactive I/O required bypdband other console-based debuggers. - Reference CPython source files (
bltinmodule.c,sysmodule.c,pdb.py) to understand how breakpoints are triggered and handled by the VS Code debugger.
Frequently Asked Questions
How do I debug a Python module instead of a script in VS Code?
To debug a Python module, open your .vscode/launch.json file and create a configuration using "type": "python" and "request": "launch", but specify "module": "yourpackage.module" instead of "program". This tells the debugger to execute python -m yourpackage.module, preserving the module import context and allowing relative imports to resolve correctly.
What is the purpose of the "justMyCode" setting in launch.json?
The "justMyCode": false setting allows the debugger to step into code outside your workspace, including the Python standard library. When debugging CPython internals or investigating how breakpoint() works, disabling this setting lets you trace execution through Lib/pdb.py and observe how sys.breakpointhook in Python/sysmodule.c routes control to the interactive debugger.
How does VS Code's debugger connect to CPython's breakpoint system?
VS Code uses the debugpy adapter, which launches CPython as a subprocess and communicates via the Debug Adapter Protocol. When your code calls breakpoint(), CPython's built-in function (implemented in Python/bltinmodule.c) invokes sys.breakpointhook(), which defaults to pdb.set_trace() from Lib/pdb.py. The debugpy adapter intercepts this interaction and maps it to the VS Code debugging UI, allowing you to inspect variables and control execution flow.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →