PY-80049: Check Python2 version correctly.

We execute `--version` to find python version.
Since 3.4 Python uses stdout for output, but before that it used stderr.

We now check both: stdout and stderr.


(cherry picked from commit ca9059b71cabbd1a94e26523192cdf9eeb8c1eb1)

IJ-CR-159420

GitOrigin-RevId: b72d289da7f1643c18dd35fc97a7a32daec7b0f3
This commit is contained in:
Ilya.Kazakevich
2025-03-28 12:59:26 +01:00
committed by intellij-monorepo-bot
parent ce63d58714
commit 35519fd1d3
6 changed files with 41 additions and 15 deletions

View File

@@ -4,6 +4,7 @@ import com.intellij.openapi.util.NlsSafe
import com.intellij.platform.eel.getOr
import com.intellij.platform.eel.path.EelPath
import com.intellij.platform.eel.provider.getEelDescriptor
import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult
import com.intellij.platform.eel.provider.utils.exec
import com.intellij.platform.eel.provider.utils.stderrString
import com.intellij.platform.eel.provider.utils.stdoutString
@@ -23,7 +24,6 @@ import kotlin.io.path.pathString
import kotlin.time.Duration.Companion.seconds
// TODO: PythonInterpreterService: validate system python
/**
* Ensures that this python is executable and returns its version. Error if python is broken.
*
@@ -32,24 +32,25 @@ import kotlin.time.Duration.Companion.seconds
*/
@ApiStatus.Internal
suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, @NlsSafe String> = withContext(Dispatchers.IO) {
val smokeTestOutput = executeWithResult("-c", "print(1)").getOr { return@withContext it }.trim()
val smokeTestOutput = executeWithResult("-c", "print(1)").getOr { return@withContext it }.stdoutString.trim()
if (smokeTestOutput != "1") {
return@withContext failure(message("python.get.version.error", pathString, smokeTestOutput))
}
val versionString = executeWithResult(PYTHON_VERSION_ARG).getOr { return@withContext it }
val versionOutput = executeWithResult(PYTHON_VERSION_ARG).getOr { return@withContext it }
// Python 2 might return version as stderr, see https://bugs.python.org/issue18338
val versionString = versionOutput.stdoutString.let { it.ifBlank { versionOutput.stderrString } }
val languageLevel = getLanguageLevelFromVersionStringStaticSafe(versionString.trim())
if (languageLevel == null) {
return@withContext failure(message("python.get.version.wrong.version", pathString, versionString))
return@withContext failure(message("python.get.version.wrong.version", pathString, versionOutput))
}
return@withContext Result.success(languageLevel)
}
/**
* Executes [this] with [args], returns either stdout or error (if execution failed or exit code != 0)
* Executes [this] with [args], returns either output or error (if execution failed or exit code != 0)
*/
@ApiStatus.Internal
suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe String, @NlsSafe String> {
private suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe EelProcessExecutionResult, @NlsSafe String> {
val output = exec(*args, timeout = 5.seconds).getOr {
val text = it.error?.message ?: message("python.get.version.too.long", pathString)
return failure(text)
@@ -58,7 +59,7 @@ suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe
failure(message("python.get.version.error", pathString, "code ${output.exitCode}, ${output.stderrString}"))
}
else {
Result.success(output.stdoutString)
Result.success(output)
}
}