debugpy Documentation

repository·main·Indexed 24 days ago

https://github.com/microsoft/debugpy

An implementation of the Debug Adapter Protocol (DAP) for Python 3 that enables debugging Python code via DAP-compliant clients like VS Code. It provides a CLI for debugging scripts and modules, the ability to attach to running processes by PID, and a Python API for programmatic control, including functions like debugpy.listen(), debugpy.wait_for_client(), and debugpy.trigger_exception_handler(). The documentation also covers subprocess debugging workflows, remote debugging with pydevd, and the implementation of custom debugger extensions.

Tokens
4.8K
Snippets
13
Records
29
Agent score
82%

What's inside debugpy

  1. Understand default SystemExit break behavior

    main

    If breakOnSystemExit is not explicitly specified in launch.json, the following default rules apply:

    • SystemExit(0) and SystemExit(None) are ignored (treated as successful exits).
    • All other non-zero exit codes cause the debugger to break.
    • If django or flask settings are set to true, exit code 3 is also ignored (used for reload signaling).
    • If breakOnSystemExitZero is set to true, the debugger will also break on SystemExit(0) and SystemExit(None).
  2. How the adapter tracks and identifies subprocesses

    main

    The debugpy adapter uses several mechanisms to manage multiple processes:

    • Identifying Subprocesses: The adapter distinguishes between the root process and subprocesses by counting connections. The first connection is always the root process; all subsequent connections are treated as subprocesses.
    • Tracking Connections: The adapter creates a Session instance as soon as a server establishes a socket connection and maintains it until the debuggee process exits.
    • IDE Connection to Subprocesses: When a subprocess is detected, the adapter sends a ptvsd_subprocess event to the IDE. This event contains the host, port (where the adapter is listening for the IDE), and the processId of the subprocess. The IDE uses this information to send an attach request specifying that processId.
    • Adapter Connection to Subprocesses: For any connection after the first one, the adapter requires the processId to be specified in the attach request. It uses this ID to look up the corresponding Session in its internal tracker.
  3. Understand subprocess debugging terminology

    main

    To understand how debugpy handles multiple processes, familiarize yourself with these core components:

    • Debuggee process: The actual Python process being debugged.
    • IDE: Your development environment (e.g., VSCode) acting as a DAP client.
    • Debug server: A pydevd instance with a debugpy wrapper hosted inside each debuggee process.
    • Debug adapter: The debugpy component that mediates communication between the IDE and the debug servers.
    • IDE listener port: A port opened by the adapter for the IDE to connect to.
    • Server listener port: A port opened by the adapter for debug servers to connect to.
    • Adapter listener port: A port opened by the server for the adapter to connect to.
  4. How the debug server manages IDE connection states

    main

    The debug server (inside the debuggee) stays aware of the IDE's connection status through signals from the adapter:

    • Connection/Initialization: For every connection the adapter receives from the IDE, it sends an initialized request to the server.
    • Disconnection: Whenever the IDE disconnects, the adapter sends a disconnected request to the server (even if the IDE did not explicitly send one).

    These events allow the server to manage logical debug sessions (e.g., enabling/disabling tracing or deciding whether to continue running if the code was stopped at a breakpoint) even if the underlying TCP connection remains active throughout the lifetime of the debuggee.

  5. How subprocess debugging works in a 'launch' scenario

    main

    In a launch scenario, the IDE starts the debugging session. The workflow is as follows:

    1. The user starts debugging (e.g., pressing F5) using a launch configuration.
    2. The IDE spawns the adapter and connects via stdio.
    3. The adapter spawns the primary debuggee process and passes the server listener port via command line.
    4. The primary debuggee connects to the adapter's server listener port.
    5. When the user's code spawns a child process, the child process is passed the server listener port via command line.
    6. The child process connects to the adapter's server listener port.
    7. The adapter emits a ptvsd_subprocess event to the IDE.
    8. The IDE connects to the adapter's IDE listener port and sends an attach request to the child process.
    9. When debugging stops, the IDE requests a disconnect, and the adapter terminates both the primary and child processes.
  6. Debug a script file with debugpy CLI

    main

    To run a Python script with the debugger enabled, use the -m debugpy command.

    By default, the script starts executing immediately. To prevent the script from running until a debugger client (like VS Code) has attached, use the --wait-for-client flag.

    To specify the interface and port, use --listen <host>:<port> or just --listen <port> (which defaults to 127.0.0.1). To allow connections from other machines, listen on 0.0.0.0.

  7. Attach to a running process by PID

    main

    You can inject the debugger into an already running Python process using its Process ID (PID) with the --pid flag. This starts a debugpy server within that process.

    -m debugpy --listen localhost:5678 --pid 12345
  8. Implement a custom debugpy extension

    main

    You can extend the debugger without modifying its core code by implementing custom extensions. To do this, you must follow a specific directory structure, ensure your root directory is in sys.path (e.g., via PYTHONPATH), and use a specific namespace pattern.

    1. Directory Structure

    Your extension must follow this layout:

    |-- root_directory (must be on python path)
    |   |-- pydevd_plugins
    |   |   |-- __init__.py (must contain preamble)
    |   |   |-- extensions
    |   |   |   |-- __init__.py (must contain preamble)
    |   |   |   |-- pydevd_plugin_<your_plugin_name>.py

    2. Namespace Preamble

    Both pydevd_plugins/__init__.py and pydevd_plugins/extensions/__init__.py must contain only the following preamble to support namespace packages:

    try:
        __import__('pkg_resources').declare_namespace(__name__)
    except ImportError:
        import pkgutil
        __path__ = pkgutil.extend_path(__path__, __name__)

    3. Plugin Implementation

    • Naming: The filename inside the extensions folder must start with pydevd_plugin_ (e.g., pydevd_plugin_my_extension.py).
    • Logic: Implement one or more abstract base classes defined in _pydevd_bundle.pydevd_extension_api. You can do this by either inheriting from the classes or registering with the abstract base class.
    |--  root_directory-> must be on python path
    |    |-- pydevd_plugins
    |    |   |-- __init__.py -> must contain preamble
    |    |   |-- extensions
    |    |   |   |-- __init__.py -> must contain preamble
    |    |   |   |-- pydevd_plugin_plugin_name.py
  9. Debug a Python module with debugpy CLI

    main
    To debug a module instead of a script, use the -m flag for the module after the debugpy arguments. Arguments passed after the module name will be treated as sys.argv for that module.
  10. How to file an issue with debugpy

    main

    When reporting a bug, follow these steps to ensure a helpful report:

    1. Search existing issues: Check for known issues or similar reports first.
    2. Use the template: Follow the official bug report template.
    3. Provide logs: Include any available debugger logs. Refer to the Debugger logging section of the documentation to learn how to enable them.
  11. Attach a target process to the pydev debugger

    main

    You can attach a running target process to the pydev debugger using the attach_pydevd.py utility.

    Prerequisites:

    • A remote debugger must already be active and listening on the specified port.
    • The target process ID (PID) must be known.

    Usage: Run the script from the command line, providing the --port where the debugger is listening and the --pid of the process you wish to attach to.

    python attach_pydevd.py --port 5678 --pid 1234