PY-77381 Unable to select an existing Poetry environment as a project interpreter

Add PoetryExistingEnvironmentSelector

(cherry picked from commit 4fc75a24669d8a2fc12b5ff963cc5715ebc6ab3a)

GitOrigin-RevId: b5ed7b45a3b4e27786bf899ab42f9ffe757d7f9d
This commit is contained in:
Egor.Eliseev
2024-12-20 15:19:05 +02:00
committed by intellij-monorepo-bot
parent 18f5e952db
commit 1c33aab97d
6 changed files with 133 additions and 8 deletions

View File

@@ -551,6 +551,7 @@ sdk.create.tooltip.browse=Browse\u2026
sdk.create.custom.venv.install.fix.title=Install {0} {1}
sdk.create.custom.venv.run.error.message= Error Running {0}
sdk.create.custom.venv.progress.title.detect.executable=Detect executable
sdk.create.custom.existing.env.title ={0} env use
sdk.create.targets.local=Local Machine
sdk.create.custom.virtualenv=Virtualenv

View File

@@ -0,0 +1,85 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.sdk.add.v2
import com.intellij.openapi.observable.properties.ObservableMutableProperty
import com.intellij.openapi.observable.util.notEqualsTo
import com.intellij.openapi.ui.validation.DialogValidationRequestor
import com.intellij.ui.dsl.builder.Align
import com.intellij.ui.dsl.builder.Panel
import com.jetbrains.python.PyBundle.message
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.PySdkUtil
import com.jetbrains.python.sdk.PythonSdkUtil
import com.jetbrains.python.sdk.poetry.pyProjectToml
import com.jetbrains.python.statistics.InterpreterCreationMode
import com.jetbrains.python.statistics.InterpreterType
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.jetbrains.annotations.ApiStatus.Internal
import java.nio.file.Path
import java.util.*
@Internal
abstract class CustomExistingEnvironmentSelector(private val name: String, model: PythonMutableTargetAddInterpreterModel, protected val moduleOrProject: ModuleOrProject) : PythonExistingEnvironmentConfigurator(model) {
private lateinit var comboBox: PythonInterpreterComboBox
protected val existingEnvironments: MutableStateFlow<List<PythonSelectableInterpreter>> = MutableStateFlow(emptyList())
protected val selectedEnv: ObservableMutableProperty<PythonSelectableInterpreter?> = propertyGraph.property(null)
init {
model.scope.launch {
val modulePath = when (moduleOrProject) {
is ModuleOrProject.ProjectOnly -> moduleOrProject.project.basePath?.let { Path.of(it) }
is ModuleOrProject.ModuleAndProject -> pyProjectToml(moduleOrProject.module)?.let { Path.of(it.parent.path) }
}
if (modulePath != null) {
detectEnvironments(modulePath)
}
}
}
override fun buildOptions(panel: Panel, validationRequestor: DialogValidationRequestor) {
with(panel) {
executableSelector(
executable,
validationRequestor,
message("sdk.create.custom.venv.executable.path", name),
message("sdk.create.custom.venv.missing.text", name),
).component
row(message("sdk.create.custom.existing.env.title", name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() })) {
comboBox = pythonInterpreterComboBox(selectedEnv, model, { path -> addEnvByPath(path) }, model.interpreterLoading)
.align(Align.FILL)
.component
}.visibleIf(executable.notEqualsTo(""))
}
}
override fun onShown() {
comboBox.setItems(existingEnvironments)
}
override fun createStatisticsInfo(target: PythonInterpreterCreationTargets): InterpreterStatisticsInfo {
val statisticsTarget = target.toStatisticsField()
return InterpreterStatisticsInfo(interpreterType,
statisticsTarget,
false,
false,
true,
false,
InterpreterCreationMode.CUSTOM)
}
private fun addEnvByPath(path: String) {
val languageLevel = PySdkUtil.getLanguageLevelForSdk(PythonSdkUtil.findSdkByKey(path))
val interpreter = ManuallyAddedSelectableInterpreter(path, languageLevel)
existingEnvironments.value += interpreter
}
internal abstract val executable: ObservableMutableProperty<String>
internal abstract val interpreterType: InterpreterType
internal abstract suspend fun detectEnvironments(modulePath: Path)
}

