[python] PY-84777 Use system pythons as a fallback for SDK configuration

There was a problem with detecting system-wide pythons, which relied on
binary not being a part of conda env or virtualenv. But it led to
unrelated Hatch and Poetry pythons automatically configured as
interpreters in new projects. Another problem is that free-threaded
python was chosen as default interpreter with highest priority because
of the newest version.

This change uses SystemPythonService to detect system pythons properly,
also free-threaded python used as a default interpreter only if it's the
only available option.

Merge-request: IJ-MR-179008
Merged-by: Alexey Katsman <alexey.katsman@jetbrains.com>

GitOrigin-RevId: 73bc98aed2918c44832b57f22b86c9c7d17a4301
This commit is contained in:
Alexey Katsman
2025-10-22 13:10:20 +00:00
committed by intellij-monorepo-bot
parent 9140cb4e7b
commit a03643bb9c
44 changed files with 304 additions and 272 deletions
@@ -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<PythonWithLanguageLevel> {
override val pythonInfo: PythonInfo,
) : VanillaPythonWithPythonInfo, Comparable<PythonWithPythonInfo> {
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<PythonBinary>): Collection<Pair<PythonBinary, PyResult<VanillaPythonWithLanguageLevel>>> =
suspend fun createByPythonBinaries(pythonBinaries: Collection<PythonBinary>): Collection<Pair<PythonBinary, PyResult<VanillaPythonWithPythonInfo>>> =
coroutineScope {
pythonBinaries.map {
async {
@@ -49,9 +49,9 @@ class VanillaPythonWithLanguageLevelImpl internal constructor(
}.awaitAll()
}.sortedBy { it.first }
suspend fun createByPythonBinary(pythonBinary: PythonBinary): PyResult<VanillaPythonWithLanguageLevelImpl> {
val languageLevel = pythonBinary.validatePythonAndGetVersion().getOr { return it }
return Result.success(VanillaPythonWithLanguageLevelImpl(pythonBinary, languageLevel))
suspend fun createByPythonBinary(pythonBinary: PythonBinary): PyResult<VanillaPythonWithPythonInfoImpl> {
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)
}
@@ -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")
}
}
@@ -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")
}
@@ -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.*")))
}
}
@@ -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
}
@@ -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]
@@ -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
interface PythonWithUi : PythonWithPythonInfo, UiHolder
@@ -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)
@@ -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<LanguageLevelHolder> {
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<UiHolder> {
override fun compare(o1: UiHolder, o2: UiHolder): Int {
if (logger.isDebugEnabled) {
@@ -28,13 +17,24 @@ object UiComparator : Comparator<UiHolder> {
}
}
class LanguageLevelWithUiComparator<T> : Comparator<T> where T : LanguageLevelHolder, T : UiHolder {
object PythonInfoComparator : Comparator<PythonInfoHolder> {
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<T> : Comparator<T> 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> T.string(): String where T : LanguageLevelHolder, T : UiHolder = "($languageLevel,${ui?.toolName})"
private fun <T> T.string(): String where T : PythonInfoHolder, T : UiHolder =
"(${pythonInfo.languageLevel},${ui?.toolName},free-threaded:${pythonInfo.freeThreaded})"
@@ -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<MockLevel>())
val set = TreeSet(PythonInfoWithUiComparator<MockInfo>())
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
) : PythonInfoHolder, UiHolder
@@ -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<SystemPython> {
class SystemPython internal constructor(private val delegate: VanillaPythonWithPythonInfo, override val ui: PyToolUIInfo?) : VanillaPythonWithPythonInfo by delegate, PythonWithUi, Comparable<SystemPython> {
private companion object {
val comparator = LanguageLevelWithUiComparator<SystemPython>()
val comparator = PythonInfoWithUiComparator<SystemPython>()
}
override fun equals(other: Any?): Boolean {
@@ -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<SystemPython> {
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<PythonBinary>()
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)
}
}
}
@@ -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()
}
}
@@ -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")
}
}
@@ -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<PythonBinary>,
override val uiCustomization: PyToolUIInfo?
override val uiCustomization: PyToolUIInfo?,
) : SystemPythonProvider {
override suspend fun findSystemPythons(eelApi: EelApi) = Result.success(pythons)
}