diff --git a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/PythonSdkConfigurator.kt b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/PythonSdkConfigurator.kt index d4cad65f6d29..c44516533700 100644 --- a/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/PythonSdkConfigurator.kt +++ b/python/ide/impl/src/com/intellij/pycharm/community/ide/impl/PythonSdkConfigurator.kt @@ -124,7 +124,9 @@ class PythonSdkConfigurator : DirectoryProjectConfigurator { return@withContext } - if (findPreviousUsedSdk(existingSdks, project, module)) { + val systemPythons = findSortedSystemPythons(module) + + if (findPreviousUsedSdk(module, existingSdks, systemPythons)) { return@withContext } @@ -132,48 +134,60 @@ class PythonSdkConfigurator : DirectoryProjectConfigurator { return@withContext } - findSystemWideSdk(module, existingSdks, project) + findSystemWideSdk(module, existingSdks, systemPythons) } private suspend fun findSystemWideSdk( module: Module, existingSdks: List, - project: Project, + systemPythons: List, ): Unit = reportRawProgress { indicator -> indicator.text(PyBundle.message("looking.for.system.interpreter")) thisLogger().debug("Looking for a system-wide interpreter") - val eelDescriptor = module.project.getEelDescriptor() val homePaths = existingSdks - .mapNotNull { sdk -> sdk.homePath?.let { homePath -> Path.of(homePath) } } - .filter { it.getEelDescriptor() == eelDescriptor } - SystemPythonService().findSystemPythons(eelDescriptor.toEelApi()) - .sortedWith(compareByDescending { it.languageLevel }.thenBy { it.pythonBinary }) - .sortedByDescending { it.pythonBinary } - .sortedByDescending { it.languageLevel } - .firstOrNull { it.pythonBinary !in homePaths }?.let { - thisLogger().debug { "Detected system-wide interpreter: $it" } - withContext(Dispatchers.EDT) { - SdkConfigurationUtil.createAndAddSDK(project, it.pythonBinary, PythonSdkType.getInstance())?.apply { - thisLogger().debug { "Created system-wide interpreter: $this" } - setReadyToUseSdk(project, module, this) - } + .mapNotNull { sdk -> sdk.takeIf { !it.isTargetBased() }?.homePath?.let { homePath -> Path.of(homePath) } } + .filter { it.getEelDescriptor() == module.project.getEelDescriptor() } + systemPythons.firstOrNull { it.pythonBinary !in homePaths }?.let { + thisLogger().debug { "Detected system-wide interpreter: $it" } + withContext(Dispatchers.EDT) { + SdkConfigurationUtil.createAndAddSDK(module.project, it.pythonBinary, PythonSdkType.getInstance())?.apply { + thisLogger().debug { "Created system-wide interpreter: $this" } + setReadyToUseSdk(module.project, module, this) } } + } } private suspend fun findPreviousUsedSdk( - existingSdks: List, - project: Project, module: Module, + existingSdks: List, + systemPythons: List, ): Boolean = reportRawProgress { indicator -> indicator.text(PyBundle.message("looking.for.previous.system.interpreter")) thisLogger().debug("Looking for the previously used system-wide interpreter") - val sdk = mostPreferred(filterSystemWideSdks(existingSdks)) ?: return@reportRawProgress false + val sdk = systemPythons.firstNotNullOfOrNull { systemPython -> + existingSdks.firstOrNull { sdk -> + val sdkHomePath = sdk.takeIf { !it.isTargetBased() } + ?.homePath + ?.let { Path.of(it) } + ?.takeIf { it.getEelDescriptor() == module.project.getEelDescriptor() } + sdkHomePath == systemPython.pythonBinary + } + } ?: return@reportRawProgress false thisLogger().debug { "Previously used system-wide interpreter: $sdk" } - setReadyToUseSdk(project, module, sdk) + setReadyToUseSdk(module.project, module, sdk) return@reportRawProgress true } + private suspend fun findSortedSystemPythons(module: Module) = reportRawProgress { indicator -> + indicator.text(PyBundle.message("looking.for.system.pythons")) + SystemPythonService().findSystemPythons(module.project.getEelDescriptor().toEelApi()) + .sortedWith( + // Free-threaded Python is unstable, we don't want to have it selected by default if we have alternatives + compareBy { it.pythonInfo.freeThreaded }.thenByDescending { it.pythonInfo.languageLevel } + ) + } + private suspend fun findDefaultInterpreter(project: Project, module: Module): Boolean = reportRawProgress { indicator -> indicator.text(PyBundle.message("looking.for.default.interpreter")) thisLogger().debug("Looking for the default interpreter setting for a new project") diff --git a/python/interpreters/src/advancedApi/advancedApi.kt b/python/interpreters/src/advancedApi/advancedApi.kt index f1b5db35e7ae..1910abaa33d9 100644 --- a/python/interpreters/src/advancedApi/advancedApi.kt +++ b/python/interpreters/src/advancedApi/advancedApi.kt @@ -5,10 +5,10 @@ import com.intellij.python.community.execService.* import com.intellij.python.community.execService.python.HelperName import com.intellij.python.community.execService.python.advancedApi.executeHelperAdvanced import com.intellij.python.community.execService.python.advancedApi.executePythonAdvanced -import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetInfo import com.intellij.python.community.interpreters.ValidInterpreter +import com.jetbrains.python.PythonInfo import com.jetbrains.python.errorProcessing.PyResult -import com.jetbrains.python.psi.LanguageLevel import org.jetbrains.annotations.ApiStatus // This in advanced API, most probably you need "api.kt" @@ -38,14 +38,14 @@ suspend fun ExecService.executeHelperAdvanced( ): PyResult = executeHelperAdvanced(python.asExecutablePython, helper, args, options, procListener, processOutputTransformer) /** - * Ensures that this python is executable and returns its version. Error if python is broken. + * Ensures that this python is executable and returns its info. 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 ValidInterpreter.validatePythonAndGetVersion(): PyResult = - ExecService().validatePythonAndGetVersion(asExecutablePython) +suspend fun ValidInterpreter.validatePythonAndGetInfo(): PyResult = + ExecService().validatePythonAndGetInfo(asExecutablePython) /** * Execute [helper] on [python]. For remote eels, [helper] is copied (but only one file!). diff --git a/python/interpreters/src/api.kt b/python/interpreters/src/api.kt index c2a7a3dcead0..7cc46a663d55 100644 --- a/python/interpreters/src/api.kt +++ b/python/interpreters/src/api.kt @@ -9,7 +9,7 @@ import com.intellij.python.community.execService.python.advancedApi.executeHelpe import com.intellij.python.community.execService.python.advancedApi.executePythonAdvanced import com.intellij.python.community.interpreters.impl.InterpreterFields import com.intellij.python.community.interpreters.impl.InterpreterServiceImpl -import com.intellij.python.community.services.shared.PythonWithLanguageLevel +import com.intellij.python.community.services.shared.PythonWithPythonInfo import com.intellij.python.community.services.shared.PythonWithUi import com.jetbrains.python.errorProcessing.PyResult import org.jetbrains.annotations.Nls @@ -25,7 +25,7 @@ sealed interface Interpreter : InterpreterFields * Interpreter was usable at the moment it was loaded. * It has [ui] and [getReadableName] and can be used to execute code against [ExecService] (see extension functions) */ -interface ValidInterpreter : PythonWithLanguageLevel, PythonWithUi, Interpreter +interface ValidInterpreter : PythonWithPythonInfo, PythonWithUi, Interpreter /** * At the moment of loading this interpreter was invalid due to [invalidMessage]. diff --git a/python/interpreters/src/impl/InterpreterImpl.kt b/python/interpreters/src/impl/InterpreterImpl.kt index 050a0e16207f..0ed8cf1eb172 100644 --- a/python/interpreters/src/impl/InterpreterImpl.kt +++ b/python/interpreters/src/impl/InterpreterImpl.kt @@ -8,20 +8,20 @@ import com.intellij.python.community.interpreters.InvalidInterpreter import com.intellij.python.community.interpreters.ValidInterpreter import com.intellij.python.community.services.shared.PythonWithName import com.jetbrains.python.PyToolUIInfo -import com.jetbrains.python.psi.LanguageLevel +import com.jetbrains.python.PythonInfo import com.jetbrains.python.sdk.PythonSdkAdditionalData import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.Nls import java.util.* internal class ValidInterpreterImpl( - override val languageLevel: LanguageLevel, + override val pythonInfo: PythonInfo, override val asExecutablePython: ExecutablePython, private val mixin: SdkMixin, override val ui: PyToolUIInfo?, ) : ValidInterpreter, InterpreterFields by mixin { override fun toString(): String { - return "ValidInterpreterImpl(languageLevel=$languageLevel, asExecutablePython=$asExecutablePython)" + return "ValidInterpreterImpl(pythonInfo=$pythonInfo, asExecutablePython=$asExecutablePython)" } } diff --git a/python/interpreters/src/impl/InterpreterServiceImpl.kt b/python/interpreters/src/impl/InterpreterServiceImpl.kt index 7489894dfbe1..749f75be31d9 100644 --- a/python/interpreters/src/impl/InterpreterServiceImpl.kt +++ b/python/interpreters/src/impl/InterpreterServiceImpl.kt @@ -7,20 +7,21 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.roots.ModuleRootManager import com.intellij.python.community.execService.ExecService import com.intellij.python.community.execService.python.advancedApi.ExecutablePython -import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetInfo import com.intellij.python.community.interpreters.Interpreter import com.intellij.python.community.interpreters.InterpreterService import com.intellij.python.community.interpreters.impl.PyInterpreterBundle.message import com.intellij.python.community.interpreters.spi.InterpreterProvider import com.jetbrains.python.PyToolUIInfo +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.MessageError import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.sdk.PythonSdkAdditionalData -import com.jetbrains.python.sdk.legacy.PythonSdkUtil -import com.jetbrains.python.sdk.legacy.PythonSdkUtil.isPythonSdk import com.jetbrains.python.sdk.flavors.PyFlavorData import com.jetbrains.python.sdk.getOrCreateAdditionalData +import com.jetbrains.python.sdk.legacy.PythonSdkUtil +import com.jetbrains.python.sdk.legacy.PythonSdkUtil.isPythonSdk import java.nio.file.InvalidPathException import java.nio.file.Path import kotlin.io.path.Path @@ -88,11 +89,11 @@ private suspend fun createInterpreter(provider: InterpreterPr } is Result.Success -> { val executablePython = r.result - val languageLevel = sdk.versionString?.let { LanguageLevel.fromPythonVersionSafe(it) } - ?: ExecService().validatePythonAndGetVersion(executablePython).getOr { - return InvalidInterpreterImpl(SdkMixin(sdk, additionalData), message("py.interpreter.no.version", it.error.message)) - } - return ValidInterpreterImpl(languageLevel, executablePython, SdkMixin(sdk, additionalData), provider.ui) + val pythonInfo = sdk.versionString?.let { LanguageLevel.fromPythonVersionSafe(it) }?.let { PythonInfo(it) } + ?: ExecService().validatePythonAndGetInfo(executablePython).getOr { + return InvalidInterpreterImpl(SdkMixin(sdk, additionalData), message("py.interpreter.no.version", it.error.message)) + } + return ValidInterpreterImpl(pythonInfo, executablePython, SdkMixin(sdk, additionalData), provider.ui) } } } diff --git a/python/openapi/src/com/jetbrains/python/PythonInfo.kt b/python/openapi/src/com/jetbrains/python/PythonInfo.kt new file mode 100644 index 000000000000..1c16fe6dc859 --- /dev/null +++ b/python/openapi/src/com/jetbrains/python/PythonInfo.kt @@ -0,0 +1,22 @@ +// 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 + +import com.jetbrains.python.psi.LanguageLevel +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Internal +data class PythonInfo( + val languageLevel: LanguageLevel, + val freeThreaded: Boolean = false, +) : Comparable { + override fun compareTo(other: PythonInfo): Int { + // Backward, a newer version has higher priority + val versionComparison = -LanguageLevel.VERSION_COMPARATOR.compare(languageLevel, other.languageLevel) + if (versionComparison != 0) { + return versionComparison + } + + // Not free threaded is more stable, hence it has higher priority + return freeThreaded.compareTo(other.freeThreaded) + } +} \ No newline at end of file diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index 6e5575cd14d3..26ca6ecb62ce 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -1170,6 +1170,7 @@ install.packages.from.pipfile=Install packages from Pipfile looking.for.default.interpreter=Looking for the default interpreter setting for a new project looking.for.previous.system.interpreter=Looking for the previously used system-wide interpreter looking.for.system.interpreter=Looking for a system-wide interpreter +looking.for.system.pythons=Looking for system pythons looking.for.shared.conda.environment=Looking for a shared conda environment current.interpreter=Current Interpreter: {0} switch.python.interpreter=Switch Python Interpreter diff --git a/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt b/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt index 3716d17b8b50..d8c1e9e93ad5 100644 --- a/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt +++ b/python/python-exec-service/execService.python/src/advancedApi/advancedApi.kt @@ -5,9 +5,9 @@ import com.intellij.python.community.execService.* import com.intellij.python.community.execService.impl.transformerToHandler import com.intellij.python.community.execService.python.HelperName import com.intellij.python.community.execService.python.addHelper -import com.intellij.python.community.execService.python.impl.validatePythonAndGetVersionImpl +import com.intellij.python.community.execService.python.impl.validatePythonAndGetInfoImpl +import com.jetbrains.python.PythonInfo import com.jetbrains.python.errorProcessing.PyResult -import com.jetbrains.python.psi.LanguageLevel import org.jetbrains.annotations.ApiStatus // This in advanced API, most probably you need "api.kt" @@ -44,11 +44,11 @@ suspend fun ExecService.executeHelperAdvanced( options, transformerToHandler(procListener, processOutputTransformer)) /** - * Ensures that this python is executable and returns its version. Error if python is broken. + * Ensures that this python is executable and returns its info. 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 ExecService.validatePythonAndGetVersion(python: ExecutablePython): PyResult = - validatePythonAndGetVersionImpl(python) +suspend fun ExecService.validatePythonAndGetInfo(python: ExecutablePython): PyResult = + validatePythonAndGetInfoImpl(python) diff --git a/python/python-exec-service/execService.python/src/api.kt b/python/python-exec-service/execService.python/src/api.kt index 608807369eba..7b53dfb95810 100644 --- a/python/python-exec-service/execService.python/src/api.kt +++ b/python/python-exec-service/execService.python/src/api.kt @@ -4,11 +4,11 @@ package com.intellij.python.community.execService.python import com.intellij.python.community.execService.* import com.intellij.python.community.execService.python.advancedApi.ExecutablePython import com.intellij.python.community.execService.python.advancedApi.executeHelperAdvanced -import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.advancedApi.validatePythonAndGetInfo import com.intellij.python.community.helpersLocator.PythonHelpersLocator import com.jetbrains.python.PythonBinary +import com.jetbrains.python.PythonInfo import com.jetbrains.python.errorProcessing.PyResult -import com.jetbrains.python.psi.LanguageLevel /** * Python binary itself (i.e python.exe) @@ -30,17 +30,17 @@ suspend fun ExecService.executeHelper( executeHelperAdvanced(ExecutablePython.vanillaExecutablePython(python), helper, args, options, procListener, ZeroCodeStdoutTransformer) /** - * Ensures that this python is executable and returns its version. Error if python is broken. + * Ensures that this python is executable and returns its info. 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 */ -suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinaryOnEelOrTarget): PyResult = - validatePythonAndGetVersion(ExecutablePython.vanillaExecutablePython(python)) +suspend fun ExecService.validatePythonAndGetInfo(python: PythonBinaryOnEelOrTarget): PyResult = + validatePythonAndGetInfo(ExecutablePython.vanillaExecutablePython(python)) -suspend fun PythonBinaryOnEelOrTarget.validatePythonAndGetVersion(): PyResult = ExecService().validatePythonAndGetVersion(this) -suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinary): PyResult = validatePythonAndGetVersion(python.asBinToExec()) -suspend fun PythonBinary.validatePythonAndGetVersion(): PyResult = asBinToExec().validatePythonAndGetVersion() +suspend fun PythonBinaryOnEelOrTarget.validatePythonAndGetInfo(): PyResult = ExecService().validatePythonAndGetInfo(this) +suspend fun ExecService.validatePythonAndGetInfo(python: PythonBinary): PyResult = validatePythonAndGetInfo(python.asBinToExec()) +suspend fun PythonBinary.validatePythonAndGetInfo(): PyResult = asBinToExec().validatePythonAndGetInfo() /** diff --git a/python/python-exec-service/execService.python/src/impl/impl.kt b/python/python-exec-service/execService.python/src/impl/impl.kt index ff74633db92b..a3fbc656f854 100644 --- a/python/python-exec-service/execService.python/src/impl/impl.kt +++ b/python/python-exec-service/execService.python/src/impl/impl.kt @@ -1,6 +1,7 @@ // 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.community.execService.python.impl +import com.intellij.openapi.diagnostic.fileLogger import com.intellij.openapi.util.NlsSafe import com.intellij.platform.eel.provider.utils.EelProcessExecutionResult import com.intellij.platform.eel.provider.utils.stderrString @@ -11,9 +12,11 @@ import com.intellij.python.community.execService.python.advancedApi.ExecutablePy import com.intellij.python.community.execService.python.advancedApi.executePythonAdvanced import com.intellij.python.community.execService.python.impl.PyExecPythonBundle.message import com.jetbrains.python.PYTHON_VERSION_ARG +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.errorProcessing.getOr +import com.jetbrains.python.onFailure import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.sdk.flavors.PythonSdkFlavor.getLanguageLevelFromVersionStringStaticSafe import kotlinx.coroutines.Dispatchers @@ -23,7 +26,7 @@ import kotlin.io.path.pathString import kotlin.time.Duration.Companion.minutes @ApiStatus.Internal -internal suspend fun ExecService.validatePythonAndGetVersionImpl(python: ExecutablePython): PyResult = withContext(Dispatchers.IO) { +internal suspend fun ExecService.validatePythonAndGetInfoImpl(python: ExecutablePython): PyResult = withContext(Dispatchers.IO) { val options = ExecOptions(timeout = 1.minutes) val smokeTestOutput = executePythonAdvanced(python, Args("-c", "print(1)"), processInteractiveHandler = transformerToHandler(null, ZeroCodeStdoutTransformer), options = options).getOr(message("python.cannot.exec", python.userReadableName)) { return@withContext it }.trim() @@ -31,16 +34,28 @@ internal suspend fun ExecService.validatePythonAndGetVersionImpl(python: Executa return@withContext PyResult.localizedError(message("python.get.version.error", python.userReadableName, smokeTestOutput)) } - val versionOutput: EelProcessExecutionResult = executePythonAdvanced(python, options = options, args = Args(PYTHON_VERSION_ARG), processInteractiveHandler = transformerToHandler(null, { r -> + val versionOutput: EelProcessExecutionResult = executePythonAdvanced(python, options = options, args = Args(PYTHON_VERSION_ARG), processInteractiveHandler = transformerToHandler(null) { r -> if (r.exitCode == 0) Result.success(r) else Result.failure(message("python.get.version.error", python.userReadableName, r.exitCode)) - })).getOr { return@withContext it } + }).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 PyResult.localizedError(message("python.get.version.wrong.version", python.userReadableName, versionString)) } - return@withContext Result.success(languageLevel) + + val freeThreaded = if (languageLevel.isAtLeast(LanguageLevel.PYTHON313)) { + val gilEnabledOutput = executePythonAdvanced( + python, + Args("-c", "import sys; print(sys._is_gil_enabled())"), + processInteractiveHandler = transformerToHandler(null, ZeroCodeStdoutTransformer), + options = options + ).onFailure { fileLogger().warn(it.toString()) }.successOrNull?.trim() + gilEnabledOutput == "False" + } + else false + + return@withContext Result.success(PythonInfo(languageLevel, freeThreaded)) } private val ExecutablePython.userReadableName: @NlsSafe String diff --git a/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/HelpersShowCaseTest.kt b/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/HelpersShowCaseTest.kt index b97908b8a6f7..ebdf4d253f6a 100644 --- a/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/HelpersShowCaseTest.kt +++ b/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/HelpersShowCaseTest.kt @@ -7,7 +7,7 @@ import com.intellij.platform.testFramework.junit5.eel.params.api.* import com.intellij.python.community.execService.ExecService import com.intellij.python.community.execService.asBinToExec import com.intellij.python.community.execService.python.executeHelper -import com.intellij.python.community.execService.python.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.validatePythonAndGetInfo import com.intellij.python.community.helpersLocator.PythonHelpersLocator import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase import com.intellij.python.junit5Tests.framework.env.PythonBinaryPath @@ -53,7 +53,7 @@ class HelpersShowCaseTest() { val output = ExecService().executeHelper(python.asBinToExec(), helper.name, listOf("--version")).orThrow().trim() Assertions.assertEquals(hello, output, "wrong helper output") - val langLevel = ExecService().validatePythonAndGetVersion(python).getOrThrow() + val langLevel = ExecService().validatePythonAndGetInfo(python).getOrThrow().languageLevel Assertions.assertTrue(langLevel.isPy3K, "Wrong lang level:$langLevel") } finally { diff --git a/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/PythonBinaryValidationTest.kt b/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/PythonBinaryValidationTest.kt index 3b0307f940e6..12b5d5b92c9c 100644 --- a/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/PythonBinaryValidationTest.kt +++ b/python/python-exec-service/execService.python/tests/com/intellij/python/junit5Tests/env/PythonBinaryValidationTest.kt @@ -1,7 +1,7 @@ // 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 -import com.intellij.python.community.execService.python.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.validatePythonAndGetInfo import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase import com.intellij.python.junit5Tests.framework.env.PythonBinaryPath import com.intellij.python.junit5Tests.randomBinary @@ -15,13 +15,13 @@ import org.junit.jupiter.api.Test class PythonBinaryValidationTest { @Test fun sunnyDayTest(@PythonBinaryPath python: PythonBinary): Unit = runBlocking { - val level = python.validatePythonAndGetVersion().orThrow() - Assertions.assertNotNull(level, "Failed to get python level") + val info = python.validatePythonAndGetInfo().orThrow() + Assertions.assertNotNull(info, "Failed to get python info") } @Test fun rainyDayTest(): Unit = runBlocking { - when (val r = randomBinary.validatePythonAndGetVersion()) { + when (val r = randomBinary.validatePythonAndGetInfo()) { is Result.Success -> { Assertions.fail("${randomBinary} isn't a python, should fail, but got ${r.result}") } diff --git a/python/python-features-trainer/testSrc/com/intellij/python/junit5Tests/env/PythonLangSupportTest.kt b/python/python-features-trainer/testSrc/com/intellij/python/junit5Tests/env/PythonLangSupportTest.kt index c6790d3c3350..22608dcba263 100644 --- a/python/python-features-trainer/testSrc/com/intellij/python/junit5Tests/env/PythonLangSupportTest.kt +++ b/python/python-features-trainer/testSrc/com/intellij/python/junit5Tests/env/PythonLangSupportTest.kt @@ -6,7 +6,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectManager import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.util.SystemInfoRt -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl import com.intellij.python.community.services.systemPython.SystemPythonService import com.intellij.python.community.services.systemPython.createVenvFromSystemPython import com.intellij.python.featuresTrainer.ift.PythonLangSupport @@ -86,7 +86,7 @@ class PythonLangSupportTest { val sdk = project.pythonSdk!! try { val pythonBinary = Path.of(sdk.homePath!!) - Assertions.assertTrue(VanillaPythonWithLanguageLevelImpl.createByPythonBinary(pythonBinary).orThrow().languageLevel.isPy3K, "Sdk is broken") + Assertions.assertTrue(VanillaPythonWithPythonInfoImpl.createByPythonBinary(pythonBinary).orThrow().pythonInfo.languageLevel.isPy3K, "Sdk is broken") } finally { writeAction { diff --git a/python/python-venv/src/com/intellij/python/community/impl/venv/venv.kt b/python/python-venv/src/com/intellij/python/community/impl/venv/venv.kt index b36d1fe44029..4af59d829730 100644 --- a/python/python-venv/src/com/intellij/python/community/impl/venv/venv.kt +++ b/python/python-venv/src/com/intellij/python/community/impl/venv/venv.kt @@ -3,10 +3,13 @@ package com.intellij.python.community.impl.venv import com.intellij.openapi.application.EDT import com.intellij.openapi.diagnostic.fileLogger -import com.intellij.python.community.execService.* +import com.intellij.python.community.execService.BinaryToExec +import com.intellij.python.community.execService.ExecOptions +import com.intellij.python.community.execService.ExecService +import com.intellij.python.community.execService.asBinToExec import com.intellij.python.community.execService.python.HelperName import com.intellij.python.community.execService.python.executeHelper -import com.intellij.python.community.execService.python.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.validatePythonAndGetInfo import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult @@ -66,7 +69,7 @@ suspend fun createVenv( } add(venvDir) } - val version = python.validatePythonAndGetVersion().getOr(PyVenvBundle.message("py.venv.error.cant.base.version")) { return it } + val version = python.validatePythonAndGetInfo().getOr(PyVenvBundle.message("py.venv.error.cant.base.version")) { return it }.languageLevel val helper = if (version.isAtLeast(LanguageLevel.PYTHON38)) VIRTUALENV_ZIPAPP_NAME else LEGACY_VIRTUALENV_ZIPAPP_NAME execService.executeHelper(python, helper, args, ExecOptions(timeout = 3.minutes)) .getOr(PyVenvBundle.message("py.venv.error.executing.script", helper)) { return it } diff --git a/python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithLanguageLevelImpl.kt b/python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithPythonInfoImpl.kt similarity index 71% rename from python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithLanguageLevelImpl.kt rename to python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithPythonInfoImpl.kt index 9a6ae733c881..83cac36f6e2b 100644 --- a/python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithLanguageLevelImpl.kt +++ b/python/services/internal-impl/src/com/intellij/python/community/services/internal/impl/VanillaPythonWithPythonInfoImpl.kt @@ -4,16 +4,16 @@ package com.intellij.python.community.services.internal.impl import com.intellij.platform.eel.EelPlatform import com.intellij.platform.eel.provider.asNioPath import com.intellij.platform.eel.provider.getEelDescriptor -import com.intellij.python.community.execService.python.validatePythonAndGetVersion -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl.Companion.concurrentLimit -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl.Companion.createByPythonBinary -import com.intellij.python.community.services.shared.LanguageLevelComparator -import com.intellij.python.community.services.shared.PythonWithLanguageLevel -import com.intellij.python.community.services.shared.VanillaPythonWithLanguageLevel +import com.intellij.python.community.execService.python.validatePythonAndGetInfo +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl.Companion.concurrentLimit +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl.Companion.createByPythonBinary +import com.intellij.python.community.services.shared.PythonInfoComparator +import com.intellij.python.community.services.shared.PythonWithPythonInfo +import com.intellij.python.community.services.shared.VanillaPythonWithPythonInfo import com.jetbrains.python.PythonBinary +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult -import com.jetbrains.python.psi.LanguageLevel import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -25,10 +25,10 @@ import kotlin.io.path.pathString import kotlin.io.path.relativeTo @Internal -class VanillaPythonWithLanguageLevelImpl internal constructor( +class VanillaPythonWithPythonInfoImpl internal constructor( override val pythonBinary: PythonBinary, - override val languageLevel: LanguageLevel, -) : VanillaPythonWithLanguageLevel, Comparable { + override val pythonInfo: PythonInfo, +) : VanillaPythonWithPythonInfo, Comparable { companion object { @@ -38,7 +38,7 @@ class VanillaPythonWithLanguageLevelImpl internal constructor( * Like [createByPythonBinary] but runs in parallel up to [concurrentLimit] * @return python path -> python with language level sorted from highest to lowest. */ - suspend fun createByPythonBinaries(pythonBinaries: Collection): Collection>> = + suspend fun createByPythonBinaries(pythonBinaries: Collection): Collection>> = coroutineScope { pythonBinaries.map { async { @@ -49,9 +49,9 @@ class VanillaPythonWithLanguageLevelImpl internal constructor( }.awaitAll() }.sortedBy { it.first } - suspend fun createByPythonBinary(pythonBinary: PythonBinary): PyResult { - val languageLevel = pythonBinary.validatePythonAndGetVersion().getOr { return it } - return Result.success(VanillaPythonWithLanguageLevelImpl(pythonBinary, languageLevel)) + suspend fun createByPythonBinary(pythonBinary: PythonBinary): PyResult { + val pythonInfo = pythonBinary.validatePythonAndGetInfo().getOr { return it } + return Result.success(VanillaPythonWithPythonInfoImpl(pythonBinary, pythonInfo)) } } @@ -59,7 +59,7 @@ class VanillaPythonWithLanguageLevelImpl internal constructor( if (this === other) return true if (javaClass != other?.javaClass) return false - other as VanillaPythonWithLanguageLevelImpl + other as VanillaPythonWithPythonInfoImpl return pythonBinary == other.pythonBinary } @@ -69,7 +69,7 @@ class VanillaPythonWithLanguageLevelImpl internal constructor( } override fun toString(): String { - return "$pythonBinary ($languageLevel)" + return "$pythonBinary ($pythonInfo)" } override suspend fun getReadableName(): @Nls String { @@ -81,8 +81,8 @@ class VanillaPythonWithLanguageLevelImpl internal constructor( } val pythonString = (if (pythonBinary.startsWith(home)) "~$separator" + pythonBinary.relativeTo(home).pathString else pythonBinary.pathString) - return "$pythonString ($languageLevel)" + return "$pythonString ($pythonInfo)" } - override fun compareTo(other: PythonWithLanguageLevel): Int = LanguageLevelComparator.compare(this, other) + override fun compareTo(other: PythonWithPythonInfo): Int = PythonInfoComparator.compare(this, other) } \ No newline at end of file diff --git a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/env/services/internal/impl/PythonWithLanguageLevelImplTest.kt b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/env/services/internal/impl/PythonWithLanguageLevelImplTest.kt index 214be131586c..b35e775ade11 100644 --- a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/env/services/internal/impl/PythonWithLanguageLevelImplTest.kt +++ b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/env/services/internal/impl/PythonWithLanguageLevelImplTest.kt @@ -1,7 +1,7 @@ // 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.services.internal.impl -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase import com.intellij.python.junit5Tests.framework.env.PythonBinaryPath import com.intellij.python.junit5Tests.randomBinary @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test class PythonWithLanguageLevelImplTest { @Test fun testRainyDay(): Unit = runBlocking { - when (val r = VanillaPythonWithLanguageLevelImpl.createByPythonBinary(randomBinary)) { + when (val r = VanillaPythonWithPythonInfoImpl.createByPythonBinary(randomBinary)) { is Result.Failure -> Unit is Result.Success -> fail("Unexpected success ${r.result}") } @@ -23,8 +23,8 @@ class PythonWithLanguageLevelImplTest { @Test fun testSunnyDay(@PythonBinaryPath pythonBinary: PythonBinary): Unit = runBlocking { - val python = VanillaPythonWithLanguageLevelImpl.createByPythonBinary(pythonBinary).orThrow() + val python = VanillaPythonWithPythonInfoImpl.createByPythonBinary(pythonBinary).orThrow() assertEquals(pythonBinary, python.pythonBinary, "Wrong python binary") - assertTrue(python.languageLevel.isPy3K, "Wrong python version") + assertTrue(python.pythonInfo.languageLevel.isPy3K, "Wrong python version") } } \ No newline at end of file diff --git a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/CompareByLanguageLevelTest.kt b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/CompareByLanguageLevelTest.kt index b14394da8b38..4cd0f0661295 100644 --- a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/CompareByLanguageLevelTest.kt +++ b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/CompareByLanguageLevelTest.kt @@ -1,8 +1,9 @@ // 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.unit -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl import com.intellij.testFramework.junit5.TestApplication +import com.jetbrains.python.PythonInfo import com.jetbrains.python.psi.LanguageLevel import org.junit.jupiter.api.Assertions.assertArrayEquals import org.junit.jupiter.api.Test @@ -14,12 +15,12 @@ class CompareByLanguageLevelTest { @Test fun testCompareByLanguageLevel(@TempDir path: Path) { val list = listOf( - VanillaPythonWithLanguageLevelImpl(path, LanguageLevel.PYTHON38), - VanillaPythonWithLanguageLevelImpl(path, LanguageLevel.PYTHON312), - VanillaPythonWithLanguageLevelImpl(path, LanguageLevel.PYTHON311), + VanillaPythonWithPythonInfoImpl(path, PythonInfo(LanguageLevel.PYTHON38)), + VanillaPythonWithPythonInfoImpl(path, PythonInfo(LanguageLevel.PYTHON312)), + VanillaPythonWithPythonInfoImpl(path, PythonInfo(LanguageLevel.PYTHON311)), ) - val sortedLevels = list.sorted().map { it.languageLevel }.toTypedArray() + val sortedLevels = list.sorted().map { it.pythonInfo.languageLevel }.toTypedArray() assertArrayEquals(arrayOf(LanguageLevel.PYTHON312, LanguageLevel.PYTHON311, LanguageLevel.PYTHON38), sortedLevels, "Highest python goes first") } diff --git a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/alsoWin/services/internal/impl/ReadableNameTest.kt b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/alsoWin/services/internal/impl/ReadableNameTest.kt index 24e40b0f69b5..c5814d0cfc83 100644 --- a/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/alsoWin/services/internal/impl/ReadableNameTest.kt +++ b/python/services/internal-impl/tests/com/intellij/python/junit5Tests/unit/alsoWin/services/internal/impl/ReadableNameTest.kt @@ -1,8 +1,9 @@ // 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.unit.alsoWin.services.internal.impl -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl import com.intellij.testFramework.junit5.TestApplication +import com.jetbrains.python.PythonInfo import com.jetbrains.python.psi.LanguageLevel import kotlinx.coroutines.runBlocking import org.hamcrest.CoreMatchers.allOf @@ -23,7 +24,7 @@ class ReadableNameTest { @Test fun testNoHomePath(@TempDir path: Path): Unit = runBlocking { val fakePython = path.resolve(PYTHON_FILE_NAME) - val name = VanillaPythonWithLanguageLevelImpl(fakePython, LanguageLevel.PYTHON312).getReadableName() + val name = VanillaPythonWithPythonInfoImpl(fakePython, PythonInfo(LanguageLevel.PYTHON312)).getReadableName() assertThat("Wrong name generated", name, allOf(containsString("3.12"), containsString(fakePython.pathString))) } @@ -33,11 +34,11 @@ class ReadableNameTest { var fakePython = home.resolve(PYTHON_FILE_NAME) - var name = VanillaPythonWithLanguageLevelImpl(fakePython, LanguageLevel.PYTHON312).getReadableName() + var name = VanillaPythonWithPythonInfoImpl(fakePython, PythonInfo(LanguageLevel.PYTHON312)).getReadableName() assertThat("Wrong name generated", name, allOf(containsString("3.12"), matchesPattern(".*~[\\\\/]$PYTHON_FILE_NAME.*"))) fakePython = home.resolve("deep").resolve(PYTHON_FILE_NAME) - name = VanillaPythonWithLanguageLevelImpl(fakePython, LanguageLevel.PYTHON312).getReadableName() + name = VanillaPythonWithPythonInfoImpl(fakePython, PythonInfo(LanguageLevel.PYTHON312)).getReadableName() assertThat("Wrong name generated", name, allOf(containsString("3.12"), matchesPattern(".*~[\\\\/]deep[\\\\/]$PYTHON_FILE_NAME.*"))) } } \ No newline at end of file diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/LanguageLevelHolder.kt b/python/services/shared/src/com/intellij/python/community/services/shared/PythonInfoHolder.kt similarity index 56% rename from python/services/shared/src/com/intellij/python/community/services/shared/LanguageLevelHolder.kt rename to python/services/shared/src/com/intellij/python/community/services/shared/PythonInfoHolder.kt index 0fde57d45e88..0145c2bc7a6a 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/LanguageLevelHolder.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/PythonInfoHolder.kt @@ -1,11 +1,11 @@ // 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.community.services.shared -import com.jetbrains.python.psi.LanguageLevel +import com.jetbrains.python.PythonInfo /** - * Something with language level + * Something with python info */ -interface LanguageLevelHolder { - val languageLevel: LanguageLevel +interface PythonInfoHolder { + val pythonInfo: PythonInfo } \ No newline at end of file diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithLanguageLevel.kt b/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithPythonInfo.kt similarity index 75% rename from python/services/shared/src/com/intellij/python/community/services/shared/PythonWithLanguageLevel.kt rename to python/services/shared/src/com/intellij/python/community/services/shared/PythonWithPythonInfo.kt index f2f16dcdaf3c..294065de3a63 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithLanguageLevel.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithPythonInfo.kt @@ -4,9 +4,9 @@ package com.intellij.python.community.services.shared import com.intellij.python.community.execService.python.advancedApi.ExecutablePython /** - * Python (vanilla, conda, whatever) with known language level. + * Python (vanilla, conda, whatever) with known python info. */ -interface PythonWithLanguageLevel : PythonWithName, LanguageLevelHolder { +interface PythonWithPythonInfo : PythonWithName, PythonInfoHolder { /** * Convert python to something that can be executed on [java.util.concurrent.ExecutorService] diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithUi.kt b/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithUi.kt index dbc0f8831a49..74d38ba1b0fc 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithUi.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/PythonWithUi.kt @@ -2,6 +2,6 @@ package com.intellij.python.community.services.shared /** - * Python that has both [languageLevel] and [ui] + * Python that has both [pythonInfo] and [ui] */ -interface PythonWithUi : PythonWithLanguageLevel, UiHolder \ No newline at end of file +interface PythonWithUi : PythonWithPythonInfo, UiHolder \ No newline at end of file diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithLanguageLevel.kt b/python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithPythonInfo.kt similarity index 87% rename from python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithLanguageLevel.kt rename to python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithPythonInfo.kt index da0760823278..6f58b0d67a59 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithLanguageLevel.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/VanillaPythonWithPythonInfo.kt @@ -8,7 +8,7 @@ import com.jetbrains.python.PythonBinary /** * Vanilla (not conda) has [pythonBinary] */ -interface VanillaPythonWithLanguageLevel : PythonWithLanguageLevel { +interface VanillaPythonWithPythonInfo : PythonWithPythonInfo { val pythonBinary: PythonBinary override val asExecutablePython: ExecutablePython get() = ExecutablePython.vanillaExecutablePython(pythonBinary) diff --git a/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt b/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt index 77689d77306a..aef5e105e1bc 100644 --- a/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt +++ b/python/services/shared/src/com/intellij/python/community/services/shared/comparators.kt @@ -3,22 +3,11 @@ package com.intellij.python.community.services.shared import com.intellij.openapi.diagnostic.fileLogger import com.jetbrains.python.PyToolUIInfo -import com.jetbrains.python.psi.LanguageLevel import java.util.* private val logger = fileLogger() -object LanguageLevelComparator : Comparator { - override fun compare(o1: LanguageLevelHolder, o2: LanguageLevelHolder): Int { - // Backward: first python is the highest - if (logger.isDebugEnabled) { - logger.debug("langLevel ${o1.languageLevel} vs ${o2.languageLevel}") - } - return LanguageLevel.VERSION_COMPARATOR.compare(o1.languageLevel, o2.languageLevel) * -1 - } -} - object UiComparator : Comparator { override fun compare(o1: UiHolder, o2: UiHolder): Int { if (logger.isDebugEnabled) { @@ -28,13 +17,24 @@ object UiComparator : Comparator { } } -class LanguageLevelWithUiComparator : Comparator where T : LanguageLevelHolder, T : UiHolder { +object PythonInfoComparator : Comparator { + override fun compare(o1: PythonInfoHolder, o2: PythonInfoHolder): Int { + // Backward: first python is the highest + if (logger.isDebugEnabled) { + logger.debug("pythonInfo ${o1.pythonInfo} vs ${o2.pythonInfo}") + } + return o1.pythonInfo.compareTo(o2.pythonInfo) + } +} + +class PythonInfoWithUiComparator : Comparator where T : PythonInfoHolder, T : UiHolder { override fun compare(o1: T, o2: T): Int { if (logger.isDebugEnabled) { logger.debug("full ${o1.string()} vs ${o2.string()}") } - return LanguageLevelComparator.compare(o1, o2) * 10 + UiComparator.compare(o1, o2) + return PythonInfoComparator.compare(o1, o2) * 10 + UiComparator.compare(o1, o2) } } -private fun T.string(): String where T : LanguageLevelHolder, T : UiHolder = "($languageLevel,${ui?.toolName})" +private fun T.string(): String where T : PythonInfoHolder, T : UiHolder = + "(${pythonInfo.languageLevel},${ui?.toolName},free-threaded:${pythonInfo.freeThreaded})" diff --git a/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt b/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt index 4d9a8a38236c..28b34ea4d83f 100644 --- a/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt +++ b/python/services/shared/tests/com/intellij/python/junit5Tests/unit/comparators/ComparatorsTest.kt @@ -1,10 +1,11 @@ // 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.unit.comparators -import com.intellij.python.community.services.shared.LanguageLevelHolder -import com.intellij.python.community.services.shared.LanguageLevelWithUiComparator -import com.jetbrains.python.PyToolUIInfo +import com.intellij.python.community.services.shared.PythonInfoHolder +import com.intellij.python.community.services.shared.PythonInfoWithUiComparator import com.intellij.python.community.services.shared.UiHolder +import com.jetbrains.python.PyToolUIInfo +import com.jetbrains.python.PythonInfo import com.jetbrains.python.psi.LanguageLevel import org.hamcrest.MatcherAssert import org.hamcrest.Matchers @@ -16,29 +17,33 @@ class ComparatorsTest { @Test fun testComparators() { val mocks = arrayOf( - MockLevel(LanguageLevel.PYTHON314), - MockLevel(LanguageLevel.PYTHON310), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("A")), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("Z")), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("B")), - MockLevel(LanguageLevel.PYTHON27), - MockLevel(LanguageLevel.PYTHON313), + MockInfo(PythonInfo(LanguageLevel.PYTHON314)), + MockInfo(PythonInfo(LanguageLevel.PYTHON314, true)), + MockInfo(PythonInfo(LanguageLevel.PYTHON310)), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("A")), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("Z")), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("B")), + MockInfo(PythonInfo(LanguageLevel.PYTHON27)), + MockInfo(PythonInfo(LanguageLevel.PYTHON313, true)), + MockInfo(PythonInfo(LanguageLevel.PYTHON313)), ) - val set = TreeSet(LanguageLevelWithUiComparator()) + val set = TreeSet(PythonInfoWithUiComparator()) set.addAll(mocks) MatcherAssert.assertThat("", set, Matchers.contains( - MockLevel(LanguageLevel.PYTHON314), - MockLevel(LanguageLevel.PYTHON313), - MockLevel(LanguageLevel.PYTHON310), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("A")), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("B")), - MockLevel(LanguageLevel.PYTHON310, ui = PyToolUIInfo("Z")), - MockLevel(LanguageLevel.PYTHON27) + MockInfo(PythonInfo(LanguageLevel.PYTHON314)), + MockInfo(PythonInfo(LanguageLevel.PYTHON314, true)), + MockInfo(PythonInfo(LanguageLevel.PYTHON313)), + MockInfo(PythonInfo(LanguageLevel.PYTHON313, true)), + MockInfo(PythonInfo(LanguageLevel.PYTHON310)), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("A")), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("B")), + MockInfo(PythonInfo(LanguageLevel.PYTHON310), ui = PyToolUIInfo("Z")), + MockInfo(PythonInfo(LanguageLevel.PYTHON27)) )) } } -private data class MockLevel( - override val languageLevel: LanguageLevel, +private data class MockInfo( + override val pythonInfo: PythonInfo, override val ui: PyToolUIInfo? = null, -) : LanguageLevelHolder, UiHolder \ No newline at end of file +) : PythonInfoHolder, UiHolder diff --git a/python/services/system-python/src/com/intellij/python/community/services/systemPython/api.kt b/python/services/system-python/src/com/intellij/python/community/services/systemPython/api.kt index c7dcd93ecb63..4b02c2bf71e3 100644 --- a/python/services/system-python/src/com/intellij/python/community/services/systemPython/api.kt +++ b/python/services/system-python/src/com/intellij/python/community/services/systemPython/api.kt @@ -6,9 +6,9 @@ import com.intellij.openapi.components.service import com.intellij.platform.eel.EelApi import com.intellij.platform.eel.provider.localEel import com.intellij.python.community.impl.venv.createVenv -import com.intellij.python.community.services.shared.LanguageLevelWithUiComparator +import com.intellij.python.community.services.shared.PythonInfoWithUiComparator import com.intellij.python.community.services.shared.PythonWithUi -import com.intellij.python.community.services.shared.VanillaPythonWithLanguageLevel +import com.intellij.python.community.services.shared.VanillaPythonWithPythonInfo import com.jetbrains.python.PyToolUIInfo import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result @@ -56,10 +56,10 @@ fun SystemPythonService(): SystemPythonService = ApplicationManager.getApplicati * * Instances could be obtained with [SystemPythonService] */ -class SystemPython internal constructor(private val delegate: VanillaPythonWithLanguageLevel, override val ui: PyToolUIInfo?) : VanillaPythonWithLanguageLevel by delegate, PythonWithUi, Comparable { +class SystemPython internal constructor(private val delegate: VanillaPythonWithPythonInfo, override val ui: PyToolUIInfo?) : VanillaPythonWithPythonInfo by delegate, PythonWithUi, Comparable { private companion object { - val comparator = LanguageLevelWithUiComparator() + val comparator = PythonInfoWithUiComparator() } override fun equals(other: Any?): Boolean { diff --git a/python/services/system-python/src/com/intellij/python/community/services/systemPython/systemPythonServiceImpl.kt b/python/services/system-python/src/com/intellij/python/community/services/systemPython/systemPythonServiceImpl.kt index 6ecbdb1fdd53..e485257fe61d 100644 --- a/python/services/system-python/src/com/intellij/python/community/services/systemPython/systemPythonServiceImpl.kt +++ b/python/services/system-python/src/com/intellij/python/community/services/systemPython/systemPythonServiceImpl.kt @@ -11,12 +11,12 @@ import com.intellij.platform.eel.EelDescriptor import com.intellij.platform.eel.provider.getEelDescriptor import com.intellij.platform.eel.provider.localEel import com.intellij.python.community.impl.installer.PySdkToInstallManager -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl -import com.jetbrains.python.PyToolUIInfo +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl import com.intellij.python.community.services.systemPython.SystemPythonServiceImpl.MyServiceState import com.intellij.python.community.services.systemPython.impl.Cache import com.intellij.python.community.services.systemPython.impl.PySystemPythonBundle import com.jetbrains.python.NON_INTERACTIVE_ROOT_TRACE_CONTEXT +import com.jetbrains.python.PyToolUIInfo import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult @@ -66,7 +66,7 @@ internal class SystemPythonServiceImpl(scope: CoroutineScope) : SystemPythonServ } override suspend fun registerSystemPython(pythonPath: PythonBinary): PyResult { - val pythonWithLangLevel = VanillaPythonWithLanguageLevelImpl.createByPythonBinary(pythonPath) + val pythonWithLangLevel = VanillaPythonWithPythonInfoImpl.createByPythonBinary(pythonPath) .getOr(PySystemPythonBundle.message("py.system.python.service.python.is.broken", pythonPath)) { return it } val systemPython = SystemPython(pythonWithLangLevel, null) state.userProvidedPythons.add(pythonPath.pathString) @@ -124,7 +124,7 @@ internal class SystemPythonServiceImpl(scope: CoroutineScope) : SystemPythonServ val badPythons = mutableSetOf() val pythons = pythonsFromExtensions + state.userProvidedPythonsAsPath.filter { it.getEelDescriptor() == eelApi.descriptor } - val result = VanillaPythonWithLanguageLevelImpl.createByPythonBinaries(pythons.toSet()) + val result = VanillaPythonWithPythonInfoImpl.createByPythonBinaries(pythons.toSet()) .mapNotNull { (python, r) -> when (r) { is Result.Success -> SystemPython(r.result, pythonsUi[r.result.pythonBinary]) @@ -159,4 +159,4 @@ private object LocalPythonInstaller : PythonInstallerService { } return Result.Companion.success(Unit) } -} \ No newline at end of file +} diff --git a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt index f0572fb0e3db..a2546291f72d 100644 --- a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt +++ b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/Py27Test.kt @@ -13,7 +13,8 @@ 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") + val python27 = testEnvironments.firstOrNull { it.pythonInfo.languageLevel == LanguageLevel.PYTHON27 } + ?: error("No 2.7 found in $testEnvironments") SystemPythonService().registerSystemPython(python27.pythonBinary).getOrThrow() } } \ No newline at end of file diff --git a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/SystemPythonServiceShowCaseTest.kt b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/SystemPythonServiceShowCaseTest.kt index 967c18dc5904..507e98163bbe 100644 --- a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/SystemPythonServiceShowCaseTest.kt +++ b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/SystemPythonServiceShowCaseTest.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.diagnostic.fileLogger import com.intellij.openapi.util.SystemInfo +import com.intellij.platform.eel.EelApi import com.intellij.platform.eel.ExecuteProcessException import com.intellij.platform.eel.ThrowsChecked import com.intellij.platform.eel.provider.getEelDescriptor @@ -23,8 +24,11 @@ import com.intellij.testFramework.common.timeoutRunBlocking import com.intellij.testFramework.junit5.RegistryKey import com.intellij.testFramework.junit5.TestDisposable import com.intellij.testFramework.registerExtension +import com.jetbrains.python.PyToolUIInfo +import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.MessageError +import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.getOrThrow import com.jetbrains.python.sdk.flavors.PythonSdkFlavor import com.jetbrains.python.venvReader.VirtualEnvReader @@ -35,10 +39,6 @@ import org.hamcrest.Matchers.not import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -import com.intellij.platform.eel.EelApi -import com.jetbrains.python.PyToolUIInfo -import com.jetbrains.python.PythonBinary -import com.jetbrains.python.errorProcessing.PyResult import java.nio.file.Path import kotlin.io.path.deleteExisting import kotlin.io.path.pathString @@ -55,11 +55,11 @@ class SystemPythonServiceShowCaseTest { val eelApi = systemPython.pythonBinary.getEelDescriptor().toEelApi() val process = eelApi.exec.spawnProcess(systemPython.pythonBinary.pathString, "--version").eelIt() val output = async { - (if (systemPython.languageLevel.isPy3K) process.stdout else process.stderr).readWholeText() + (if (systemPython.pythonInfo.languageLevel.isPy3K) process.stdout else process.stderr).readWholeText() } Assertions.assertTrue(process.exitCode.await() == 0) val versionString = PythonSdkFlavor.getLanguageLevelFromVersionStringStaticSafe(output.await())!! - Assertions.assertEquals(systemPython.languageLevel, versionString, "Wrong version") + Assertions.assertEquals(systemPython.pythonInfo.languageLevel, versionString, "Wrong version") } } diff --git a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/impl/EnvProviderTest.kt b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/impl/EnvProviderTest.kt index cdb90fa415b4..3a43b4a053b2 100644 --- a/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/impl/EnvProviderTest.kt +++ b/python/services/system-python/tests/com/intellij/python/junit5Tests/env/systemPython/impl/EnvProviderTest.kt @@ -3,8 +3,8 @@ package com.intellij.python.junit5Tests.env.systemPython.impl import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager +import com.intellij.platform.eel.EelApi import com.intellij.python.community.impl.venv.createVenv -import com.jetbrains.python.PyToolUIInfo import com.intellij.python.community.services.systemPython.SystemPythonProvider import com.intellij.python.community.services.systemPython.SystemPythonService import com.intellij.python.junit5Tests.framework.env.PyEnvTestCase @@ -12,6 +12,7 @@ import com.intellij.python.junit5Tests.framework.env.PythonBinaryPath import com.intellij.testFramework.common.timeoutRunBlocking import com.intellij.testFramework.junit5.TestDisposable import com.intellij.testFramework.registerExtension +import com.jetbrains.python.PyToolUIInfo import com.jetbrains.python.PythonBinary import com.jetbrains.python.Result import com.jetbrains.python.getOrThrow @@ -20,7 +21,6 @@ import org.hamcrest.Matchers import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -import com.intellij.platform.eel.EelApi import java.nio.file.Path @PyEnvTestCase @@ -35,7 +35,7 @@ class EnvProviderTest { if (systemPythons.size > 1) { val best = systemPythons.first() for (python in systemPythons.subList(1, systemPythonBinaries.size)) { - assertTrue(python.languageLevel <= best.languageLevel, "$best is the first, bust worse than $python") + assertTrue(python.pythonInfo.languageLevel <= best.pythonInfo.languageLevel, "$best is the first, bust worse than $python") } } } @@ -56,7 +56,7 @@ class EnvProviderTest { private class InlineTestProvider( private val pythons: Set, - override val uiCustomization: PyToolUIInfo? + override val uiCustomization: PyToolUIInfo?, ) : SystemPythonProvider { override suspend fun findSystemPythons(eelApi: EelApi) = Result.success(pythons) } diff --git a/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java b/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java index d09f40e93bfd..4ca3003a4db3 100644 --- a/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyInterpreterInspection.java @@ -161,6 +161,7 @@ public final class PyInterpreterInspection extends PyInspection { } } + // TODO: We should use SystemPythonService here as well, postponing as it's quite unlikely we get here (although we can) final var systemWideSdk = PySdkExtKt.mostPreferred(PySdkExtKt.filterSystemWideSdks(existingSdks)); if (systemWideSdk != null) { return new UseExistingInterpreterFix(systemWideSdk, module); diff --git a/python/src/com/jetbrains/python/poetry/PoetryFilesUtils.kt b/python/src/com/jetbrains/python/poetry/PoetryFilesUtils.kt index 0e491bf00eef..34518599a47f 100644 --- a/python/src/com/jetbrains/python/poetry/PoetryFilesUtils.kt +++ b/python/src/com/jetbrains/python/poetry/PoetryFilesUtils.kt @@ -95,9 +95,9 @@ class PoetryPyProjectTomlPythonVersionsService : Disposable { fun validateSdkVersions(moduleFile: VirtualFile, sdks: List): List = sdks.filter { getVersion(moduleFile).isValid(it.versionString) } - fun validateInterpretersVersions(moduleFile: VirtualFile, interpreters: Flow>?>): Flow>?> { + fun

validateInterpretersVersions(moduleFile: VirtualFile, interpreters: Flow>?>): Flow>?> { val version = getVersion(moduleFile) - return interpreters.map { list -> list?.filter { version.isValid(it.languageLevel) } } + return interpreters.map { list -> list?.filter { version.isValid(it.pythonInfo.languageLevel) } } } private fun getVersion(moduleFile: VirtualFile): PoetryPythonVersion = diff --git a/python/src/com/jetbrains/python/poetry/sdk/evolution/PoetrySelectSdkProvider.kt b/python/src/com/jetbrains/python/poetry/sdk/evolution/PoetrySelectSdkProvider.kt index 03a01af6c30a..7235438c969d 100644 --- a/python/src/com/jetbrains/python/poetry/sdk/evolution/PoetrySelectSdkProvider.kt +++ b/python/src/com/jetbrains/python/poetry/sdk/evolution/PoetrySelectSdkProvider.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.ex.ActionUtil import com.intellij.openapi.ui.popup.ListSeparator import com.intellij.openapi.vfs.readText import com.intellij.python.community.services.systemPython.SystemPythonService +import com.intellij.python.pyproject.PyProjectToml import com.intellij.python.sdk.ui.evolution.sdk.EvoModuleSdk import com.intellij.python.sdk.ui.evolution.sdk.resolvePythonExecutable import com.intellij.python.sdk.ui.evolution.tool.pip.sdk.getPythonVersion @@ -15,7 +16,6 @@ import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeElement import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeLazyNodeElement import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeLeafElement import com.intellij.python.sdk.ui.evolution.ui.components.EvoTreeSection -import com.intellij.python.pyproject.PyProjectToml import com.jetbrains.python.PyBundle import com.jetbrains.python.PythonHomePath import com.jetbrains.python.Result @@ -33,7 +33,7 @@ import kotlin.io.path.name private class PoetrySelectSdkProvider() : EvoSelectSdkProvider { override fun getTreeElement(evoModuleSdk: EvoModuleSdk): EvoTreeElement = EvoTreeLazyNodeElement("Poetry", PythonIcons.Python.Origami) { - val poetryExecutable = getPoetryExecutable().getOr { + getPoetryExecutable().getOr { return@EvoTreeLazyNodeElement it } @@ -65,7 +65,7 @@ private class PoetrySelectSdkProvider() : EvoSelectSdkProvider { EvoTreeSection(ListSeparator(label), leafs) } - val systemPythons = SystemPythonService().findSystemPythons().groupBy { it.languageLevel }.keys.sortedDescending() + val systemPythons = SystemPythonService().findSystemPythons().groupBy { it.pythonInfo.languageLevel }.keys.sortedDescending() val prefix = specials?.firstOrNull()?.name?.substringBeforeLast("-") ?: "$projectName-sha256" val specialSection = EvoTreeSection( label = ListSeparator("$poetryVirtualenvsPath/$prefix"), @@ -84,20 +84,6 @@ private class PoetrySelectSdkProvider() : EvoSelectSdkProvider { Result.success(sections) } - //override fun getTreeElement(evoModuleSdk: EvoModuleSdk): EvoTreeElement { - // val path = evoModuleSdk.module.which("poetry") ?: return null - // return null - // val header = object : AnAction("Base: $path", null, PythonIcons.Python.Origami) { - // override fun actionPerformed(e: AnActionEvent) = Unit - // //} - // // - // //val evoLazyActionGroup = EvoLazyActionGroup( - // // ExternalToolActionGroup("Poetry", "Poetry", EvolutionIcons.Tools.Poetry) - // //) { - // // Result.success(listOf(header)) - // //} - // //return evoLazyActionGroup - //} } private class SelectPoetryEnvAction( diff --git a/python/src/com/jetbrains/python/projectCreation/venvWithSdkCreator.kt b/python/src/com/jetbrains/python/projectCreation/venvWithSdkCreator.kt index 05ce2a1236d7..02cdb70f5fb9 100644 --- a/python/src/com/jetbrains/python/projectCreation/venvWithSdkCreator.kt +++ b/python/src/com/jetbrains/python/projectCreation/venvWithSdkCreator.kt @@ -12,19 +12,22 @@ import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.platform.util.progress.withProgressText -import com.intellij.python.community.execService.python.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.validatePythonAndGetInfo import com.intellij.python.community.impl.venv.createVenv import com.intellij.python.community.services.systemPython.SystemPython import com.intellij.python.community.services.systemPython.SystemPythonService import com.intellij.python.community.services.systemPython.createVenvFromSystemPython -import com.jetbrains.python.* +import com.jetbrains.python.PyBundle +import com.jetbrains.python.PythonBinary +import com.jetbrains.python.PythonModuleTypeBase +import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.MessageError import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.errorProcessing.getOr -import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.sdk.configurePythonSdk import com.jetbrains.python.sdk.createSdk import com.jetbrains.python.sdk.flavors.PythonSdkFlavor +import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.sdk.setAssociationToModule import com.jetbrains.python.venvReader.VirtualEnvReader import kotlinx.coroutines.Dispatchers @@ -106,7 +109,7 @@ private suspend fun findExistingVenv( logger.warn("No flavor found for $pythonPath") return@withContext null } - return@withContext when (val p = pythonPath.validatePythonAndGetVersion()) { + return@withContext when (val p = pythonPath.validatePythonAndGetInfo()) { is Result.Success -> pythonPath is Result.Failure -> { logger.warn("No version string. python seems to be broken: $pythonPath. ${p.error}") diff --git a/python/src/com/jetbrains/python/sdk/PySdkExt.kt b/python/src/com/jetbrains/python/sdk/PySdkExt.kt index 70f069805b81..f13e5812ba0f 100644 --- a/python/src/com/jetbrains/python/sdk/PySdkExt.kt +++ b/python/src/com/jetbrains/python/sdk/PySdkExt.kt @@ -511,7 +511,6 @@ fun PyDetectedSdk.pyvenvContains(pattern: String): Boolean = runReadAction { if (isTargetBased()) { return@runReadAction false } - homeDirectory?.toNioPathOrNull()?.parent?.parent?.resolve("pyvenv.cfg") val pyvenvFile = homeDirectory?.parent?.parent?.findFile("pyvenv.cfg") ?: return@runReadAction false val text = FileDocumentManager.getInstance().getDocument(pyvenvFile)?.text ?: return@runReadAction false pattern in text diff --git a/python/src/com/jetbrains/python/sdk/add/v2/CustomNewEnvironmentCreator.kt b/python/src/com/jetbrains/python/sdk/add/v2/CustomNewEnvironmentCreator.kt index 17d26ded338c..f5306eefa5fb 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/CustomNewEnvironmentCreator.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/CustomNewEnvironmentCreator.kt @@ -25,7 +25,7 @@ import org.jetbrains.annotations.ApiStatus.Internal import java.nio.file.Path @Internal -internal abstract class CustomNewEnvironmentCreator( +internal abstract class CustomNewEnvironmentCreator

( private val name: String, model: PythonMutableTargetAddInterpreterModel

, protected val errorSink: ErrorSink, @@ -152,7 +152,7 @@ internal abstract class CustomNewEnvironmentCreator( is InstallableSelectableInterpreter -> installBaseSdk(baseInterpreter.sdk, model.existingSdks) ?.let { val sdkWrapper = model.fileSystem.wrapSdk(it) - val installed = model.addInstalledInterpreter(sdkWrapper.homePath, baseInterpreter.languageLevel) + val installed = model.addInstalledInterpreter(sdkWrapper.homePath, baseInterpreter.pythonInfo) model.state.baseInterpreter.set(installed) installed } diff --git a/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt b/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt index c80613ff5fba..cacb1d191df2 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt @@ -9,13 +9,17 @@ import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.util.io.FileUtil import com.intellij.platform.eel.EelApi import com.intellij.platform.eel.provider.localEel -import com.intellij.python.community.execService.* -import com.intellij.python.community.execService.python.validatePythonAndGetVersion -import com.intellij.python.community.services.internal.impl.VanillaPythonWithLanguageLevelImpl -import com.intellij.python.community.services.shared.VanillaPythonWithLanguageLevel +import com.intellij.python.community.execService.BinOnEel +import com.intellij.python.community.execService.BinOnTarget +import com.intellij.python.community.execService.BinaryToExec +import com.intellij.python.community.execService.ExecService +import com.intellij.python.community.execService.python.validatePythonAndGetInfo +import com.intellij.python.community.services.internal.impl.VanillaPythonWithPythonInfoImpl +import com.intellij.python.community.services.shared.VanillaPythonWithPythonInfo import com.intellij.python.community.services.systemPython.SystemPython import com.intellij.python.community.services.systemPython.SystemPythonService import com.jetbrains.python.PyBundle.message +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.MessageError import com.jetbrains.python.errorProcessing.PyResult @@ -47,7 +51,7 @@ data class SdkWrapper

(val sdk: Sdk, val homePath: P) internal class VenvAlreadyExistsError

( val detectedSelectableInterpreter: DetectedSelectableInterpreter

, -) : MessageError(message("python.add.sdk.already.contains.python.with.version", detectedSelectableInterpreter.languageLevel)) +) : MessageError(message("python.add.sdk.already.contains.python.with.version", detectedSelectableInterpreter.pythonInfo.languageLevel)) sealed interface FileSystem

{ val isReadOnly: Boolean @@ -111,7 +115,7 @@ sealed interface FileSystem

{ val systemPython = SystemPythonService().registerSystemPython(pathToPython.path).getOr { return it } val interpreter = DetectedSelectableInterpreter( homePath = PathHolder.Eel(systemPython.pythonBinary), - languageLevel = systemPython.languageLevel, + pythonInfo = systemPython.pythonInfo, isBase = true ) @@ -125,9 +129,9 @@ sealed interface FileSystem

{ override suspend fun detectSelectableVenv(): List> { // Venvs are not detected manually, but must migrate to VenvService or so val pythonBinaries = VirtualEnvSdkFlavor.getInstance().suggestLocalHomePaths(null, null) - val suggestedPythonBinaries = VanillaPythonWithLanguageLevelImpl.createByPythonBinaries(pythonBinaries) + val suggestedPythonBinaries = VanillaPythonWithPythonInfoImpl.createByPythonBinaries(pythonBinaries) - val venvs: List = suggestedPythonBinaries.mapNotNull { (venv, r) -> + val venvs: List = suggestedPythonBinaries.mapNotNull { (venv, r) -> when (r) { is Result.Failure -> { fileLogger().warn("Skipping $venv : ${r.error}") @@ -147,7 +151,7 @@ sealed interface FileSystem

{ }.map { (python, base, ui) -> DetectedSelectableInterpreter( homePath = PathHolder.Eel(python.pythonBinary), - languageLevel = python.languageLevel, + pythonInfo = python.pythonInfo, isBase = base, ui = ui ) @@ -223,13 +227,13 @@ sealed interface FileSystem

{ private suspend fun registerSystemPython(pathToPython: PathHolder.Target): PyResult> { val pythonBinaryToExec = getBinaryToExec(pathToPython) - val languageLevel = pythonBinaryToExec.validatePythonAndGetVersion().getOr { + val pythonInfo = pythonBinaryToExec.validatePythonAndGetInfo().getOr { return it } val interpreter = DetectedSelectableInterpreter( homePath = pathToPython, - languageLevel = languageLevel, + pythonInfo = pythonInfo, true, ).also { systemPythonCache.add(it) @@ -277,7 +281,7 @@ internal fun

FileSystem

.getInstallableInterpreters(): List - InstallableSelectableInterpreter(languageLevel, sdk) + InstallableSelectableInterpreter(PythonInfo(languageLevel), sdk) } } else -> emptyList() @@ -305,11 +309,11 @@ internal suspend fun

FileSystem

.getExistingSelectableInterpr val languageLevel = sdk.versionString?.let { PythonSdkFlavor.getLanguageLevelFromVersionStringStaticSafe(it) } ?: run { - ExecService().validatePythonAndGetVersion(sdk.asBinToExecute()).getOrLogException(LOG) + ExecService().validatePythonAndGetInfo(sdk.asBinToExecute()).getOrLogException(LOG)?.languageLevel } languageLevel?.let { - ExistingSelectableInterpreter

(wrapSdk(sdk), it, sdk.isSystemWide) + ExistingSelectableInterpreter

(wrapSdk(sdk), PythonInfo(it), sdk.isSystemWide) } } allValidSdks diff --git a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddCustomInterpreter.kt b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddCustomInterpreter.kt index b0734c1a481d..8eed1fe8dbd2 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/PythonAddCustomInterpreter.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/PythonAddCustomInterpreter.kt @@ -167,4 +167,4 @@ internal class PythonAddCustomInterpreter

( fun createStatisticsInfo(): InterpreterStatisticsInfo { return currentSdkManager.createStatisticsInfo(PythonInterpreterCreationTargets.LOCAL_MACHINE) } -} \ No newline at end of file +} diff --git a/python/src/com/jetbrains/python/sdk/add/v2/common.kt b/python/src/com/jetbrains/python/sdk/add/v2/common.kt index ca41f285fb45..4c78e9cf1a1d 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/common.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/common.kt @@ -257,7 +257,7 @@ internal suspend fun

PythonSelectableInterpreter

.setupSdk( allSdks = allSdks, fileSystem = fileSystem, pythonBinaryPath = homePath!!, - languageLevel = languageLevel, + languageLevel = pythonInfo.languageLevel, targetPanelExtension = targetPanelExtension ).getOr { return it } @@ -306,4 +306,4 @@ internal suspend fun BinaryToExec.getToolVersion(toolVersionPrefix: String): PyR val versionPresentation = StringUtil.shortenTextWithEllipsis(version, 250, 0, true) PyResult.localizedError(message("selected.tool.is.wrong", toolVersionPrefix.trim(), versionPresentation)) } -} \ No newline at end of file +} diff --git a/python/src/com/jetbrains/python/sdk/add/v2/models.kt b/python/src/com/jetbrains/python/sdk/add/v2/models.kt index 159dc11aa6cf..a865e970ace5 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/models.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/models.kt @@ -9,20 +9,19 @@ import com.intellij.openapi.observable.properties.ObservableMutableProperty import com.intellij.openapi.observable.properties.PropertyGraph import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.vfs.toNioPathOrNull -import com.intellij.python.community.services.shared.LanguageLevelHolder -import com.intellij.python.community.services.shared.LanguageLevelWithUiComparator +import com.intellij.python.community.services.shared.PythonInfoHolder +import com.intellij.python.community.services.shared.PythonInfoWithUiComparator import com.intellij.python.community.services.shared.UiHolder import com.intellij.python.pyproject.PyProjectToml import com.intellij.util.concurrency.annotations.RequiresEdt -import com.jetbrains.python.* import com.jetbrains.python.PyBundle.message -import com.jetbrains.python.Result.Companion.success -import com.jetbrains.python.errorProcessing.ErrorSink +import com.jetbrains.python.PyToolUIInfo +import com.jetbrains.python.PythonInfo +import com.jetbrains.python.TraceContext import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.errorProcessing.emit import com.jetbrains.python.getOrNull import com.jetbrains.python.newProjectWizard.projectPath.ProjectPathFlows -import com.jetbrains.python.psi.LanguageLevel import com.jetbrains.python.sdk.PySdkToInstall import com.jetbrains.python.sdk.PySdkUtil import com.jetbrains.python.sdk.add.v2.conda.CondaViewModel @@ -119,8 +118,8 @@ abstract class PythonAddInterpreterModel

( manuallyAddedInterpreters ) { detected, manual -> val base = detected?.filter { it.isBase } ?: return@combine null - val existingLanguageLevels = base.map { it.languageLevel }.toSet() - val nonExistingInstallable = installable.filter { it.languageLevel !in existingLanguageLevels } + val existingLanguageLevels = base.map { it.pythonInfo.languageLevel }.toSet() + val nonExistingInstallable = installable.filter { it.pythonInfo.languageLevel !in existingLanguageLevels } manual + base.sorted() + nonExistingInstallable }.stateIn(scope, started = SharingStarted.Eagerly, initialValue = null) } @@ -134,7 +133,7 @@ abstract class PythonAddInterpreterModel

( internal suspend fun addManuallyAddedInterpreter(homePath: P): PyResult> { val python = homePath.let { fileSystem.getSystemPythonFromSelection(it) }.getOr { return it } - val interpreter = ManuallyAddedSelectableInterpreter(homePath, python.languageLevel).also { + val interpreter = ManuallyAddedSelectableInterpreter(homePath, python.pythonInfo).also { this@PythonAddInterpreterModel.addManuallyAddedInterpreter(it) } return PyResult.success(interpreter) @@ -143,15 +142,15 @@ abstract class PythonAddInterpreterModel

( open fun addInterpreter(sdk: Sdk) { val interpreter = ExistingSelectableInterpreter( fileSystem.wrapSdk(sdk), - PySdkUtil.getLanguageLevelForSdk(sdk), + PythonInfo(PySdkUtil.getLanguageLevelForSdk(sdk)), sdk.isSystemWide ) this@PythonAddInterpreterModel.addManuallyAddedInterpreter(interpreter) } @RequiresEdt - internal fun addInstalledInterpreter(homePath: P, languageLevel: LanguageLevel): DetectedSelectableInterpreter

{ - val installedInterpreter = DetectedSelectableInterpreter(homePath, languageLevel, true) + internal fun addInstalledInterpreter(homePath: P, pythonInfo: PythonInfo): DetectedSelectableInterpreter

{ + val installedInterpreter = DetectedSelectableInterpreter(homePath, pythonInfo, true) _detectedInterpreters.value = (_detectedInterpreters.value ?: emptyList()) + installedInterpreter return installedInterpreter } @@ -169,7 +168,7 @@ class PythonLocalAddInterpreterModel

(projectPathFlows: ProjectPa val interpreterToSelect = preferredInterpreterBasePath?.let { path -> detectedInterpreters.value?.find { it.homePath == path } - } ?: baseInterpreters.value?.filterIsInstance>()?.maxByOrNull { it.languageLevel } + } ?: baseInterpreters.value?.filterIsInstance>()?.maxByOrNull { it.pythonInfo.languageLevel } if (interpreterToSelect != null) { state.baseInterpreter.set(interpreterToSelect) @@ -178,13 +177,13 @@ class PythonLocalAddInterpreterModel

(projectPathFlows: ProjectPa } -sealed class PythonSelectableInterpreter

: Comparable>, UiHolder, LanguageLevelHolder { +sealed class PythonSelectableInterpreter

: Comparable>, UiHolder, PythonInfoHolder { companion object { - private val comparator = LanguageLevelWithUiComparator>() + private val comparator = PythonInfoWithUiComparator>() } abstract val homePath: P? - abstract override val languageLevel: LanguageLevel + abstract override val pythonInfo: PythonInfo override val ui: PyToolUIInfo? = null override fun toString(): String = "PythonSelectableInterpreter(homePath='$homePath')" @@ -193,14 +192,14 @@ sealed class PythonSelectableInterpreter

: Comparable( val sdkWrapper: SdkWrapper

, - override val languageLevel: LanguageLevel, + override val pythonInfo: PythonInfo, val isSystemWide: Boolean, ) : PythonSelectableInterpreter

() { override val homePath: P get() = sdkWrapper.homePath override fun toString(): String { - return "ExistingSelectableInterpreter(sdk=${sdkWrapper.sdk}, languageLevel=$languageLevel, isSystemWide=$isSystemWide, homePath='$homePath')" + return "ExistingSelectableInterpreter(sdk=${sdkWrapper.sdk}, pythonInfo=$pythonInfo, isSystemWide=$isSystemWide, homePath='$homePath')" } } @@ -209,26 +208,26 @@ class ExistingSelectableInterpreter

( */ class DetectedSelectableInterpreter

( override val homePath: P, - override val languageLevel: LanguageLevel, + override val pythonInfo: PythonInfo, val isBase: Boolean, override val ui: PyToolUIInfo? = null, ) : PythonSelectableInterpreter

() { override fun toString(): String { - return "DetectedSelectableInterpreter(homePath='$homePath', languageLevel=$languageLevel, isBase=$isBase, uiCustomization=$ui)" + return "DetectedSelectableInterpreter(homePath='$homePath', pythonInfo=$pythonInfo, isBase=$isBase, uiCustomization=$ui)" } } class ManuallyAddedSelectableInterpreter

( override val homePath: P, - override val languageLevel: LanguageLevel, + override val pythonInfo: PythonInfo, ) : PythonSelectableInterpreter

() { override fun toString(): String { - return "ManuallyAddedSelectableInterpreter(homePath='$homePath', languageLevel=$languageLevel)" + return "ManuallyAddedSelectableInterpreter(homePath='$homePath', pythonInfo=$pythonInfo)" } } class InstallableSelectableInterpreter

( - override val languageLevel: LanguageLevel, + override val pythonInfo: PythonInfo, val sdk: PySdkToInstall, ) : PythonSelectableInterpreter

() { override val homePath: P? = null diff --git a/python/src/com/jetbrains/python/sdk/add/v2/poetry/PoetryExistingEnvironmentSelector.kt b/python/src/com/jetbrains/python/sdk/add/v2/poetry/PoetryExistingEnvironmentSelector.kt index c427b32b58ba..362139be2a7b 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/poetry/PoetryExistingEnvironmentSelector.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/poetry/PoetryExistingEnvironmentSelector.kt @@ -5,18 +5,13 @@ import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.ProjectJdkTable import com.intellij.openapi.projectRoots.Sdk import com.jetbrains.python.PyBundle +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.sdk.ModuleOrProject -import com.jetbrains.python.sdk.legacy.PythonSdkUtil -import com.jetbrains.python.sdk.add.v2.CustomExistingEnvironmentSelector -import com.jetbrains.python.sdk.add.v2.DetectedSelectableInterpreter -import com.jetbrains.python.sdk.add.v2.PathHolder -import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel -import com.jetbrains.python.sdk.add.v2.PathValidator -import com.jetbrains.python.sdk.add.v2.ValidatedPath -import com.jetbrains.python.sdk.add.v2.Version +import com.jetbrains.python.sdk.add.v2.* import com.jetbrains.python.sdk.basePath +import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.sdk.poetry.createPoetrySdk import com.jetbrains.python.sdk.poetry.detectPoetryEnvs import com.jetbrains.python.sdk.poetry.isPoetry @@ -25,7 +20,7 @@ import com.jetbrains.python.statistics.version import java.nio.file.Path import kotlin.io.path.pathString -internal class PoetryExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreterModel

, module: Module?) : CustomExistingEnvironmentSelector

("poetry", model, module) { +internal class PoetryExistingEnvironmentSelector

(model: PythonMutableTargetAddInterpreterModel

, module: Module?) : CustomExistingEnvironmentSelector

("poetry", model, module) { override val toolState: PathValidator> = model.poetryViewModel.toolValidator override val interpreterType: InterpreterType = InterpreterType.POETRY @@ -55,10 +50,10 @@ internal class PoetryExistingEnvironmentSelector(model: PythonMut val existingEnvs = detectPoetryEnvs(null, null, modulePath.pathString).mapNotNull { env -> env.homePath?.let { path -> model.fileSystem.parsePath(path).successOrNull?.let { fsPath -> - DetectedSelectableInterpreter

(fsPath, env.version, false) + DetectedSelectableInterpreter

(fsPath, PythonInfo(env.version), false) } } } return existingEnvs } -} \ No newline at end of file +} diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt index d2eb92b5aa47..20e994c004cb 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt @@ -152,7 +152,7 @@ internal fun

SimpleColoredComponent.customizeForPythonInterpret is DetectedSelectableInterpreter, is ManuallyAddedSelectableInterpreter -> { icon = IconLoader.getTransparentIcon(interpreter.ui?.icon ?: PythonParserIcons.PythonFile) val title = interpreter.ui?.toolName ?: message("sdk.rendering.detected.grey.text") - append(String.format("Python %-4s", interpreter.languageLevel)) + append(String.format("Python %-4s", interpreter.pythonInfo.languageLevel)) append(" (" + replaceHomePathToTilde(interpreter.homePath.toString()) + ") $title", SimpleTextAttributes.GRAYED_SMALL_ATTRIBUTES) } is InstallableSelectableInterpreter -> { diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt b/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt index 659c02a70e9a..2e636e7f173e 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt @@ -4,20 +4,15 @@ package com.jetbrains.python.sdk.add.v2.uv import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.Sdk import com.jetbrains.python.PyBundle +import com.jetbrains.python.PythonInfo import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult import com.jetbrains.python.sdk.ModuleOrProject -import com.jetbrains.python.sdk.legacy.PythonSdkUtil -import com.jetbrains.python.sdk.add.v2.CustomExistingEnvironmentSelector -import com.jetbrains.python.sdk.add.v2.DetectedSelectableInterpreter -import com.jetbrains.python.sdk.add.v2.PathHolder -import com.jetbrains.python.sdk.add.v2.PythonMutableTargetAddInterpreterModel -import com.jetbrains.python.sdk.add.v2.PathValidator -import com.jetbrains.python.sdk.add.v2.ValidatedPath -import com.jetbrains.python.sdk.add.v2.Version +import com.jetbrains.python.sdk.add.v2.* import com.jetbrains.python.sdk.associatedModulePath import com.jetbrains.python.sdk.basePath import com.jetbrains.python.sdk.isAssociatedWithModule +import com.jetbrains.python.sdk.legacy.PythonSdkUtil import com.jetbrains.python.sdk.uv.isUv import com.jetbrains.python.sdk.uv.setupExistingEnvAndSdk import com.jetbrains.python.statistics.InterpreterType @@ -27,7 +22,7 @@ import com.jetbrains.python.venvReader.tryResolvePath import java.nio.file.Path import kotlin.io.path.pathString -internal class UvExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreterModel

, module: Module?) +internal class UvExistingEnvironmentSelector

(model: PythonMutableTargetAddInterpreterModel

, module: Module?) : CustomExistingEnvironmentSelector

("uv", model, module) { override val toolState: PathValidator> = model.uvViewModel.toolValidator override val interpreterType: InterpreterType = InterpreterType.UV @@ -68,7 +63,7 @@ internal class UvExistingEnvironmentSelector(model: PythonMutable }.mapNotNull { env -> env.homePath?.let { path -> model.fileSystem.parsePath(path).successOrNull?.let { homePath -> - DetectedSelectableInterpreter(homePath, env.version, false) + DetectedSelectableInterpreter(homePath, PythonInfo(env.version), false) } } } @@ -80,4 +75,4 @@ internal class UvExistingEnvironmentSelector(model: PythonMutable is ModuleOrProject.ModuleAndProject -> moduleOrProject.module else -> null } -} \ No newline at end of file +} diff --git a/python/src/com/jetbrains/python/sdk/poetry/PoetrySdkProvider.kt b/python/src/com/jetbrains/python/sdk/poetry/PoetrySdkProvider.kt index 6fa1f565e1f3..cf08137d6ba2 100644 --- a/python/src/com/jetbrains/python/sdk/poetry/PoetrySdkProvider.kt +++ b/python/src/com/jetbrains/python/sdk/poetry/PoetrySdkProvider.kt @@ -3,10 +3,9 @@ package com.jetbrains.python.sdk.poetry import com.intellij.openapi.module.Module import com.intellij.openapi.projectRoots.Sdk import com.intellij.openapi.projectRoots.SdkAdditionalData -import com.intellij.openapi.util.UserDataHolder import com.jetbrains.python.PyBundle -import com.jetbrains.python.poetry.PoetryPyProjectTomlPythonVersionsService -import com.jetbrains.python.sdk.* +import com.jetbrains.python.sdk.PyInterpreterInspectionQuickFixData +import com.jetbrains.python.sdk.PySdkProvider import com.jetbrains.python.sdk.poetry.quickFixes.PoetryAssociationQuickFix import org.jdom.Element import javax.swing.Icon @@ -43,19 +42,4 @@ class PoetrySdkProvider : PySdkProvider { override fun loadAdditionalDataForSdk(element: Element): SdkAdditionalData? { return PyPoetrySdkAdditionalData.load(element) } - } -// TODO: PythonInterpreterService: validate system python - -internal fun validateSdks(module: Module?, existingSdks: List, context: UserDataHolder): List { - val moduleFile = module?.baseDir - val sdks = findBaseSdks(existingSdks, module, context).takeIf { it.isNotEmpty() } - ?: detectSystemWideSdks(module, existingSdks, context) - - return if (moduleFile != null) { - PoetryPyProjectTomlPythonVersionsService.instance.validateSdkVersions(moduleFile, sdks) - } - else { - sdks - }.filter { it.sdkSeemsValid && !it.isPoetry } -} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/target/PyInterpreterVersionUtil.kt b/python/src/com/jetbrains/python/target/PyInterpreterVersionUtil.kt index adce718e8867..76c621c5b241 100644 --- a/python/src/com/jetbrains/python/target/PyInterpreterVersionUtil.kt +++ b/python/src/com/jetbrains/python/target/PyInterpreterVersionUtil.kt @@ -6,7 +6,7 @@ package com.jetbrains.python.target import com.intellij.python.community.execService.BinOnEel import com.intellij.python.community.execService.BinOnTarget import com.intellij.python.community.execService.BinaryToExec -import com.intellij.python.community.execService.python.validatePythonAndGetVersion +import com.intellij.python.community.execService.python.validatePythonAndGetInfo import com.intellij.remote.RemoteSdkException import com.jetbrains.python.Result import com.jetbrains.python.errorProcessing.PyResult @@ -19,14 +19,16 @@ private fun PyTargetAwareAdditionalData.getBinaryToExec(): BinaryToExec { val configuration = targetEnvironmentConfiguration val binaryToExec = if (configuration == null) { BinOnEel(Path.of(interpreterPath)) - } else { + } + else { BinOnTarget(interpreterPath, configuration) } return binaryToExec } @ApiStatus.Internal -suspend fun PyTargetAwareAdditionalData.getInterpreterVersion(): PyResult = getBinaryToExec().validatePythonAndGetVersion() +suspend fun PyTargetAwareAdditionalData.getInterpreterVersion(): PyResult = + getBinaryToExec().validatePythonAndGetInfo().mapSuccess { it.languageLevel } @ApiStatus.Internal