View File

@@ -0,0 +1,40 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.sdk.add.v2
import com.intellij.openapi.observable.properties.ObservableMutableProperty
import com.intellij.openapi.projectRoots.ProjectJdkTable
import com.intellij.openapi.projectRoots.Sdk
import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.poetry.detectPoetryEnvs
import com.jetbrains.python.sdk.poetry.isPoetry
import com.jetbrains.python.sdk.poetry.setupPoetrySdkUnderProgress
import com.jetbrains.python.statistics.InterpreterType
import com.jetbrains.python.statistics.version
import java.nio.file.Path
import kotlin.io.path.pathString
class PoetryExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreterModel, moduleOrProject: ModuleOrProject) : CustomExistingEnvironmentSelector("poetry", model, moduleOrProject) {
override val executable: ObservableMutableProperty<String> = model.state.poetryExecutable
override val interpreterType: InterpreterType = InterpreterType.POETRY
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk> {
val selectedInterpreter = selectedEnv.get()
ProjectJdkTable.getInstance().allJdks.find { sdk -> sdk.isPoetry && sdk.homePath == selectedInterpreter?.homePath }?.let { return Result.success(it) }
val module = when (moduleOrProject) {
is ModuleOrProject.ModuleAndProject -> {
moduleOrProject.module
}
else -> null
}
return setupPoetrySdkUnderProgress(moduleOrProject.project, module, ProjectJdkTable.getInstance().allJdks.toList(), null, selectedInterpreter?.homePath, true)
}
override suspend fun detectEnvironments(modulePath: Path) {
val existingEnvs = detectPoetryEnvs(null, null, modulePath.pathString).mapNotNull {
env -> env.homePath?.let { path -> DetectedSelectableInterpreter(path, env.version) }
}
existingEnvironments.value = existingEnvs
}
}

View File

@@ -34,10 +34,11 @@ class PythonAddCustomInterpreter(val model: PythonMutableTargetAddInterpreterMod
UV to EnvironmentCreatorUv(model, moduleOrProject),
)
private val existingInterpreterSelectors = mapOf(
PYTHON to PythonExistingEnvironmentSelector(model),
CONDA to CondaExistingEnvironmentSelector(model, errorSink),
)
private val existingInterpreterSelectors = buildMap {
put(PYTHON, PythonExistingEnvironmentSelector(model))
put(CONDA, CondaExistingEnvironmentSelector(model, errorSink))
if (moduleOrProject != null) put(POETRY, PoetryExistingEnvironmentSelector(model, moduleOrProject))
}
val currentSdkManager: PythonAddEnvironment
get() {

View File

@@ -274,8 +274,6 @@ open class AddInterpreterState(propertyGraph: PropertyGraph) {
* Use [PythonAddInterpreterModel.getBaseCondaOrError]
*/
val baseCondaEnv: ObservableMutableProperty<PyCondaEnv?> = propertyGraph.property(null)
}
class MutableTargetState(propertyGraph: PropertyGraph) : AddInterpreterState(propertyGraph) {

View File

@@ -154,9 +154,9 @@ internal fun runPoetryInBackground(module: Module, args: List<String>, @NlsSafe
}
}
internal suspend fun detectPoetryEnvs(module: Module?, existingSdkPaths: Set<String>, projectPath: @SystemIndependent @NonNls String?): List<PyDetectedSdk> {
internal suspend fun detectPoetryEnvs(module: Module?, existingSdkPaths: Set<String>?, projectPath: @SystemIndependent @NonNls String?): List<PyDetectedSdk> {
val path = module?.basePath?.let { Path.of(it) } ?: projectPath?.let { Path.of(it) } ?: return emptyList()
return getPoetryEnvs(path).filter { existingSdkPaths.contains(getPythonExecutable(it)) }.map { PyDetectedSdk(getPythonExecutable(it)) }
return getPoetryEnvs(path).filter { existingSdkPaths?.contains(getPythonExecutable(it)) != false }.map { PyDetectedSdk(getPythonExecutable(it)) }
}
internal suspend fun getPoetryVersion(): String? = runPoetry(null, "--version").getOrNull()?.split(' ')?.lastOrNull()