Repository navigation
Description
Environment data
- debugpy version: 1.4.0
- OS and version: Linux 5.13.4
- Python version (& distribution if applicable, e.g. Anaconda): 3.9.6
- Using VS Code or Visual Studio: Neither, using nvim-dap
Actual behavior
If the debug adapter is started using python -m debugpy.adapter it remains running indefinitely. It doesn't terminate at the end of a debug session and it also doesn't terminate if it receives a disconnect event.
If the debug adapter is started using python -m debugpy --listen localhost:5678 --wait-for-client somefile.py the debug adapter exits after the debug session ends. If the client sends a disconnect request, the debug adapter doesn't exist.
Expected behavior
According to the specification, there are two modes, single session mode and multi session mode.
single session mode: in this mode, the development tool starts a debug adapter as a standalone process and communicates with it through stdin and stdout. At the end of the debug session the debug adapter is terminated. For concurrent debug sessions, the development tool starts multiple debug adapters.
Given that debugpy communicates over stdin and stdout if started via python -m debugpy.adapter without --listen argument I'd expect that is uses the single session mode - in that case it should automatically terminate after the debug session ends.
Update: Point below is resolved
On the other hand, if the debug adapter is started manually using the --listen argument, I'd expect that it runs in multi session mode:
multi session mode: in this mode, the development tool does not start the debug adapter but assumes that it is already running and that it listens on a specific port for connections attempts. For every debug session, the development tool initiates a new communication session on a specific port and disconnects at the end of the session.
In that case, it is supposed to keep running? This may be tricky given that it runs the debugee itself, but I'd expect that it terminates if it receives a disconnect request. Currently it remains running if it receives a disconnect request.
The ‘disconnect’ request is sent from the client to the debug adapter in order to stop debugging.
It asks the debug adapter to disconnect from the debuggee and to terminate the debug adapter.
(From https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Disconnect)
Steps to reproduce:
- Setup neovim with nvim-dap and configure the debugpy debug adapter
- Debug a file
ps aux | grep debugpy
Here is the full log of a session
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:742 ] "request" {
arguments = {
adapterID = "nvim-dap",
clientId = "neovim",
clientname = "neovim",
columnsStartAt1 = true,
linesStartAt1 = true,
locale = "en_US.UTF-8",
pathFormat = "path",
supportsRunInTerminalRequest = true,
supportsVariableType = true
},
command = "initialize",
seq = 0,
type = "request"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
category = "telemetry",
data = {
packageVersion = "1.4.0"
},
output = "ptvsd"
},
event = "output",
seq = 1,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
category = "telemetry",
data = {
packageVersion = "1.4.0"
},
output = "debugpy"
},
event = "output",
seq = 2,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
exceptionBreakpointFilters = { {
default = false,
filter = "raised",
label = "Raised Exceptions"
}, {
default = true,
filter = "uncaught",
label = "Uncaught Exceptions"
} },
supportsClipboardContext = true,
supportsCompletionsRequest = true,
supportsConditionalBreakpoints = true,
supportsConfigurationDoneRequest = true,
supportsDebuggerProperties = true,
supportsDelayedStackTraceLoading = true,
supportsEvaluateForHovers = true,
supportsExceptionInfoRequest = true,
supportsExceptionOptions = true,
supportsFunctionBreakpoints = true,
supportsGotoTargetsRequest = true,
supportsHitConditionalBreakpoints = true,
supportsLogPoints = true,
supportsModulesRequest = true,
supportsSetExpression = true,
supportsSetVariable = true,
supportsStepInTargetsRequest = true,
supportsTerminateDebuggee = true,
supportsValueFormattingOptions = true
},
command = "initialize",
request_seq = 0,
seq = 3,
success = true,
type = "response"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:742 ] "request" {
arguments = {
console = "integratedTerminal",
name = "Launch file",
program = "~/workspace/tmp/py/foo.py",
request = "launch",
type = "python"
},
command = "launch",
seq = 1,
type = "request"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
arguments = {
args = { "~/.virtualenvs/tools/bin/python", "~/.virtualenvs/tools/lib/python3.9/site-packages/debugpy/launcher", "46003", "--", "~/workspace/tmp/py/foo.py" },
cwd = "~/workspace/tmp/py",
env = {},
kind = "integrated",
title = "Python Debug Console"
},
command = "runInTerminal",
seq = 4,
type = "request"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:48 ] "run_in_terminal" {
args = { "~/.virtualenvs/tools/bin/python", "~/.virtualenvs/tools/lib/python3.9/site-packages/debugpy/launcher", "46003", "--", "~/workspace/tmp/py/foo.py" },
cwd = "~/workspace/tmp/py",
env = {},
kind = "integrated",
title = "Python Debug Console"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:762 ] "response" {
body = {
processId = 329948
},
command = "runInTerminal",
request_seq = 4,
seq = 2,
success = true,
type = "response"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
event = "initialized",
seq = 5,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:742 ] "request" {
arguments = {
filters = { "uncaught" }
},
command = "setExceptionBreakpoints",
seq = 3,
type = "request"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
command = "setExceptionBreakpoints",
request_seq = 3,
seq = 6,
success = true,
type = "response"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:742 ] "request" {
command = "configurationDone",
seq = 4,
type = "request"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
command = "configurationDone",
request_seq = 4,
seq = 7,
success = true,
type = "response"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
command = "launch",
request_seq = 1,
seq = 8,
success = true,
type = "response"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
isLocalProcess = true,
name = "~/workspace/tmp/py/foo.py",
pointerSize = 64,
startMethod = "launch",
systemProcessId = 329954
},
event = "process",
seq = 9,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
reason = "started",
threadId = 1
},
event = "thread",
seq = 10,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:13Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
reason = "exited",
threadId = 1
},
event = "thread",
seq = 11,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:14Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
body = {
exitCode = 0
},
event = "exited",
seq = 12,
type = "event"
}
[ DEBUG ] 2021-07-22T15:12:14Z+0200 ] ...fig/nvim/pack/plugins/start/nvim-dap/lua/dap/session.lua:510 ] {
event = "terminated",
seq = 13,
type = "event"
}
Activity
karthiknadig commentedon Jul 22, 2021
Mathias Fußenegger (@mfussenegger) when you use
--listenyou are usingattachmode. From the specification:Unless you use the
terminateDebuggeeset on theDisconnectArgumentsrequest, we keep the session running. This is to allow use cases where re-connection to the same process is needed. Note that this is exposed viasupportsTerminateDebuggeecapability.mfussenegger commentedon Jul 22, 2021
Sorry right - the second point about
--listenwas wrong. I must have mixed up something in my setup while I was investigating the issue.But the issue about the debug adapter not terminating in the stdin/stdout mode remains.
mfussenegger commentedon Jul 23, 2021
I've adapted nvim-dap to send a SIGTERM to the process after the session terminated.
From my side this could be closed, unless you still want to change debugpy to exit itself if the session terminates in
launch/ single session modeint19h commentedon Aug 2, 2021
If I remember correctly, this is by design, because VSCode didn't like it when the adapter exited by itself while the connection is still open (even after "disconnect"). However, it should exit as soon as connection is closed - which in case of working over stdio is represented by the other side closing the corresponding pipe fd/handle, thus indicating that no further messages will be sent. Can you try this?
judej commentedon Oct 13, 2021
Closing stale issue. Please reopen if neeeded
wildmoremoremore commentedon Nov 4, 2026
On Windows Vim with Vimspector, sending a successful DAP disconnect request did not make the standalone debugpy adapter process exit. With debugpy 1.8.19, the process was still running after 10 seconds when I tested initialize followed by disconnect.
In Vimspector's Vim job backend, I changed vimspector#internal#job#StopDebugSession() to close the adapter channel's stdin after the DAP disconnect response:
let channel = job_getchannel(job)
if ch_status(channel) ==# 'open'
call ch_close_in(channel)
endif
Then I wait briefly for the adapter to exit and keep a forced-stop timeout as a fallback. With this change, debugpy received EOF and exited naturally with status 0 about 30 ms after the successful disconnect response.
This may help other Vim clients that launch python -m debugpy.adapter over stdio: after disconnect, close the adapter's stdin so the stdio server can finish shutting down.