mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 06:59:44 +07:00
PY-78001 Support adding existing venv's as UV
Add UvExistingEnvironmentSelector (cherry picked from commit be8827506d521c5487cf4fbb3ca15d979f760d44) GitOrigin-RevId: 5a8cdf35bbd89e0473724554d3390d6d9eb19311
This commit is contained in:
committed by
intellij-monorepo-bot
parent
1c33aab97d
commit
dca31999f3
@@ -11,13 +11,10 @@ import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
|
||||
import com.intellij.pycharm.community.ide.impl.PyCharmCommunityCustomizationBundle
|
||||
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.basePath
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfigurationExtension
|
||||
import com.jetbrains.python.sdk.uv.PY_PROJECT_TOML
|
||||
import com.jetbrains.python.sdk.uv.impl.getUvExecutable
|
||||
import com.jetbrains.python.sdk.uv.setupUvSdkUnderProgress
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Path
|
||||
|
||||
class PyUvSdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
companion object {
|
||||
@@ -54,12 +51,7 @@ class PyUvSdkConfiguration : PyProjectSdkConfigurationExtension {
|
||||
override fun supportsHeadlessModel(): Boolean = true
|
||||
|
||||
private suspend fun createUv(module: Module): Result<Sdk> {
|
||||
val basePath = module.basePath?.let { Path.of(it) }
|
||||
if (basePath == null) {
|
||||
return Result.failure(FileNotFoundException("Can't find module base path"))
|
||||
}
|
||||
|
||||
val sdk = setupUvSdkUnderProgress(module, basePath, ProjectJdkTable.getInstance().allJdks.toList(), null)
|
||||
val sdk = setupUvSdkUnderProgress(ModuleOrProject.ModuleAndProject(module), ProjectJdkTable.getInstance().allJdks.toList(), null)
|
||||
sdk.onSuccess {
|
||||
SdkConfigurationUtil.addSdk(it)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.module.Module
|
||||
import com.intellij.openapi.observable.properties.ObservableMutableProperty
|
||||
import com.intellij.openapi.observable.util.notEqualsTo
|
||||
import com.intellij.openapi.ui.validation.DialogValidationRequestor
|
||||
@@ -11,7 +12,6 @@ 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
|
||||
@@ -31,7 +31,7 @@ abstract class CustomExistingEnvironmentSelector(private val name: String, model
|
||||
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) }
|
||||
is ModuleOrProject.ModuleAndProject -> findModulePath(moduleOrProject.module)
|
||||
}
|
||||
|
||||
if (modulePath != null) {
|
||||
@@ -49,6 +49,12 @@ abstract class CustomExistingEnvironmentSelector(private val name: String, model
|
||||
message("sdk.create.custom.venv.missing.text", name),
|
||||
).component
|
||||
|
||||
addInterpretersComboBox(panel)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun addInterpretersComboBox(panel: Panel) {
|
||||
with(panel) {
|
||||
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)
|
||||
@@ -82,4 +88,5 @@ abstract class CustomExistingEnvironmentSelector(private val name: String, model
|
||||
internal abstract val executable: ObservableMutableProperty<String>
|
||||
internal abstract val interpreterType: InterpreterType
|
||||
internal abstract suspend fun detectEnvironments(modulePath: Path)
|
||||
internal abstract suspend fun findModulePath(module: Module): Path?
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class EnvironmentCreatorUv(model: PythonMutableTargetAddInterpreterModel, privat
|
||||
}
|
||||
|
||||
val python = homePath?.let { Path.of(it) }
|
||||
return setupUvSdkUnderProgress(module, Path.of(projectPath), baseSdks, python)
|
||||
return setupUvSdkUnderProgress(ModuleOrProject.ModuleAndProject(module), baseSdks, python)
|
||||
}
|
||||
|
||||
override suspend fun detectExecutable() {
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
// 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.module.Module
|
||||
import com.intellij.openapi.observable.properties.ObservableMutableProperty
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
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.pyProjectToml
|
||||
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) {
|
||||
internal class PoetryExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreterModel, moduleOrProject: ModuleOrProject) : CustomExistingEnvironmentSelector("poetry", model, moduleOrProject) {
|
||||
override val executable: ObservableMutableProperty<String> = model.state.poetryExecutable
|
||||
override val interpreterType: InterpreterType = InterpreterType.POETRY
|
||||
|
||||
@@ -37,4 +40,6 @@ class PoetryExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreter
|
||||
|
||||
existingEnvironments.value = existingEnvs
|
||||
}
|
||||
|
||||
override suspend fun findModulePath(module: Module): Path? = pyProjectToml(module)?.toNioPathOrNull()?.parent
|
||||
}
|
||||
@@ -38,6 +38,7 @@ class PythonAddCustomInterpreter(val model: PythonMutableTargetAddInterpreterMod
|
||||
put(PYTHON, PythonExistingEnvironmentSelector(model))
|
||||
put(CONDA, CondaExistingEnvironmentSelector(model, errorSink))
|
||||
if (moduleOrProject != null) put(POETRY, PoetryExistingEnvironmentSelector(model, moduleOrProject))
|
||||
if (moduleOrProject != null) put(UV, UvExistingEnvironmentSelector(model, moduleOrProject))
|
||||
}
|
||||
|
||||
val currentSdkManager: PythonAddEnvironment
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.sdk.add.v2
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.observable.properties.ObservableMutableProperty
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.associatedModulePath
|
||||
import com.jetbrains.python.sdk.isAssociatedWithModule
|
||||
import com.jetbrains.python.sdk.uv.isUv
|
||||
import com.jetbrains.python.sdk.uv.pyProjectToml
|
||||
import com.jetbrains.python.sdk.uv.setupUvSdkUnderProgress
|
||||
import com.jetbrains.python.statistics.InterpreterType
|
||||
import com.jetbrains.python.statistics.version
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
internal class UvExistingEnvironmentSelector(model: PythonMutableTargetAddInterpreterModel, moduleOrProject: ModuleOrProject)
|
||||
: CustomExistingEnvironmentSelector("uv", model, moduleOrProject) {
|
||||
override val executable: ObservableMutableProperty<String> = model.state.uvExecutable
|
||||
override val interpreterType: InterpreterType = InterpreterType.UV
|
||||
|
||||
override suspend fun getOrCreateSdk(moduleOrProject: ModuleOrProject): Result<Sdk> {
|
||||
val selectedInterpreterPath = selectedEnv.get()?.homePath ?: return Result.failure(FileNotFoundException("No selected interpreter"))
|
||||
val existingSdk = ProjectJdkTable.getInstance().allJdks.find { it.homePath == selectedInterpreterPath }
|
||||
val associatedModule = extractModule(moduleOrProject)
|
||||
|
||||
// uv sdk in current module
|
||||
if (existingSdk != null && existingSdk.isUv && existingSdk.isAssociatedWithModule(associatedModule)) {
|
||||
return Result.success(existingSdk)
|
||||
}
|
||||
|
||||
val existingWorkingDir = existingSdk?.associatedModulePath?.let { Path.of(it) }
|
||||
val usePip = existingWorkingDir!= null && !existingSdk.isUv
|
||||
|
||||
return setupUvSdkUnderProgress(moduleOrProject, ProjectJdkTable.getInstance().allJdks.toList(), Path.of(selectedInterpreterPath), existingWorkingDir, usePip)
|
||||
}
|
||||
|
||||
override suspend fun detectEnvironments(modulePath: Path) {
|
||||
val existingEnvs = ProjectJdkTable.getInstance().allJdks.filter {
|
||||
it.isUv && (it.associatedModulePath == modulePath.pathString || it.associatedModulePath == null)
|
||||
}.mapNotNull { env ->
|
||||
env.homePath?.let { path -> DetectedSelectableInterpreter(path, env.version) }
|
||||
}
|
||||
|
||||
existingEnvironments.value = existingEnvs
|
||||
}
|
||||
|
||||
override suspend fun findModulePath(module: Module): Path? = pyProjectToml(module)?.toNioPathOrNull()?.parent
|
||||
|
||||
private fun extractModule(moduleOrProject: ModuleOrProject): Module? =
|
||||
when (moduleOrProject) {
|
||||
is ModuleOrProject.ModuleAndProject -> moduleOrProject.module
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -264,12 +264,10 @@ internal fun Row.pythonInterpreterComboBox(
|
||||
}
|
||||
}
|
||||
return cell
|
||||
|
||||
|
||||
}
|
||||
|
||||
class PythonInterpreterComboBox(
|
||||
val backingProperty: ObservableMutableProperty<PythonSelectableInterpreter?>,
|
||||
internal class PythonInterpreterComboBox(
|
||||
private val backingProperty: ObservableMutableProperty<PythonSelectableInterpreter?>,
|
||||
val controller: PythonAddInterpreterModel,
|
||||
val onPathSelected: (String) -> Unit,
|
||||
) : ComboBox<PythonSelectableInterpreter?>() {
|
||||
|
||||
@@ -16,6 +16,6 @@ interface UvLowLevel {
|
||||
suspend fun listPackages(): Result<List<PythonPackage>>
|
||||
suspend fun listOutdatedPackages(): Result<List<PythonOutdatedPackage>>
|
||||
|
||||
suspend fun installPackage(name: PythonPackageSpecification, options: List<String>): Result<Unit>
|
||||
suspend fun uninstallPackage(name: PythonPackage): Result<Unit>
|
||||
suspend fun installPackage(name: PythonPackageSpecification, options: List<String>, usePip: Boolean = false): Result<Unit>
|
||||
suspend fun uninstallPackage(name: PythonPackage, usePip: Boolean = false): Result<Unit>
|
||||
}
|
||||
@@ -5,24 +5,24 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.openapi.vfs.toNioPathOrNull
|
||||
import com.intellij.platform.ide.progress.withBackgroundProgress
|
||||
import com.intellij.util.PathUtil
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.icons.PythonIcons
|
||||
import com.jetbrains.python.sdk.createSdk
|
||||
import com.jetbrains.python.sdk.findAmongRoots
|
||||
import com.jetbrains.python.sdk.setAssociationToModule
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.uv.impl.createUvCli
|
||||
import com.jetbrains.python.sdk.uv.impl.createUvLowLevel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.nio.file.Path
|
||||
import javax.swing.Icon
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
internal val Sdk.isUv: Boolean
|
||||
get() = sdkAdditionalData is UvSdkAdditionalData
|
||||
|
||||
internal suspend fun uvLock(module: com.intellij.openapi.module.Module): VirtualFile? {
|
||||
internal suspend fun uvLock(module: Module): VirtualFile? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
findAmongRoots(module, UV_LOCK)
|
||||
}
|
||||
@@ -38,32 +38,54 @@ internal fun suggestedSdkName(basePath: Path): @NlsSafe String {
|
||||
return "uv (${PathUtil.getFileName(basePath.pathString)})"
|
||||
}
|
||||
|
||||
val UV_ICON = PythonIcons.UV
|
||||
val UV_LOCK: String = "uv.lock"
|
||||
val UV_ICON: Icon = PythonIcons.UV
|
||||
const val UV_LOCK: String = "uv.lock"
|
||||
|
||||
// FIXME: move pyprojecttoml code out to common package
|
||||
val PY_PROJECT_TOML: String = "pyproject.toml"
|
||||
const val PY_PROJECT_TOML: String = "pyproject.toml"
|
||||
|
||||
suspend fun setupUvSdkUnderProgress(
|
||||
module: Module,
|
||||
projectPath: Path,
|
||||
moduleOrProject: ModuleOrProject,
|
||||
existingSdks: List<Sdk>,
|
||||
python: Path?
|
||||
python: Path?,
|
||||
existingSdkWorkingDir: Path? = null,
|
||||
usePip: Boolean = false,
|
||||
): Result<Sdk> {
|
||||
val uv = createUvLowLevel(projectPath, createUvCli())
|
||||
|
||||
val init = pyProjectToml(module) == null
|
||||
val (pyProjectToml, moduleWorkingDirectory) = resolveWorkingDirectory(moduleOrProject)
|
||||
val init = pyProjectToml == null
|
||||
val uvWorkingDir = existingSdkWorkingDir ?: moduleWorkingDirectory
|
||||
val uv = createUvLowLevel(uvWorkingDir, createUvCli())
|
||||
|
||||
val envExecutable =
|
||||
withBackgroundProgress(module.project, PyBundle.message("python.sdk.dialog.title.setting.up.uv.environment"), true) {
|
||||
uv.initializeEnvironment(init, python)
|
||||
}.getOrElse {
|
||||
return Result.failure(it)
|
||||
if (existingSdkWorkingDir == null) {
|
||||
withBackgroundProgress(moduleOrProject.project, PyBundle.message("python.sdk.dialog.title.setting.up.uv.environment"), true) {
|
||||
uv.initializeEnvironment(init, python)
|
||||
}.getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
}
|
||||
else {
|
||||
python
|
||||
} ?: throw IllegalArgumentException("Python executable is required to setup uv environment")
|
||||
|
||||
val sdk = createSdk(envExecutable, existingSdks, projectPath.pathString, suggestedSdkName(projectPath), UvSdkAdditionalData())
|
||||
val sdk = createSdk(envExecutable, existingSdks, moduleWorkingDirectory.pathString, suggestedSdkName(moduleWorkingDirectory), UvSdkAdditionalData(existingSdkWorkingDir, usePip))
|
||||
sdk.onSuccess {
|
||||
it.setAssociationToModule(module)
|
||||
it.setAssociationToPath(moduleWorkingDirectory.pathString)
|
||||
}
|
||||
|
||||
return sdk
|
||||
}
|
||||
|
||||
private suspend fun resolveWorkingDirectory(moduleOrProject: ModuleOrProject): Pair<VirtualFile?, Path> {
|
||||
var pyProjectToml: VirtualFile? = null
|
||||
val workingDirectory = when (moduleOrProject) {
|
||||
is ModuleOrProject.ModuleAndProject -> {
|
||||
pyProjectToml = pyProjectToml(moduleOrProject.module)
|
||||
pyProjectToml?.toNioPathOrNull()?.parent ?: moduleOrProject.module.basePath?.let { Path.of(it) }
|
||||
}
|
||||
else -> moduleOrProject.project.basePath?.let { Path.of(it) }
|
||||
} ?: throw IllegalArgumentException("Path to module or working directory is required")
|
||||
|
||||
return Pair(pyProjectToml, workingDirectory)
|
||||
}
|
||||
@@ -22,7 +22,7 @@ internal class UvPackageManager(project: Project, sdk: Sdk, val uv: UvLowLevel)
|
||||
var outdatedPackages: Map<String, PythonOutdatedPackage> = emptyMap()
|
||||
|
||||
override suspend fun installPackageCommand(specification: PythonPackageSpecification, options: List<String>): Result<String> {
|
||||
uv.installPackage(specification, options).getOrElse {
|
||||
uv.installPackage(specification, options, (sdk.sdkAdditionalData as? UvSdkAdditionalData)?.usePip ?: false).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ internal class UvPackageManager(project: Project, sdk: Sdk, val uv: UvLowLevel)
|
||||
}
|
||||
|
||||
override suspend fun updatePackageCommand(specification: PythonPackageSpecification): Result<String> {
|
||||
uv.installPackage(specification, emptyList()).getOrElse {
|
||||
uv.installPackage(specification, emptyList(), (sdk.sdkAdditionalData as? UvSdkAdditionalData)?.usePip ?: false).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ internal class UvPackageManager(project: Project, sdk: Sdk, val uv: UvLowLevel)
|
||||
}
|
||||
|
||||
override suspend fun uninstallPackageCommand(pkg: PythonPackage): Result<String> {
|
||||
uv.uninstallPackage(pkg).getOrElse {
|
||||
uv.uninstallPackage(pkg, (sdk.sdkAdditionalData as? UvSdkAdditionalData)?.usePip ?: false).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ class UvPackageManagerProvider : PythonPackageManagerProvider {
|
||||
return null
|
||||
}
|
||||
|
||||
val uv = createUvLowLevel(Path.of(project.basePath!!), createUvCli())
|
||||
val uvWorkingDirectory = (sdk.sdkAdditionalData as? UvSdkAdditionalData)?.uvWorkingDirectory ?: Path.of(project.basePath!!)
|
||||
val uv = createUvLowLevel(uvWorkingDirectory, createUvCli())
|
||||
return UvPackageManager(project, sdk, uv)
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,30 @@ import com.jetbrains.python.sdk.flavors.PyFlavorData
|
||||
import com.jetbrains.python.sdk.flavors.PythonFlavorProvider
|
||||
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor
|
||||
import org.jdom.Element
|
||||
import java.nio.file.Path
|
||||
import javax.swing.Icon
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
|
||||
class UvSdkAdditionalData : PythonSdkAdditionalData {
|
||||
constructor() : super(UvSdkFlavor)
|
||||
constructor(data: PythonSdkAdditionalData) : super(data)
|
||||
val uvWorkingDirectory: Path?
|
||||
val usePip: Boolean
|
||||
|
||||
constructor(uvWorkingDirectory: Path? = null, usePip: Boolean = false) : super(UvSdkFlavor) {
|
||||
this.uvWorkingDirectory = uvWorkingDirectory
|
||||
this.usePip = usePip
|
||||
}
|
||||
|
||||
constructor(data: PythonSdkAdditionalData, uvWorkingDirectory: Path? = null, usePip: Boolean = false) : super(data) {
|
||||
this.uvWorkingDirectory = uvWorkingDirectory
|
||||
this.usePip = usePip
|
||||
}
|
||||
|
||||
override fun save(element: Element) {
|
||||
super.save(element)
|
||||
element.setAttribute(IS_UV, "true")
|
||||
element.setAttribute(UV_WORKING_DIR, uvWorkingDirectory?.pathString ?: "")
|
||||
element.setAttribute(USE_PIP, usePip.toString())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -24,7 +39,9 @@ class UvSdkAdditionalData : PythonSdkAdditionalData {
|
||||
fun load(element: Element): UvSdkAdditionalData? {
|
||||
return when {
|
||||
element.getAttributeValue(IS_UV) == "true" -> {
|
||||
UvSdkAdditionalData().apply {
|
||||
val uvWorkingDirectory = if (element.getAttributeValue(UV_WORKING_DIR).isNullOrEmpty()) null else Path.of(element.getAttributeValue(UV_WORKING_DIR))
|
||||
val usePip = element.getAttributeValue(USE_PIP)?.toBoolean() ?: false
|
||||
UvSdkAdditionalData(uvWorkingDirectory, usePip).apply {
|
||||
load(element)
|
||||
}
|
||||
}
|
||||
@@ -39,8 +56,8 @@ class UvSdkAdditionalData : PythonSdkAdditionalData {
|
||||
}
|
||||
}
|
||||
|
||||
object UvSdkFlavor : PythonSdkFlavor<PyFlavorData.Empty>() {
|
||||
override fun getIcon() = UV_ICON
|
||||
object UvSdkFlavor : CPythonSdkFlavor<PyFlavorData.Empty>() {
|
||||
override fun getIcon(): Icon = UV_ICON
|
||||
override fun getFlavorDataClass(): Class<PyFlavorData.Empty> = PyFlavorData.Empty::class.java
|
||||
|
||||
override fun isValidSdkPath(pathStr: String): Boolean {
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
internal class UvLowLevelImpl(val cwd: Path, val uvCli: UvCli) : UvLowLevel {
|
||||
internal class UvLowLevelImpl(val cwd: Path, private val uvCli: UvCli) : UvLowLevel {
|
||||
override suspend fun initializeEnvironment(init: Boolean, python: Path?): Result<Path> {
|
||||
val addPythonArg: (MutableList<String>) -> Unit = { args ->
|
||||
python?.let {
|
||||
@@ -93,25 +93,29 @@ internal class UvLowLevelImpl(val cwd: Path, val uvCli: UvCli) : UvLowLevel {
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun installPackage(spec: PythonPackageSpecification, options: List<String>): Result<Unit> {
|
||||
val version = if (spec.versionSpecs.isNullOrBlank()) spec.name else "${spec.name}${spec.versionSpecs}"
|
||||
uvCli.runUv(cwd, "add", version, *options.toTypedArray()).getOrElse {
|
||||
override suspend fun installPackage(name: PythonPackageSpecification, options: List<String>, usePip: Boolean): Result<Unit> {
|
||||
val version = if (name.versionSpecs.isNullOrBlank()) name.name else "${name.name}${name.versionSpecs}"
|
||||
val command = if (usePip) listOf("pip", "install") else listOf("add")
|
||||
uvCli.runUv(cwd, *command.toTypedArray(), version, *options.toTypedArray()).getOrElse {
|
||||
return Result.failure(it)
|
||||
}
|
||||
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun uninstallPackage(name: PythonPackage): Result<Unit> {
|
||||
override suspend fun uninstallPackage(name: PythonPackage, usePip: Boolean): Result<Unit> {
|
||||
// TODO: check if package is in dependencies
|
||||
val result = uvCli.runUv(cwd, "remove", name.name)
|
||||
if (result.isFailure) {
|
||||
val command = if (usePip) listOf("pip", "uninstall") else listOf("remove")
|
||||
val result = uvCli.runUv(cwd, *command.toTypedArray(), name.name)
|
||||
if (result.isFailure && !usePip) {
|
||||
// try just to uninstall
|
||||
uvCli.runUv(cwd, "pip", "uninstall", name.name).onFailure {
|
||||
return Result.failure(it)
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
result.onFailure { return Result.failure(it) }
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package com.jetbrains.python.sdk.uv.ui
|
||||
|
||||
import com.intellij.application.options.ModuleListCellRenderer
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
|
||||
import com.intellij.openapi.module.Module
|
||||
@@ -25,6 +24,7 @@ import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.PySdkBundle
|
||||
import com.jetbrains.python.PythonModuleTypeBase
|
||||
import com.jetbrains.python.newProject.collector.InterpreterStatisticsInfo
|
||||
import com.jetbrains.python.sdk.ModuleOrProject
|
||||
import com.jetbrains.python.sdk.PySdkSettings
|
||||
import com.jetbrains.python.sdk.PythonSdkCoroutineService
|
||||
import com.jetbrains.python.sdk.add.PyAddNewEnvPanel
|
||||
@@ -161,7 +161,7 @@ class PyAddNewUvPanel(
|
||||
setUvExecutable(it)
|
||||
}
|
||||
val sdk = runBlockingCancellable {
|
||||
setupUvSdkUnderProgress(module, Path.of(path), existingSdks, Path.of(python))
|
||||
setupUvSdkUnderProgress(ModuleOrProject.ModuleAndProject(module), existingSdks, Path.of(python))
|
||||
}
|
||||
|
||||
sdk.onSuccess {
|
||||
|
||||
Reference in New Issue
Block a user