Skip to content

debugpy

Language: Python · Transport: stdio

The catalog entry:

[adapters.debugpy]
id = "debugpy"
command = "python3"
args = ["-m", "debugpy.adapter"]
transport = "stdio"

python3 rather than python: a bare python usually does not exist on PATH on macOS. The real interpreter is most often in the project's virtualenv and arrives through adapter_command.

Install

debugpy is a Python module, installed into an interpreter rather than into the system:

.venv/bin/pip install debugpy
python3 -m debugpy --version

Two interpreters, two settings

This is the part that causes confusion. There are two independent choices:

Setting Which interpreter
adapter_command runs the adapter
launch_arguments.python runs your code
[profiles.main]
adapter = "debugpy"
adapter_command = "${root}/.venv/bin/python"
program = "main.py"

[profiles.main.launch_arguments]
python = "${root}/.venv/bin/python"

They are usually the same interpreter, and bugsaur init writes both when it finds .venv/bin/python or venv/bin/python in the project root. They are still separate settings, and No module named debugpy almost always means one of them points at an interpreter without debugpy installed.

The working directory reaches the debuggee explicitly

Unlike Delve, debugpy does not pass its own working directory down to the debuggee by inheritance. Bugsaur sends the working directory in the DAP request body, which is why the project root reliably becomes the debuggee's directory — and why imports of a package under src/ resolve without a cwd line.

Debugging a module

debugpy accepts module. There is no profile field for it; use the escape hatch:

[profiles.tests]
adapter = "debugpy"
program = "."
args = ["-x", "tests/"]

[profiles.tests.launch_arguments]
module = "pytest"

Django and other reloaders

An autoreloader forks a second process that the debugger is not attached to, so breakpoints in the worker never fire. Disable it:

args = ["runserver", "--noreload", "8000"]

The same applies to any framework with a reload-on-change mode — uvicorn's --reload, Flask's debug reloader.

Diagnostics

Symptom Cause Fix
No module named debugpy installed into a different interpreter install into the one in adapter_command
Session never starts adapter_command points at a nonexistent path check the venv path
Breakpoints never fire an autoreloader forked a second process --noreload
ImportError for your own package the working directory is not what the code assumes Working directory
Stops only in library code the exception came from deeper Call Stack