mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-18 20:41:22 +07:00
Python: refactor validatePythonAndGetVersion to become eel-compatible
GitOrigin-RevId: 8d6e71dcd5694f98a7763204c8848d175c8ea78b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
849ae5b514
commit
1aec794bc8
@@ -33,5 +33,8 @@
|
||||
<orderEntry type="library" name="jackson-module-kotlin" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.progress" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit5" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel.provider" />
|
||||
<orderEntry type="module" module-name="intellij.platform.eel" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -85,4 +85,5 @@ path.validation.invalid=Path is invalid: {0}
|
||||
path.validation.inaccessible=Path is inaccessible
|
||||
|
||||
python.get.version.error={0} returned error: {1}
|
||||
python.get.version.too.long={0} took too long
|
||||
python.get.version.wrong.version={0} has a wrong version: {1}
|
||||
|
||||
@@ -1,69 +1,53 @@
|
||||
package com.jetbrains.python
|
||||
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.openapi.diagnostic.fileLogger
|
||||
import com.intellij.util.io.awaitExit
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.platform.eel.getOr
|
||||
import com.intellij.platform.eel.impl.utils.exec
|
||||
import com.jetbrains.python.PySdkBundle.message
|
||||
import com.jetbrains.python.Result.Companion.failure
|
||||
import com.jetbrains.python.psi.LanguageLevel
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor.PYTHON_VERSION_ARG
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor.getVersionStringFromOutput
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import kotlin.io.path.pathString
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
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 (reports error to logs as well).
|
||||
* Ensures that this python is executable and returns its version. Error if python is broken.
|
||||
*
|
||||
* Some pythons might be broken: they may be executable, even return a version, but still fail to execute it.
|
||||
* As we need workable pythons, we validate it by executing
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
suspend fun PythonBinary.validatePythonAndGetVersion(): Result<LanguageLevel, LocalizedErrorString> = withContext(Dispatchers.IO) {
|
||||
val fileLogger = fileLogger()
|
||||
val process =
|
||||
try {
|
||||
GeneralCommandLine(pathString, "-c", "print(1)").createProcess()
|
||||
}
|
||||
catch (e: ExecutionException) {
|
||||
val error = message("python.get.version.error", pathString, e.message)
|
||||
fileLogger.warn(error, e)
|
||||
return@withContext failure(LocalizedErrorString(error))
|
||||
}
|
||||
val timeout = 5.seconds
|
||||
val exitCode = withTimeoutOrNull(timeout) {
|
||||
process.awaitExit()
|
||||
val smokeTestOutput = executeWithResult("-c", "print(1)").getOr { return@withContext it }.trim()
|
||||
if (smokeTestOutput != "1") {
|
||||
return@withContext failure(LocalizedErrorString(message("python.get.version.error", pathString, smokeTestOutput)))
|
||||
}
|
||||
when (exitCode) {
|
||||
null -> {
|
||||
fileLogger.warn("$this didn't return in $timeout, skipping")
|
||||
}
|
||||
0 -> {
|
||||
val pythonVersion = PythonSdkFlavor.getVersionStringStatic(pathString)
|
||||
?: return@withContext failure(LocalizedErrorString(message("python.get.version.wrong.version", pathString, "")))
|
||||
val languageLevel = LanguageLevel.fromPythonVersion(pythonVersion)
|
||||
if (languageLevel == null) {
|
||||
fileLogger.warn("$pythonVersion is not valid version")
|
||||
return@withContext failure(LocalizedErrorString(message("python.get.version.wrong.version", pathString, "")))
|
||||
}
|
||||
return@withContext Result.success(languageLevel)
|
||||
}
|
||||
else -> {
|
||||
fileLogger.warn("$this exited with code ${exitCode}, skipping")
|
||||
}
|
||||
|
||||
val versionString = executeWithResult(PYTHON_VERSION_ARG).getOr { return@withContext it }
|
||||
val languageLevel = getVersionStringFromOutput(versionString)?.let {
|
||||
LanguageLevel.fromPythonVersion(it)
|
||||
}
|
||||
process.destroyForcibly()
|
||||
if (withTimeoutOrNull(500.milliseconds) {
|
||||
process.awaitExit()
|
||||
} == null) {
|
||||
fileLogger.warn("Process $process still running, might be leaked")
|
||||
if (languageLevel == null) {
|
||||
return@withContext failure(LocalizedErrorString(message("python.get.version.wrong.version", pathString, versionString)))
|
||||
}
|
||||
return@withContext failure(LocalizedErrorString(message("python.get.version.error", pathString, exitCode)))
|
||||
return@withContext Result.success(languageLevel)
|
||||
}
|
||||
|
||||
private suspend fun PythonBinary.executeWithResult(vararg args: String): Result<@NlsSafe String, LocalizedErrorString> {
|
||||
val output = exec(*args, timeout = 5.seconds).getOr {
|
||||
val text = it.error?.message ?: message("python.get.version.too.long", pathString)
|
||||
return failure(LocalizedErrorString(text))
|
||||
}
|
||||
return if (output.exitCode != 0) {
|
||||
failure(LocalizedErrorString(message("python.get.version.error", pathString, "code ${output.exitCode}, {output.stderr}")))
|
||||
}
|
||||
else {
|
||||
Result.success(output.stdout)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user