[python] PY-83362 Cannot stop debugger when debugging FastAPI

Add a temporary solution.
Will be fixed with debugpy


Merge-request: IJ-MR-180688
Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com>

GitOrigin-RevId: 4e536078c38eaa8013dc04c73c2c017def014919
This commit is contained in:
Egor Eliseev
2025-10-30 21:14:56 +00:00
committed by intellij-monorepo-bot
parent 04ce7849e1
commit 57f5eb0619
2 changed files with 70 additions and 0 deletions
@@ -0,0 +1,51 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.debugger
import com.intellij.openapi.Disposable
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
internal sealed class PythonDebuggerScope : CoroutineScope, Disposable {
override val coroutineContext: CoroutineContext =
SupervisorJob() + CoroutineName(javaClass.name)
override fun dispose() {
cancel("Disposed ${javaClass.simpleName}")
}
companion object {
/**
* Schedules a coroutine to run on a given [CoroutineContext].
*/
fun launchOn(context: CoroutineContext, action: suspend CoroutineScope.() -> Unit): Job =
global.launch(context, block = action)
/**
* Retrieves global coroutine scope for the Python Debugger.
* Designed to be used in places where the project scope is not available.
*/
val global: PythonDebuggerScope get() = service<GlobalScopeService>()
/**
* Retrieves project-level scope for the Python Debugger.
* Designed to be used for any asynchronous work where [Project] is accessible.
*/
fun getForProject(project: Project): PythonDebuggerScope =
project.service<ProjectScope>()
@Service
private class GlobalScopeService : PythonDebuggerScope()
@Service(Service.Level.PROJECT)
private class ProjectScope : PythonDebuggerScope()
}
}
@@ -3,6 +3,10 @@ package com.jetbrains.python.run
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.util.io.BaseOutputReader
import com.intellij.util.io.awaitExit
import com.jetbrains.python.debugger.PythonDebuggerScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withTimeoutOrNull
import java.nio.charset.Charset
class PyDebugProcessHandler : PythonProcessHandler {
@@ -16,4 +20,19 @@ class PyDebugProcessHandler : PythonProcessHandler {
BaseOutputReader.Options.forMostlySilentProcess()
}
}
protected override fun doDestroyProcess() {
super.doDestroyProcess()
PythonDebuggerScope.launchOn(Dispatchers.IO) {
withTimeoutOrNull(1000) {
process.awaitExit()
} ?: if (shouldDestroyProcessRecursively() && processCanBeKilledByOS(process)) {
killProcessTree(process)
}
else {
process.destroy()
}
}
}
}