PY-84953: Unify tool detection approach

Our tool detection approach varies a lot. We have different logic for
uv, poetry and other tools. Also, uv detection is not suspendable and
doesn't have any explicit thread requirements, even though it performs
I/O operations.

This change makes such detection unified and suspendable (where
possible) and moves it to BGT.

GitOrigin-RevId: 18e9c4cc085c8d373c82ad2874033b53711f09c6
This commit is contained in:
Alexey Katsman
2025-11-03 16:25:22 +00:00
committed by intellij-monorepo-bot
parent cc191b2b3d
commit 27838bc2da
32 changed files with 217 additions and 168 deletions
@@ -31,6 +31,7 @@ import com.jetbrains.python.sdk.PythonSdkType
import com.jetbrains.python.sdk.basePath
import com.jetbrains.python.sdk.configuration.*
import com.jetbrains.python.sdk.findAmongRoots
import com.jetbrains.python.sdk.impl.PySdkBundle
import com.jetbrains.python.sdk.impl.resolvePythonBinary
import com.jetbrains.python.sdk.legacy.PythonSdkUtil
import com.jetbrains.python.sdk.pipenv.*
@@ -139,12 +140,12 @@ class PyPipfileSdkConfiguration : PyProjectSdkConfigurationExtension {
val path = withContext(Dispatchers.IO) { VirtualEnvReader.Instance.findPythonInPythonRoot(Path.of(pipEnv)) }
if (path == null) {
return@withBackgroundProgress PyResult.localizedError(PyBundle.message("cannot.find.executable", "python", pipEnv))
return@withBackgroundProgress PyResult.localizedError(PySdkBundle.message("cannot.find.executable", "python", pipEnv))
}
val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path.toString())
if (file == null) {
return@withBackgroundProgress PyResult.localizedError(PyBundle.message("cannot.find.executable", "python", path))
return@withBackgroundProgress PyResult.localizedError(PySdkBundle.message("cannot.find.executable", "python", path))
}
PySdkConfigurationCollector.logPipEnv(module.project, PipEnvResult.CREATED)
@@ -24,6 +24,7 @@ import com.jetbrains.python.projectModel.poetry.POETRY_TOOL_ID
import com.jetbrains.python.sdk.PythonSdkType
import com.jetbrains.python.sdk.basePath
import com.jetbrains.python.sdk.configuration.*
import com.jetbrains.python.sdk.impl.PySdkBundle
import com.jetbrains.python.sdk.impl.resolvePythonBinary
import com.jetbrains.python.sdk.legacy.PythonSdkUtil
import com.jetbrains.python.sdk.poetry.*
@@ -97,10 +98,10 @@ class PyPoetrySdkConfiguration : PyProjectTomlConfigurationExtension {
val tomlFile = PyProjectToml.findFile(module)
val poetry = setupPoetry(basePath, null, true, tomlFile == null).getOr { return@withBackgroundProgress it }
val path = poetry.resolvePythonBinary()
?: return@withBackgroundProgress PyResult.localizedError(PyBundle.message("cannot.find.executable", "python", poetry))
?: return@withBackgroundProgress PyResult.localizedError(PySdkBundle.message("cannot.find.executable", "python", poetry))
val file = LocalFileSystem.getInstance().refreshAndFindFileByPath(path.pathString)
?: return@withBackgroundProgress PyResult.localizedError(PyBundle.message("cannot.find.executable", "python", path))
?: return@withBackgroundProgress PyResult.localizedError(PySdkBundle.message("cannot.find.executable", "python", path))
LOGGER.debug("Setting up associated poetry environment: $path, $basePath")
val sdk = SdkConfigurationUtil.setupSdk(