mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[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:
committed by
intellij-monorepo-bot
parent
9140cb4e7b
commit
a03643bb9c
@@ -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<Sdk>,
|
||||
project: Project,
|
||||
systemPythons: List<SystemPython>,
|
||||
): 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<SystemPython> { 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<Sdk>,
|
||||
project: Project,
|
||||
module: Module,
|
||||
existingSdks: List<Sdk>,
|
||||
systemPythons: List<SystemPython>,
|
||||
): 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<SystemPython> { 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")
|
||||
|
||||
@@ -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 <T> ExecService.executeHelperAdvanced(
|
||||
): PyResult<T> = 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<LanguageLevel> =
|
||||
ExecService().validatePythonAndGetVersion(asExecutablePython)
|
||||
suspend fun ValidInterpreter.validatePythonAndGetInfo(): PyResult<PythonInfo> =
|
||||
ExecService().validatePythonAndGetInfo(asExecutablePython)
|
||||
|
||||
/**
|
||||
* Execute [helper] on [python]. For remote eels, [helper] is copied (but only one file!).
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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)"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <T : PyFlavorData> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
python/openapi/src/com/jetbrains/python/PythonInfo.kt
Normal file
22
python/openapi/src/com/jetbrains/python/PythonInfo.kt
Normal file
@@ -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<PythonInfo> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 <T> 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<LanguageLevel> =
|
||||
validatePythonAndGetVersionImpl(python)
|
||||
suspend fun ExecService.validatePythonAndGetInfo(python: ExecutablePython): PyResult<PythonInfo> =
|
||||
validatePythonAndGetInfoImpl(python)
|
||||
|
||||
@@ -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<LanguageLevel> =
|
||||
validatePythonAndGetVersion(ExecutablePython.vanillaExecutablePython(python))
|
||||
suspend fun ExecService.validatePythonAndGetInfo(python: PythonBinaryOnEelOrTarget): PyResult<PythonInfo> =
|
||||
validatePythonAndGetInfo(ExecutablePython.vanillaExecutablePython(python))
|
||||
|
||||
suspend fun PythonBinaryOnEelOrTarget.validatePythonAndGetVersion(): PyResult<LanguageLevel> = ExecService().validatePythonAndGetVersion(this)
|
||||
suspend fun ExecService.validatePythonAndGetVersion(python: PythonBinary): PyResult<LanguageLevel> = validatePythonAndGetVersion(python.asBinToExec())
|
||||
suspend fun PythonBinary.validatePythonAndGetVersion(): PyResult<LanguageLevel> = asBinToExec().validatePythonAndGetVersion()
|
||||
suspend fun PythonBinaryOnEelOrTarget.validatePythonAndGetInfo(): PyResult<PythonInfo> = ExecService().validatePythonAndGetInfo(this)
|
||||
suspend fun ExecService.validatePythonAndGetInfo(python: PythonBinary): PyResult<PythonInfo> = validatePythonAndGetInfo(python.asBinToExec())
|
||||
suspend fun PythonBinary.validatePythonAndGetInfo(): PyResult<PythonInfo> = asBinToExec().validatePythonAndGetInfo()
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<LanguageLevel> = withContext(Dispatchers.IO) {
|
||||
internal suspend fun ExecService.validatePythonAndGetInfoImpl(python: ExecutablePython): PyResult<PythonInfo> = 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<EelProcessExecutionResult>(null, { r ->
|
||||
val versionOutput: EelProcessExecutionResult = executePythonAdvanced(python, options = options, args = Args(PYTHON_VERSION_ARG), processInteractiveHandler = transformerToHandler<EelProcessExecutionResult>(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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -95,9 +95,9 @@ class PoetryPyProjectTomlPythonVersionsService : Disposable {
|
||||
fun validateSdkVersions(moduleFile: VirtualFile, sdks: List<Sdk>): List<Sdk> =
|
||||
sdks.filter { getVersion(moduleFile).isValid(it.versionString) }
|
||||
|
||||
fun <P: PathHolder> validateInterpretersVersions(moduleFile: VirtualFile, interpreters: Flow<List<PythonSelectableInterpreter<P>>?>): Flow<List<PythonSelectableInterpreter<P>>?> {
|
||||
fun <P : PathHolder> validateInterpretersVersions(moduleFile: VirtualFile, interpreters: Flow<List<PythonSelectableInterpreter<P>>?>): Flow<List<PythonSelectableInterpreter<P>>?> {
|
||||
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 =
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.ApiStatus.Internal
|
||||
import java.nio.file.Path
|
||||
|
||||
@Internal
|
||||
internal abstract class CustomNewEnvironmentCreator<P: PathHolder>(
|
||||
internal abstract class CustomNewEnvironmentCreator<P : PathHolder>(
|
||||
private val name: String,
|
||||
model: PythonMutableTargetAddInterpreterModel<P>,
|
||||
protected val errorSink: ErrorSink,
|
||||
@@ -152,7 +152,7 @@ internal abstract class CustomNewEnvironmentCreator<P: PathHolder>(
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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<P>(val sdk: Sdk, val homePath: P)
|
||||
|
||||
internal class VenvAlreadyExistsError<P : PathHolder>(
|
||||
val detectedSelectableInterpreter: DetectedSelectableInterpreter<P>,
|
||||
) : 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<P : PathHolder> {
|
||||
val isReadOnly: Boolean
|
||||
@@ -111,7 +115,7 @@ sealed interface FileSystem<P : PathHolder> {
|
||||
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<P : PathHolder> {
|
||||
override suspend fun detectSelectableVenv(): List<DetectedSelectableInterpreter<PathHolder.Eel>> {
|
||||
// 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<VanillaPythonWithLanguageLevel> = suggestedPythonBinaries.mapNotNull { (venv, r) ->
|
||||
val venvs: List<VanillaPythonWithPythonInfo> = suggestedPythonBinaries.mapNotNull { (venv, r) ->
|
||||
when (r) {
|
||||
is Result.Failure -> {
|
||||
fileLogger().warn("Skipping $venv : ${r.error}")
|
||||
@@ -147,7 +151,7 @@ sealed interface FileSystem<P : PathHolder> {
|
||||
}.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<P : PathHolder> {
|
||||
|
||||
private suspend fun registerSystemPython(pathToPython: PathHolder.Target): PyResult<DetectedSelectableInterpreter<PathHolder.Target>> {
|
||||
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 <P : PathHolder> FileSystem<P>.getInstallableInterpreters(): List<I
|
||||
}
|
||||
.sortedByDescending { it.first }
|
||||
.map { (languageLevel, sdk) ->
|
||||
InstallableSelectableInterpreter(languageLevel, sdk)
|
||||
InstallableSelectableInterpreter(PythonInfo(languageLevel), sdk)
|
||||
}
|
||||
}
|
||||
else -> emptyList()
|
||||
@@ -305,11 +309,11 @@ internal suspend fun <P : PathHolder> FileSystem<P>.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<P>(wrapSdk(sdk), it, sdk.isSystemWide)
|
||||
ExistingSelectableInterpreter<P>(wrapSdk(sdk), PythonInfo(it), sdk.isSystemWide)
|
||||
}
|
||||
}
|
||||
allValidSdks
|
||||
|
||||
@@ -167,4 +167,4 @@ internal class PythonAddCustomInterpreter<P : PathHolder>(
|
||||
fun createStatisticsInfo(): InterpreterStatisticsInfo {
|
||||
return currentSdkManager.createStatisticsInfo(PythonInterpreterCreationTargets.LOCAL_MACHINE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +257,7 @@ internal suspend fun <P : PathHolder> PythonSelectableInterpreter<P>.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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<P : PathHolder>(
|
||||
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<P : PathHolder>(
|
||||
internal suspend fun addManuallyAddedInterpreter(homePath: P): PyResult<ManuallyAddedSelectableInterpreter<P>> {
|
||||
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<P : PathHolder>(
|
||||
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<P> {
|
||||
val installedInterpreter = DetectedSelectableInterpreter(homePath, languageLevel, true)
|
||||
internal fun addInstalledInterpreter(homePath: P, pythonInfo: PythonInfo): DetectedSelectableInterpreter<P> {
|
||||
val installedInterpreter = DetectedSelectableInterpreter(homePath, pythonInfo, true)
|
||||
_detectedInterpreters.value = (_detectedInterpreters.value ?: emptyList()) + installedInterpreter
|
||||
return installedInterpreter
|
||||
}
|
||||
@@ -169,7 +168,7 @@ class PythonLocalAddInterpreterModel<P : PathHolder>(projectPathFlows: ProjectPa
|
||||
|
||||
val interpreterToSelect = preferredInterpreterBasePath?.let { path ->
|
||||
detectedInterpreters.value?.find { it.homePath == path }
|
||||
} ?: baseInterpreters.value?.filterIsInstance<ExistingSelectableInterpreter<P>>()?.maxByOrNull { it.languageLevel }
|
||||
} ?: baseInterpreters.value?.filterIsInstance<ExistingSelectableInterpreter<P>>()?.maxByOrNull { it.pythonInfo.languageLevel }
|
||||
|
||||
if (interpreterToSelect != null) {
|
||||
state.baseInterpreter.set(interpreterToSelect)
|
||||
@@ -178,13 +177,13 @@ class PythonLocalAddInterpreterModel<P : PathHolder>(projectPathFlows: ProjectPa
|
||||
}
|
||||
|
||||
|
||||
sealed class PythonSelectableInterpreter<P : PathHolder> : Comparable<PythonSelectableInterpreter<*>>, UiHolder, LanguageLevelHolder {
|
||||
sealed class PythonSelectableInterpreter<P : PathHolder> : Comparable<PythonSelectableInterpreter<*>>, UiHolder, PythonInfoHolder {
|
||||
companion object {
|
||||
private val comparator = LanguageLevelWithUiComparator<PythonSelectableInterpreter<*>>()
|
||||
private val comparator = PythonInfoWithUiComparator<PythonSelectableInterpreter<*>>()
|
||||
}
|
||||
|
||||
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<P : PathHolder> : Comparable<PythonSele
|
||||
|
||||
class ExistingSelectableInterpreter<P : PathHolder>(
|
||||
val sdkWrapper: SdkWrapper<P>,
|
||||
override val languageLevel: LanguageLevel,
|
||||
override val pythonInfo: PythonInfo,
|
||||
val isSystemWide: Boolean,
|
||||
) : PythonSelectableInterpreter<P>() {
|
||||
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<P : PathHolder>(
|
||||
*/
|
||||
class DetectedSelectableInterpreter<P : PathHolder>(
|
||||
override val homePath: P,
|
||||
override val languageLevel: LanguageLevel,
|
||||
override val pythonInfo: PythonInfo,
|
||||
val isBase: Boolean,
|
||||
override val ui: PyToolUIInfo? = null,
|
||||
) : PythonSelectableInterpreter<P>() {
|
||||
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<P : PathHolder>(
|
||||
override val homePath: P,
|
||||
override val languageLevel: LanguageLevel,
|
||||
override val pythonInfo: PythonInfo,
|
||||
) : PythonSelectableInterpreter<P>() {
|
||||
override fun toString(): String {
|
||||
return "ManuallyAddedSelectableInterpreter(homePath='$homePath', languageLevel=$languageLevel)"
|
||||
return "ManuallyAddedSelectableInterpreter(homePath='$homePath', pythonInfo=$pythonInfo)"
|
||||
}
|
||||
}
|
||||
|
||||
class InstallableSelectableInterpreter<P : PathHolder>(
|
||||
override val languageLevel: LanguageLevel,
|
||||
override val pythonInfo: PythonInfo,
|
||||
val sdk: PySdkToInstall,
|
||||
) : PythonSelectableInterpreter<P>() {
|
||||
override val homePath: P? = null
|
||||
|
||||
@@ -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<P: PathHolder>(model: PythonMutableTargetAddInterpreterModel<P>, module: Module?) : CustomExistingEnvironmentSelector<P>("poetry", model, module) {
|
||||
internal class PoetryExistingEnvironmentSelector<P : PathHolder>(model: PythonMutableTargetAddInterpreterModel<P>, module: Module?) : CustomExistingEnvironmentSelector<P>("poetry", model, module) {
|
||||
override val toolState: PathValidator<Version, P, ValidatedPath.Executable<P>> = model.poetryViewModel.toolValidator
|
||||
override val interpreterType: InterpreterType = InterpreterType.POETRY
|
||||
|
||||
@@ -55,10 +50,10 @@ internal class PoetryExistingEnvironmentSelector<P: PathHolder>(model: PythonMut
|
||||
val existingEnvs = detectPoetryEnvs(null, null, modulePath.pathString).mapNotNull { env ->
|
||||
env.homePath?.let { path ->
|
||||
model.fileSystem.parsePath(path).successOrNull?.let { fsPath ->
|
||||
DetectedSelectableInterpreter<P>(fsPath, env.version, false)
|
||||
DetectedSelectableInterpreter<P>(fsPath, PythonInfo(env.version), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
return existingEnvs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ internal fun <P : PathHolder> 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 -> {
|
||||
|
||||
@@ -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<P: PathHolder>(model: PythonMutableTargetAddInterpreterModel<P>, module: Module?)
|
||||
internal class UvExistingEnvironmentSelector<P : PathHolder>(model: PythonMutableTargetAddInterpreterModel<P>, module: Module?)
|
||||
: CustomExistingEnvironmentSelector<P>("uv", model, module) {
|
||||
override val toolState: PathValidator<Version, P, ValidatedPath.Executable<P>> = model.uvViewModel.toolValidator
|
||||
override val interpreterType: InterpreterType = InterpreterType.UV
|
||||
@@ -68,7 +63,7 @@ internal class UvExistingEnvironmentSelector<P: PathHolder>(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<P: PathHolder>(model: PythonMutable
|
||||
is ModuleOrProject.ModuleAndProject -> moduleOrProject.module
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Sdk>, context: UserDataHolder): List<Sdk> {
|
||||
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 }
|
||||
}
|
||||
@@ -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<LanguageLevel> = getBinaryToExec().validatePythonAndGetVersion()
|
||||
suspend fun PyTargetAwareAdditionalData.getInterpreterVersion(): PyResult<LanguageLevel> =
|
||||
getBinaryToExec().validatePythonAndGetInfo().mapSuccess { it.languageLevel }
|
||||
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
||||
Reference in New Issue
Block a user