mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 13:31:28 +07:00
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. GitOrigin-RevId: ca9059b71cabbd1a94e26523192cdf9eeb8c1eb1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f313e649fd
commit
92b5532dc1
@@ -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
|
||||
@@ -22,7 +23,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.
|
||||
*
|
||||
@@ -31,24 +31,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)
|
||||
@@ -57,7 +58,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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
19
python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt
vendored
Normal file
19
python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.python.junit5Tests.env.systemPython
|
||||
|
||||
import com.intellij.python.community.services.systemPython.SystemPythonService
|
||||
import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase
|
||||
import com.intellij.testFramework.common.timeoutRunBlocking
|
||||
import com.jetbrains.python.getOrThrow
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
@PyEnvTestCase
|
||||
class Py27Test {
|
||||
@Test
|
||||
fun testPy27(): Unit = timeoutRunBlocking {
|
||||
val testEnvironments = SystemPythonService().findSystemPythons()
|
||||
val python27 = testEnvironments.firstOrNull { it.languageLevel == LanguageLevel.PYTHON27 } ?: error("No 2.7 found in $testEnvironments")
|
||||
SystemPythonService().registerSystemPython(python27.pythonBinary).getOrThrow()
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.deleteExisting
|
||||
import kotlin.io.path.pathString
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
|
||||
@PyEnvTestCase
|
||||
class SystemPythonServiceShowCaseTest {
|
||||
@@ -49,7 +50,7 @@ class SystemPythonServiceShowCaseTest {
|
||||
val eelApi = systemPython.pythonBinary.getEelDescriptor().upgrade()
|
||||
val process = eelApi.exec.executeProcess(systemPython.pythonBinary.pathString, "--version").getOrThrow()
|
||||
val output = async {
|
||||
process.stdout.readWholeText().getOrThrow()
|
||||
(if (systemPython.languageLevel.isPy3K) process.stdout else process.stderr).readWholeText().getOrThrow()
|
||||
}
|
||||
Assertions.assertEquals(0, process.exitCode.await(), "Wrong exit code")
|
||||
val versionString = PythonSdkFlavor.getLanguageLevelFromVersionStringStaticSafe(output.await())!!
|
||||
|
||||
@@ -6,9 +6,11 @@ import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.platform.eel.EelApi
|
||||
import com.intellij.platform.eel.provider.localEel
|
||||
import com.intellij.python.community.services.systemPython.SystemPythonProvider
|
||||
import com.intellij.python.community.testFramework.testEnv.TypeVanillaPython
|
||||
import com.intellij.python.community.testFramework.testEnv.TypeVanillaPython3
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.toSet
|
||||
|
||||
/**
|
||||
@@ -18,8 +20,8 @@ internal class EnvTestPythonProvider : SystemPythonProvider {
|
||||
override suspend fun findSystemPythons(eelApi: EelApi): Result<Set<PythonBinary>> {
|
||||
var pythons = emptySet<PythonBinary>()
|
||||
if (eelApi == localEel) {
|
||||
pythons = TypeVanillaPython3
|
||||
.getTestEnvironments()
|
||||
// Add Py27 temporary to test Py27
|
||||
pythons = merge(TypeVanillaPython3.getTestEnvironments(), TypeVanillaPython2.getTestEnvironments())
|
||||
.map { (python, closeable) ->
|
||||
Disposer.register(ApplicationManager.getApplication()) {
|
||||
closeable.close()
|
||||
@@ -31,4 +33,6 @@ internal class EnvTestPythonProvider : SystemPythonProvider {
|
||||
|
||||
return Result.success(pythons)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object TypeVanillaPython2 : TypeVanillaPython("python2.7")
|
||||
@@ -41,7 +41,6 @@ abstract class PythonType<T : Any>(private val tag: @NonNls String) {
|
||||
?: error("Can't get language level for $flavor , $binary")
|
||||
}
|
||||
.sortedByDescending { (_, languageLevel) -> languageLevel }
|
||||
.filter { (_, languageLevel) -> languageLevel.isPy3K }
|
||||
.map { (path, _) -> path }
|
||||
}
|
||||
return pythons.asFlow()
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.PythonBinary
|
||||
import java.nio.file.Path
|
||||
|
||||
data object TypeVanillaPython3 : PythonType<PythonBinary>("python3") {
|
||||
open class TypeVanillaPython(tag: String) : PythonType<PythonBinary>(tag) {
|
||||
override suspend fun createSdkFor(python: PythonBinary): Sdk = createSdk(python)
|
||||
|
||||
fun createSdk(python: PythonBinary): Sdk =
|
||||
@@ -27,4 +27,6 @@ data object TypeVanillaPython3 : PythonType<PythonBinary>("python3") {
|
||||
Disposer.dispose(disposable)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TypeVanillaPython3 : TypeVanillaPython("python3")
|
||||
Reference in New Issue
Block a user