diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index fa2be8f19ebe..64bcece6c638 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -568,7 +568,7 @@ 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=Environment: -sdk.create.custom.existing.error.no.interpreters.to.select=No interpreters were found. Please select one manually using the Browse button. +sdk.create.custom.existing.error.no.interpreters.to.select=No interpreters were found. Select manually using the Browse button. sdk.create.custom.hatch.environment=Environment: sdk.create.custom.hatch.environment.loading=Loading environments\u2026 sdk.create.custom.hatch.environment.exists=Environment already exists @@ -1209,6 +1209,7 @@ python.add.sdk.panel.name.system.interpreter=System interpreter python.add.sdk.panel.name.virtualenv.environment=Virtualenv environment python.add.sdk.panel.name.poetry.environment=Poetry Environment python.add.sdk.wait.for.validation=Wait for the executable validation +python.add.sdk.press.enter.to.validate=Press Enter to validate python.add.sdk.version=Version: {0} python.add.sdk.already.contains.python.with.version=Already contains Python with version {0} diff --git a/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt b/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt index 444cf9a15064..4c1316e1f6b7 100644 --- a/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt +++ b/python/src/com/jetbrains/python/sdk/add/v2/FileSystem.kt @@ -13,12 +13,7 @@ import com.intellij.platform.eel.EelApi import com.intellij.platform.eel.provider.asNioPath import com.intellij.platform.eel.provider.localEel import com.intellij.platform.eel.where -import com.intellij.python.community.execService.Args -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.execGetStdout +import com.intellij.python.community.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 @@ -48,6 +43,7 @@ import java.nio.file.InvalidPathException import java.nio.file.Path import kotlin.io.path.Path import kotlin.io.path.exists +import kotlin.io.path.isDirectory private val LOG: Logger = fileLogger() @@ -64,6 +60,7 @@ sealed interface FileSystem
{ val isBrowseable: Boolean fun parsePath(raw: String): PyResult
+ fun validateExecutable(path: P): PyResult {
PyResult.localizedError(e.localizedMessage)
}
+ override fun validateExecutable(path: PathHolder.Eel): PyResult {
return PyResult.success(PathHolder.Target(raw))
}
+ /**
+ * Currently, we don't validate executable on target because there is no API to check path existence and its type on target.
+ */
+ override fun validateExecutable(path: PathHolder.Target): PyResult (
override val backProperty: ObservableMutableProperty (
val path = fileSystem.parsePath(input).getOr { error ->
return@withContext ValidatedPath.Executable (null, error)
}
- val validationResult = pathValidator(path)
- ValidatedPath.Executable(path, validationResult)
+
+ fileSystem.validateExecutable(path).getOr {
+ return@withContext ValidatedPath.Executable(path, it)
+ }
+
+ val toolValidationResult = toolValidator(path)
+ ValidatedPath.Executable(path, toolValidationResult)
}
withContext(Dispatchers.UI) { backProperty.set(exec) }
diff --git a/python/src/com/jetbrains/python/sdk/add/v2/ValidatedPathField.kt b/python/src/com/jetbrains/python/sdk/add/v2/ValidatedPathField.kt
index a2d649f24854..cfe5dc2d13e4 100644
--- a/python/src/com/jetbrains/python/sdk/add/v2/ValidatedPathField.kt
+++ b/python/src/com/jetbrains/python/sdk/add/v2/ValidatedPathField.kt
@@ -7,6 +7,7 @@ import com.intellij.execution.target.getTargetType
import com.intellij.icons.AllIcons
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CustomShortcutSet
+import com.intellij.openapi.application.UI
import com.intellij.openapi.fileChooser.FileChooserDescriptor
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.observable.properties.ObservableMutableProperty
@@ -36,30 +37,23 @@ import com.intellij.ui.dsl.builder.components.ValidationType
import com.intellij.ui.dsl.builder.components.validationTooltip
import com.intellij.util.asDisposable
import com.jetbrains.python.PyBundle.message
-import com.jetbrains.python.onFailure
import com.jetbrains.python.onSuccess
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.jetbrains.annotations.Nls
-import java.awt.AlphaComposite
-import java.awt.Component
-import java.awt.Graphics
-import java.awt.Graphics2D
import java.awt.event.ActionListener
import java.awt.event.KeyEvent
-import java.util.concurrent.atomic.AtomicLong
import javax.swing.Icon
import javax.swing.JTextField
import javax.swing.KeyStroke
import javax.swing.event.DocumentEvent
import kotlin.concurrent.atomics.AtomicBoolean
import kotlin.concurrent.atomics.ExperimentalAtomicApi
-import kotlin.math.cos
interface PathValidator ,
@@ -170,23 +111,12 @@ internal class ValidatedPathField (model: PythonAdd
scope = scope,
)
condaExecutable.initialize(scope)
- condaExecutable.displayLoaderWhen(
- loading = model.condaViewModel.condaEnvironmentsLoading,
- scope = scope,
- )
}
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): PyResult ,
+) : ComboBox Panel.addEnvironmentComboBox(
model: PythonAddInterpreterModel ,
validationRequestor: DialogValidationRequestor,
isGenerateNewMode: Boolean,
-): ComboBox {
val environmentAlreadyExists = AtomicBooleanProperty(false)
- lateinit var environmentComboBox: ComboBox Panel.addEnvironmentComboBox(
model.hatchViewModel.selectedEnvFromExisting
row(message("sdk.create.custom.hatch.environment")) {
- environmentComboBox = comboBox(emptyList(), HatchEnvComboBoxListCellRenderer(model.hatchViewModel.availableEnvironments))
+ cell(environmentComboBox)
.bindItem(hatchEnvironmentProperty)
.validationRequestor(validationRequestor and WHEN_PROPERTY_CHANGED(hatchEnvironmentProperty))
.validationInfo { component ->
@@ -121,7 +142,7 @@ private fun Panel.addEnvironmentComboBox(
when {
!component.isVisible || !component.isEnabled -> null
component.item == null -> {
- ValidationInfo(message("sdk.create.custom.hatch.error.no.environments.to.select"))
+ ValidationInfo("")
}
isGenerateNewMode && component.item?.pythonVirtualEnvironment is PythonVirtualEnvironment.Existing -> {
environmentAlreadyExists.set(true)
@@ -134,7 +155,6 @@ private fun Panel.addEnvironmentComboBox(
.applyToComponent {
preferredSize = JBUI.size(preferredSize)
}
- .component
}
row("") {
@@ -171,7 +191,7 @@ private fun Panel.addExecutableSelector(
}
internal data class HatchFormFields (
- val environmentComboBox: ComboBox ,
val basePythonComboBox: PythonInterpreterComboBox ?,
val validatedPathField: ValidatedPathField Panel.buildHatchFormFields(
validationRequestor,
installHatchActionLink
)
- var environmentComboBox: ComboBox by lateinitVal()
var basePythonComboBox: PythonInterpreterComboBox ? = null
rowsRange {
@@ -238,15 +258,3 @@ internal fun Panel.buildHatchFormFields(
return HatchFormFields(environmentComboBox, basePythonComboBox, executablePath)
}
-@Synchronized
-internal fun ComboBox (
hatchExecutable.afterChange { hatchExecutable ->
if (hatchExecutable?.validationResult?.successOrNull == null) {
- availableEnvironments.value = null
+ availableEnvironments.value = PyResult.success(emptyList())
return@afterChange
}
diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt
index 05a8e40214fe..6497bfaa7811 100644
--- a/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt
+++ b/python/src/com/jetbrains/python/sdk/add/v2/uiUtils.kt
@@ -178,7 +178,6 @@ internal fun SimpleColoredComponent.customizeForPythonInterpret
return
}
interpreter == null -> {
- icon = AllIcons.General.ShowWarning
append(message("sdk.create.custom.existing.error.no.interpreters.to.select"), SimpleTextAttributes.GRAYED_ITALIC_ATTRIBUTES)
return
}
@@ -304,7 +303,7 @@ internal fun Panel.pythonInterpreterComboBox(
when {
!it.isVisible -> null
it.isLoading.get() -> ValidationInfo(message("python.add.sdk.panel.wait"))
- selectedSdkProperty.get() == null -> ValidationInfo(message("sdk.create.custom.existing.error.no.interpreters.to.select"))
+ selectedSdkProperty.get() == null -> ValidationInfo("")
else -> null
}
}
diff --git a/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt b/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt
index 2e636e7f173e..6c99ee3166e7 100644
--- a/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt
+++ b/python/src/com/jetbrains/python/sdk/add/v2/uv/UvExistingEnvironmentSelector.kt
@@ -3,25 +3,30 @@ package com.jetbrains.python.sdk.add.v2.uv
import com.intellij.openapi.module.Module
import com.intellij.openapi.projectRoots.Sdk
+import com.intellij.python.community.execService.python.validatePythonAndGetInfo
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.getOrNull
import com.jetbrains.python.sdk.ModuleOrProject
import com.jetbrains.python.sdk.add.v2.*
import com.jetbrains.python.sdk.associatedModulePath
import com.jetbrains.python.sdk.basePath
+import com.jetbrains.python.sdk.impl.resolvePythonBinary
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
-import com.jetbrains.python.statistics.version
import com.jetbrains.python.venvReader.VirtualEnvReader
import com.jetbrains.python.venvReader.tryResolvePath
+import java.nio.file.Files
import java.nio.file.Path
+import java.util.stream.Collectors
+import kotlin.io.path.exists
import kotlin.io.path.pathString
+
internal class UvExistingEnvironmentSelector (model: PythonMutableTargetAddInterpreterModel , module: Module?)
: CustomExistingEnvironmentSelector ("uv", model, module) {
override val toolState: PathValidator (model: PythonMutabl
}
override suspend fun detectEnvironments(modulePath: Path): List>,
+ isFilterOnlyExisting: Boolean = false,
+ ) {
+ removeAllItems()
+ val environments = environmentsResult.getOr { return }
+ environments.filter { !isFilterOnlyExisting || it.pythonVirtualEnvironment is PythonVirtualEnvironment.Existing }.forEach {
+ addItem(it)
+ }
+ }
+}
+
+
private fun
>,
- isFilterOnlyExisting: Boolean = false,
-) {
- removeAllItems()
- val environments = environmentsResult.getOr { return }
- environments.filter { !isFilterOnlyExisting || it.pythonVirtualEnvironment is PythonVirtualEnvironment.Existing }.forEach {
- addItem(it)
- }
-}
-
diff --git a/python/src/com/jetbrains/python/sdk/add/v2/hatch/HatchViewModel.kt b/python/src/com/jetbrains/python/sdk/add/v2/hatch/HatchViewModel.kt
index fd4864130ebf..e768c9372ce9 100644
--- a/python/src/com/jetbrains/python/sdk/add/v2/hatch/HatchViewModel.kt
+++ b/python/src/com/jetbrains/python/sdk/add/v2/hatch/HatchViewModel.kt
@@ -56,7 +56,7 @@ class HatchViewModel