feat(debug): enable Python debug workflow and debug server integration
Adds an end-to-end Python debug workflow for the extension. Includes a new prepare-debug.ps1 script and a VS Code task. Extends the Python server and extension to coordinate a debug session and safe startup. - Add prepare-debug.ps1 and a VS Code task to build the debug bundle - Enable Python debug wiring in the server and tests - Ensure a single stable Python debug session during startup
This commit is contained in:
+51
-30
@@ -39,13 +39,26 @@ async function createServer(
|
||||
initializationOptions: IInitOptions
|
||||
): Promise<LanguageClient> {
|
||||
const command = settings.interpreter[0]
|
||||
if (!command) {
|
||||
throw new Error("No Python interpreter is configured for the language server.")
|
||||
}
|
||||
const cwd = settings.cwd
|
||||
|
||||
// Set debugger path needed for debugging python code.
|
||||
const newEnv = { ...process.env }
|
||||
const debuggerPath = await getDebuggerPath()
|
||||
const isDebugScript = await fsapi.pathExists(DEBUG_SERVER_SCRIPT_PATH)
|
||||
if (newEnv.USE_DEBUGPY && debuggerPath) {
|
||||
const debugRequested = newEnv.USE_DEBUGPY?.toLowerCase() === "true"
|
||||
if (debugRequested && !isDebugScript) {
|
||||
throw new Error(`Python debug bootstrap not found: ${DEBUG_SERVER_SCRIPT_PATH}`)
|
||||
}
|
||||
|
||||
const debuggerPath = debugRequested ? await getDebuggerPath() : undefined
|
||||
if (debugRequested && !debuggerPath) {
|
||||
throw new Error(
|
||||
"Python debugging was requested, but the Python Debugger extension did not provide debugpy."
|
||||
)
|
||||
}
|
||||
if (debugRequested && debuggerPath) {
|
||||
newEnv.DEBUGPY_PATH = debuggerPath
|
||||
} else {
|
||||
newEnv.USE_DEBUGPY = "False"
|
||||
@@ -57,10 +70,13 @@ async function createServer(
|
||||
// Set notification type
|
||||
newEnv.LS_SHOW_NOTIFICATION = settings.showNotifications
|
||||
|
||||
const args =
|
||||
newEnv.USE_DEBUGPY === "False" || !isDebugScript
|
||||
? settings.interpreter.slice(1).concat([SERVER_SCRIPT_PATH])
|
||||
: settings.interpreter.slice(1).concat([DEBUG_SERVER_SCRIPT_PATH])
|
||||
const serverScript = debugRequested ? DEBUG_SERVER_SCRIPT_PATH : SERVER_SCRIPT_PATH
|
||||
const interpreterArgs = settings.interpreter.slice(1)
|
||||
if (debugRequested && !interpreterArgs.includes("-Xfrozen_modules=off")) {
|
||||
interpreterArgs.push("-Xfrozen_modules=off")
|
||||
}
|
||||
const args = interpreterArgs.concat([serverScript])
|
||||
traceInfo(`Python debug mode: ${debugRequested ? "enabled" : "disabled"}`)
|
||||
traceInfo(`Server run command: ${[command, ...args].join(" ")}`)
|
||||
|
||||
const serverOptions: ServerOptions = {
|
||||
@@ -108,35 +124,40 @@ export async function restartServer(
|
||||
const projectRoot = await getProjectRoot()
|
||||
const workspaceSetting = await getWorkspaceSettings(serverId, projectRoot, true)
|
||||
|
||||
const newLSClient = await createServer(workspaceSetting, serverId, serverName, outputChannel, {
|
||||
settings: await getExtensionSettings(serverId, true),
|
||||
globalSettings: await getGlobalSettings(serverId, false)
|
||||
})
|
||||
traceInfo(`Server: Start requested.`)
|
||||
_disposables.push(
|
||||
newLSClient.onDidChangeState((e) => {
|
||||
switch (e.newState) {
|
||||
case State.Stopped:
|
||||
traceVerbose(`Server State: Stopped`)
|
||||
break
|
||||
case State.Starting:
|
||||
traceVerbose(`Server State: Starting`)
|
||||
break
|
||||
case State.Running:
|
||||
traceVerbose(`Server State: Running`)
|
||||
break
|
||||
}
|
||||
})
|
||||
)
|
||||
try {
|
||||
const newLSClient = await createServer(
|
||||
workspaceSetting,
|
||||
serverId,
|
||||
serverName,
|
||||
outputChannel,
|
||||
{
|
||||
settings: await getExtensionSettings(serverId, true),
|
||||
globalSettings: await getGlobalSettings(serverId, false)
|
||||
}
|
||||
)
|
||||
traceInfo(`Server: Start requested.`)
|
||||
_disposables.push(
|
||||
newLSClient.onDidChangeState((e) => {
|
||||
switch (e.newState) {
|
||||
case State.Stopped:
|
||||
traceVerbose(`Server State: Stopped`)
|
||||
break
|
||||
case State.Starting:
|
||||
traceVerbose(`Server State: Starting`)
|
||||
break
|
||||
case State.Running:
|
||||
traceVerbose(`Server State: Running`)
|
||||
break
|
||||
}
|
||||
})
|
||||
)
|
||||
await newLSClient.start()
|
||||
const level = getLSClientTraceLevel(outputChannel.logLevel, env.logLevel)
|
||||
await newLSClient.setTrace(level)
|
||||
return newLSClient
|
||||
} catch (ex) {
|
||||
traceError(`Server: Start failed: ${ex}`)
|
||||
disposeServerResources()
|
||||
return undefined
|
||||
}
|
||||
|
||||
const level = getLSClientTraceLevel(outputChannel.logLevel, env.logLevel)
|
||||
await newLSClient.setTrace(level)
|
||||
return newLSClient
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user