fixup python plugin sdk configuration silent errors; fixup quick fix path association getting stuck

GitOrigin-RevId: e709f2063464bfc4b90f3acdf0c6b82277f2dc65
This commit is contained in:
Aleksandr Sorotskii
2025-05-09 17:15:29 +02:00
committed by intellij-monorepo-bot
parent ad065be46a
commit 67a49445ef
26 changed files with 144 additions and 124 deletions

View File

@@ -40,9 +40,8 @@ import com.jetbrains.python.sdk.flavors.conda.CondaEnvSdkFlavor
import com.jetbrains.python.sdk.flavors.conda.NewCondaEnvRequest
import com.jetbrains.python.sdk.flavors.conda.PyCondaCommand
import com.jetbrains.python.sdk.flavors.listCondaEnvironments
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.sdk.setAssociationToModuleAsync
import com.jetbrains.python.sdk.showSdkExecutionException
import com.jetbrains.python.ui.pyModalBlocking
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import java.awt.BorderLayout
@@ -56,14 +55,12 @@ import javax.swing.JPanel
*/
internal class PyEnvironmentYmlSdkConfiguration : PyProjectSdkConfigurationExtension {
private val LOGGER = Logger.getInstance(PyEnvironmentYmlSdkConfiguration::class.java)
@RequiresBackgroundThread
override fun createAndAddSdkForConfigurator(module: Module): Sdk? = createAndAddSdk(module, Source.CONFIGURATOR)
override fun getIntention(module: Module): @IntentionName String? = getEnvironmentYml(module)?.let {
PyCharmCommunityCustomizationBundle.message("sdk.create.condaenv.suggestion")
}
@RequiresBackgroundThread
override fun createAndAddSdkForInspection(module: Module): Sdk? = createAndAddSdk(module, Source.INSPECTION)
@@ -123,7 +120,7 @@ internal class PyEnvironmentYmlSdkConfiguration : PyProjectSdkConfigurationExten
ApplicationManager.getApplication().invokeAndWait {
LOGGER.debug("Adding conda environment: ${sdk.homePath}, associated ${shared}}, module path ${basePath})")
if (!shared) {
pyModalBlocking { sdk.setAssociationToModule(module) }
sdk.setAssociationToModuleAsync(module)
}
SdkConfigurationUtil.addSdk(sdk)

View File

@@ -380,37 +380,35 @@ python.sdk.new.project.environment.type=Environment type:
python.sdk.new.error.no.absolute=Path must be absolute
# Poetry package manager and SDK
python.sdk.poetry.executable.not.found=Poetry executable is not found
python.sdk.poetry.executable=Poetry executable:
python.sdk.poetry.select.executable.title=Select Path to Poetry Executable
python.sdk.poetry.associated.project=Associated project:
python.sdk.poetry.associated.module=Associated module:
python.sdk.poetry.execution.exception.no.project.message=Cannot find the project associated with this Poetry environment
python.sdk.poetry.execution.exception.no.poetry.message=Cannot find Poetry
python.sdk.poetry.quickfix.fix.pipenv.name=Fix Poetry interpreter
python.sdk.poetry.quickfix.use.pipenv.name=Use Poetry interpreter
python.sdk.poetry.pip.file.lock.not.found=poetry.lock is not found
python.sdk.poetry.pip.file.lock.out.of.date=poetry.lock is out of date
python.sdk.poetry.pip.file.notification.content=Run <a href='#lock'>poetry lock</a> or <a href='#update'>poetry update</a>
python.sdk.poetry.pip.file.notification.content.without.updating=Run <a href='#lock'>poetry lock</a>, <a href='#noupdate'>poetry lock --no-update</a> or <a href='#update'>poetry update</a>
python.sdk.poetry.pip.file.watcher=pyproject.toml Watcher
python.sdk.dialog.message.creating.virtual.environments.based.on.poetry.environments.not.supported=Creating virtual environments based on Poetry environments is not supported
python.sdk.dialog.title.setting.up.poetry.environment=Setting up poetry environment
python.sdk.intention.family.name.install.requirements.from.poetry.lock=Install requirements from poetry.lock
python.sdk.inspection.message.version.outdated.latest=''{0}'' version {1} is outdated (latest: {2})
python.sdk.dialog.message.cannot.find.script.file.please.run.poetry.install.before.executing.scripts=Cannot find a script file\nPlease run 'poetry install' before executing scripts
python.sdk.dialog.title.poetry.scripts=Poetry Scripts
python.sdk.poetry.action.run.script.text=Run ''{0}''
python.sdk.inspection.message.poetry.interpreter.associated.with.another.project=Poetry interpreter is associated with another {0}: {1}
python.sdk.inspection.message.poetry.interpreter.not.associated.with.any.project=Poetry interpreter is not associated with any {0}
python.sdk.dialog.message.creating.virtual.environments.based.on.poetry.environments.not.supported=Creating virtual environments based on Poetry environments is not supported
python.sdk.poetry.environment.panel.title=Poetry Environment
python.sdk.poetry.install.packages.from.toml.checkbox.text=Install packages from pyproject.toml
python.sdk.poetry.dialog.message.poetry.interpreter.has.been.already.added=Poetry interpreter has been already added, select ''{0}''
python.sdk.inspection.message.version.outdated.latest=''{0}'' version {1} is outdated (latest: {2})
python.sdk.intention.family.name.install.requirements.from.poetry.lock=Install requirements from poetry.lock
python.sdk.poetry.associated.module=Associated module:
python.sdk.poetry.associated.project=Associated project:
python.sdk.poetry.dialog.add.new.environment.in.project.checkbox=Create an in-project environment
python.sdk.poetry.environment.panel.title=Poetry Environment
python.sdk.poetry.executable.not.found=Poetry executable is not found
python.sdk.poetry.executable=Poetry executable:
python.sdk.poetry.detecting.environments=Detecting poetry environments
python.sdk.poetry.execution.exception.no.poetry.message=Cannot find Poetry
python.sdk.poetry.install.packages.from.toml.checkbox.text=Install packages from pyproject.toml
python.sdk.poetry.pip.file.lock.not.found=poetry.lock is not found
python.sdk.poetry.pip.file.lock.out.of.date=poetry.lock is out of date
python.sdk.poetry.pip.file.notification.content.without.updating=Run <a href='#lock'>poetry lock</a>, <a href='#noupdate'>poetry lock --no-update</a> or <a href='#update'>poetry update</a>
python.sdk.poetry.pip.file.notification.content=Run <a href='#lock'>poetry lock</a> or <a href='#update'>poetry update</a>
python.sdk.poetry.pip.file.notification.locking.without.updating=Locking poetry.lock without updating
python.sdk.poetry.pip.file.notification.locking=Locking poetry.lock
python.sdk.poetry.pip.file.notification.updating=Updating Poetry environment
python.sdk.poetry.pip.file.watcher=pyproject.toml Watcher
python.sdk.poetry.quickfix.use.pipenv.name=Use Poetry interpreter
python.sdk.poetry.select.executable.title=Select Path to Poetry Executable
python.sdk.progress.setting.up.environment=Setting up {0} environment
# UV
python.sdk.dialog.message.creating.virtual.environments.based.on.uv.environments.not.supported=Creating virtual environments based on uv environments is not supported
python.sdk.uv.setting.up.uv.environment=Setting up uv environment
python.sdk.dialog.title.setting.up.uv.environment=Setting up uv environment
python.sdk.inspection.message.uv.interpreter.associated.with.another.project=uv interpreter is associated with another {0}: {1}
python.sdk.inspection.message.uv.interpreter.not.associated.with.any.project=uv interpreter is not associated with any {0}

View File

@@ -45,13 +45,26 @@ fun Sdk.getOrCreateAdditionalData(): PythonSdkAdditionalData {
return newData
}
@Internal
/**
* Saves SDK to the project table if there is no sdk with same name
*/
@Internal
suspend fun Sdk.persist(): Unit = edtWriteAction {
if (ProjectJdkTable.getInstance().findJdk(name) == null) { // Saving 2 SDKs with same name is an error
getOrCreateAdditionalData() // additional data is always required
ProjectJdkTable.getInstance().addJdk(this)
}
}
@Internal
fun Sdk.persistSync() {
ApplicationManager.getApplication().invokeAndWait {
ApplicationManager.getApplication().runWriteAction {
if (ProjectJdkTable.getInstance().findJdk(name) == null) { // Saving 2 SDKs with same name is an error
getOrCreateAdditionalData() // additional data is always required
ProjectJdkTable.getInstance().addJdk(this)
}
}
}
}

View File

@@ -11,6 +11,8 @@ import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.common.io.Resources
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.application.writeAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.diagnostic.logger
@@ -191,6 +193,28 @@ object SdksKeeper {
private fun load() = configUrl?.let { Resources.toString(it, StandardCharsets.UTF_8) }
}
// Non suspend version is required for all cases where it is possible to call it from quick-fixes,
// as right now calling pyModuleBlocking from quick-fix is not allowed in EDT.
@ApiStatus.Internal
fun Sdk.setAssociationToModuleAsync(module: Module) {
val path = module.basePath
assert(path != null) { "Module $module has not paths, and can't be associated" }
val data = getOrCreateAdditionalData()
.also {
it.associatedModulePath = path
}
val modificator = sdkModificator
modificator.sdkAdditionalData = data
runInEdt {
ApplicationManager.getApplication().runWriteAction {
modificator.commitChanges()
}
}
}
@ApiStatus.Internal
suspend fun Sdk.setAssociationToModule(module: Module) {
val path = module.basePath
@@ -200,12 +224,10 @@ suspend fun Sdk.setAssociationToModule(module: Module) {
@ApiStatus.Internal
suspend fun Sdk.setAssociationToPath(path: String?) {
val data = getOrCreateAdditionalData().also {
when {
path != null -> it.associatedModulePath = path
else -> it.associatedModulePath = null
val data = getOrCreateAdditionalData()
.also {
it.associatedModulePath = path
}
}
val modificator = sdkModificator
modificator.sdkAdditionalData = data
@@ -213,4 +235,4 @@ suspend fun Sdk.setAssociationToPath(path: String?) {
writeAction {
modificator.commitChanges()
}
}
}

View File

@@ -99,7 +99,7 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
/**
* Flavors that are aware of some system pythons must return them there.
*/
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
protected @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(final @Nullable Module module,
final @Nullable UserDataHolder context) {
return Collections.emptyList();
@@ -108,7 +108,7 @@ public abstract class PythonSdkFlavor<D extends PyFlavorData> {
/**
* On local targets some flavors could be detected. It returns a path to python interpreters for such cases.
*/
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
public final @NotNull Collection<@NotNull Path> suggestLocalHomePaths(final @Nullable Module module,
final @Nullable UserDataHolder context) {
return suggestLocalHomePathsImpl(module, context).stream().filter(path -> {

View File

@@ -249,7 +249,6 @@ fun showSdkExecutionException(sdk: Sdk?, e: ExecutionException, @NlsContexts.Dia
}
@Internal
fun Sdk.isAssociatedWithModule(module: Module?): Boolean {
val basePath = module?.basePath
val associatedPath = associatedModulePath
@@ -259,7 +258,6 @@ fun Sdk.isAssociatedWithModule(module: Module?): Boolean {
}
@Internal
fun Sdk.isAssociatedWithAnotherModule(module: Module?): Boolean {
val basePath = module?.basePath ?: return false
val associatedPath = associatedModulePath ?: return false
@@ -402,7 +400,7 @@ fun getInnerVirtualEnvRoot(sdk: Sdk): VirtualFile? {
}
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
internal fun suggestAssociatedSdkName(sdkHome: String, associatedPath: String?): String? {
// please don't forget to update com.jetbrains.python.inspections.PyInterpreterInspection.Visitor#getSuitableSdkFix
// after changing this method

View File

@@ -46,7 +46,7 @@ import javax.swing.JPanel
*
*/
class PyAddSdkDialog private constructor(
private val project: Project?,
private val project: Project,
private val module: Module?,
private val existingSdks: List<Sdk>,
) : DialogWrapper(project) {
@@ -340,7 +340,7 @@ class PyAddSdkDialog private constructor(
private const val WIZARD_CARD_PANE = "Wizard"
@JvmStatic
fun show(project: Project?, module: Module?, existingSdks: List<Sdk>, sdkAddedCallback: Consumer<Sdk?>) {
fun show(project: Project, module: Module?, existingSdks: List<Sdk>, sdkAddedCallback: Consumer<Sdk?>) {
val dialog = PyAddSdkDialog(project = project, module = module, existingSdks = existingSdks)
dialog.init()
@@ -354,7 +354,7 @@ class PyAddSdkDialog private constructor(
* `org.jetbrains.plugins.remote-run` plugin is disabled.
*/
private fun PyAddSdkProvider.safeCreateView(
project: Project?,
project: Project,
module: Module?,
existingSdks: List<Sdk>,
context: UserDataHolder,

View File

@@ -12,7 +12,7 @@ interface PyAddSdkProvider {
/**
* Returns [PyAddSdkView] if applicable.
*/
fun createView(project: Project?,
fun createView(project: Project,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,

View File

@@ -41,7 +41,7 @@ open class PyAddNewCondaEnvPanel(
private val project: Project?,
private val module: Module?,
private val existingSdks: List<Sdk>,
newProjectPath: String?,
newProjectPath: String?
) : PyAddNewEnvPanel(PythonInterpreterSelectionMode.BASE_CONDA) {
override val envName: String = "Conda"
override val panelName: String get() = PyBundle.message("python.add.sdk.panel.name.new.environment")
@@ -121,11 +121,9 @@ open class PyAddNewCondaEnvPanel(
val associatedPath = if (!shared) projectBasePath else null
val sdk = createSdkByGenerateTask(task, existingSdks, null, associatedPath, null)
if (!shared) {
pyMayBeModalBlocking {
when {
newProjectPath != null -> sdk.setAssociationToPath(newProjectPath)
module != null -> sdk.setAssociationToModule(module)
}
when {
newProjectPath != null -> pyMayBeModalBlocking { sdk.setAssociationToPath(newProjectPath) }
module != null -> sdk.setAssociationToModuleAsync(module)
}
}
@@ -136,11 +134,11 @@ open class PyAddNewCondaEnvPanel(
}
override fun getStatisticInfo(): InterpreterStatisticsInfo? {
return InterpreterStatisticsInfo(InterpreterType.CONDAVENV,
InterpreterTarget.LOCAL,
false,
makeSharedField.isSelected,
false)
return InterpreterStatisticsInfo(InterpreterType.CONDAVENV,
InterpreterTarget.LOCAL,
false,
makeSharedField.isSelected,
false)
}
override fun addChangeListener(listener: Runnable) {

View File

@@ -16,7 +16,7 @@ import javax.swing.JComponent
/**
* Use [PyAddTargetBasedSdkDialog.Companion.show] to instantiate and show the dialog.
*/
class PyAddTargetBasedSdkDialog private constructor(private val project: Project?,
class PyAddTargetBasedSdkDialog private constructor(private val project: Project,
private val module: Module?,
private val existingSdks: List<Sdk>,
private val targetEnvironmentConfiguration: TargetEnvironmentConfiguration?)
@@ -58,7 +58,7 @@ class PyAddTargetBasedSdkDialog private constructor(private val project: Project
* target.
*/
@JvmStatic
fun show(project: Project?,
fun show(project: Project,
module: Module?,
existingSdks: List<Sdk>,
sdkAddedCallback: Consumer<Sdk?>,

View File

@@ -45,7 +45,7 @@ import javax.swing.JPanel
* The panel that is supposed to be used both for local and non-local target-based versions of "New Interpreter" dialog.
*/
internal class PyAddTargetBasedSdkPanel(
private val project: Project?,
private val project: Project,
private val module: Module?,
private val existingSdks: List<Sdk>,
private val targetSupplier: Supplier<TargetEnvironmentConfiguration>?,

View File

@@ -151,7 +151,7 @@ internal fun createSdkForTarget(
if (PythonInterpreterTargetEnvironmentFactory.by(environmentConfiguration)?.needAssociateWithModule() == true) {
// FIXME: multi module project support
project?.modules?.firstOrNull()?.let {
pyModalBlocking { sdk.setAssociationToModule(it) }
sdk.setAssociationToModuleAsync(it)
}
}

View File

@@ -48,7 +48,7 @@ public final class MacPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empt
return PyFlavorData.Empty.class;
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
@Override
protected @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(@Nullable Module module, @Nullable UserDataHolder context) {
Set<Path> candidates = new HashSet<>();

View File

@@ -45,7 +45,7 @@ public final class UnixPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Emp
return PyFlavorData.Empty.class;
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
@Override
protected @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(@Nullable Module module, @Nullable UserDataHolder context) {
return getDefaultUnixPythons();

View File

@@ -46,7 +46,7 @@ public final class VirtualEnvSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Emp
return PyFlavorData.Empty.class;
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
@Override
protected @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(@Nullable Module module, @Nullable UserDataHolder context) {
return ReadAction.compute(() -> {

View File

@@ -85,7 +85,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
return PyFlavorData.Empty.class;
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
@Override
protected final @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(final @Nullable Module module,
final @Nullable UserDataHolder context) {
@@ -95,7 +95,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor<PyFlavorData.Empty> {
return ContainerUtil.map(candidates, Path::of);
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
private void findInCandidatePaths(Set<String> candidates, String... exe_names) {
@SuppressWarnings("TestOnlyProblems")
var root = System.getProperty(ROOT_TO_SEARCH_PYTHON_IN, "C:\\");

View File

@@ -58,7 +58,7 @@ public final class CondaEnvSdkFlavor extends CPythonSdkFlavor<PyCondaFlavorData>
return PyCondaFlavorData.class;
}
@RequiresBackgroundThread
@RequiresBackgroundThread(generateAssertion = false)
@Override
protected @NotNull Collection<@NotNull Path> suggestLocalHomePathsImpl(@Nullable Module module, @Nullable UserDataHolder context) {
// There is no such thing as "conda homepath" since conda doesn't store python path

View File

@@ -9,10 +9,12 @@ import com.jetbrains.python.sdk.add.PyAddSdkProvider
import com.jetbrains.python.sdk.pipenv.ui.PyAddPipEnvPanel
class PyAddPipEnvSdkProvider : PyAddSdkProvider {
override fun createView(project: Project?,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,
context: UserDataHolder) =
override fun createView(
project: Project,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,
context: UserDataHolder
) =
PyAddPipEnvPanel(project, module, existingSdks, newProjectPath, context)
}

View File

@@ -7,8 +7,7 @@ import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.jetbrains.python.PyBundle
import com.jetbrains.python.sdk.pythonSdk
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.ui.pyModalBlocking
import com.jetbrains.python.sdk.setAssociationToModuleAsync
/**
* A quick-fix for setting up the pipenv for the module of the current PSI element.
@@ -18,9 +17,9 @@ class PipEnvAssociationQuickFix : LocalQuickFix {
override fun getFamilyName() = quickFixName
override fun applyFix(project: Project, descriptor: ProblemDescriptor): Unit = pyModalBlocking {
val element = descriptor.psiElement ?: return@pyModalBlocking
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return@pyModalBlocking
module.pythonSdk?.setAssociationToModule(module)
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement ?: return
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return
module.pythonSdk?.setAssociationToModuleAsync(module)
}
}

View File

@@ -6,18 +6,18 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.util.UserDataHolder
import com.jetbrains.python.sdk.add.PyAddSdkProvider
import com.jetbrains.python.sdk.add.PyAddSdkView
import com.jetbrains.python.sdk.poetry.ui.createPoetryPanel
/**
* This source code is edited by @koxudaxi Koudai Aono <koxudaxi@gmail.com>
*/
class PyAddPoetrySdkProvider : PyAddSdkProvider {
override fun createView(project: Project?,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,
context: UserDataHolder) =
createPoetryPanel(project, module, existingSdks, newProjectPath, context)
override fun createView(
project: Project,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,
context: UserDataHolder
): PyAddSdkView? {
return createPoetryPanel(project, module, existingSdks, newProjectPath, context)
}
}

View File

@@ -7,8 +7,7 @@ import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.jetbrains.python.PyBundle
import com.jetbrains.python.sdk.pythonSdk
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.ui.pyModalBlocking
import com.jetbrains.python.sdk.setAssociationToModuleAsync
/**
* A quick-fix for setting up the poetry for the module of the current PSI element.
@@ -18,9 +17,9 @@ class PoetryAssociationQuickFix: LocalQuickFix {
override fun getFamilyName() = quickFixName
override fun applyFix(project: Project, descriptor: ProblemDescriptor): Unit = pyModalBlocking {
val element = descriptor.psiElement ?: return@pyModalBlocking
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return@pyModalBlocking
module.pythonSdk?.setAssociationToModule(module)
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement ?: return
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return
module.pythonSdk?.setAssociationToModuleAsync(module)
}
}

View File

@@ -16,7 +16,7 @@ import com.jetbrains.python.ui.pyModalBlocking
import java.util.function.Supplier
fun createPoetryPanel(
project: Project?,
project: Project,
module: Module?,
existingSdks: List<Sdk>,
newProjectPath: String?,

View File

@@ -9,19 +9,20 @@ import com.intellij.util.ui.FormBuilder
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PySdkBundle
import com.jetbrains.python.sdk.*
import com.jetbrains.python.sdk.add.PyAddSdkView
import com.jetbrains.python.sdk.add.PySdkPathChoosingComboBox
import com.jetbrains.python.sdk.add.PyAddSdkPanel
import com.jetbrains.python.sdk.add.addInterpretersAsync
import com.jetbrains.python.sdk.poetry.*
import com.jetbrains.python.ui.pyMayBeModalBlocking
import com.jetbrains.python.ui.pyModalBlocking
import com.jetbrains.python.util.runWithModalBlockingOrInBackground
import java.awt.BorderLayout
import java.util.concurrent.ConcurrentHashMap
import javax.swing.Icon
class PyAddExistingPoetryEnvPanel(
private val project: Project?,
private val project: Project,
private val module: Module?,
private val existingSdks: List<Sdk>,
override var newProjectPath: String?,
@@ -41,9 +42,8 @@ class PyAddExistingPoetryEnvPanel(
add(formPanel, BorderLayout.NORTH)
addInterpretersAsync(sdkComboBox) {
val existingSdkPaths = sdkHomes(existingSdks)
val moduleSdks = allModules(project).parallelStream().flatMap { module ->
val sdks = pyModalBlocking {
val sdks = pyMayBeModalBlocking {
detectPoetryEnvs(module, existingSdkPaths, module.basePath)
}.filterNot { it.isAssociatedWithAnotherModule(module) }
@@ -51,8 +51,8 @@ class PyAddExistingPoetryEnvPanel(
sdks.stream()
}.toList()
val rootSdks = pyModalBlocking {
detectPoetryEnvs(module, existingSdkPaths, project?.basePath ?: newProjectPath)
val rootSdks = pyMayBeModalBlocking {
detectPoetryEnvs(module, existingSdkPaths, project.basePath ?: newProjectPath)
}.filterNot { it.isAssociatedWithAnotherModule(module) }
val moduleSdkPaths = moduleSdks.map { it.name }.toSet()
@@ -63,14 +63,15 @@ class PyAddExistingPoetryEnvPanel(
}
override fun validateAll(): List<ValidationInfo> {
return listOfNotNull(validateSdkComboBox(sdkComboBox, this))
return emptyList()
}
override fun getOrCreateSdk(): Sdk? {
return when (val sdk = sdkComboBox.selectedSdk) {
is PyDetectedSdk -> {
val mappedModule = sdkToModule[sdk.name] ?: module
pyModalBlocking {
runWithModalBlockingOrInBackground(project, msg = PyBundle.message("python.sdk.dialog.title.setting.up.poetry.environment")) {
setupPoetrySdkUnderProgress(project, mappedModule, existingSdks, newProjectPath,
getPythonExecutable(sdk.name), false, sdk.name).onSuccess {
PySdkSettings.instance.preferredVirtualEnvBaseSdk = getPythonExecutable(sdk.name)
@@ -80,12 +81,4 @@ class PyAddExistingPoetryEnvPanel(
else -> sdk
}
}
// FIXME: @Egor
fun validateSdkComboBox(field: PySdkPathChoosingComboBox, view: PyAddSdkView): ValidationInfo? {
return when (val sdk = field.selectedSdk) {
null -> ValidationInfo(PySdkBundle.message("python.sdk.field.is.empty"), field)
else -> null
}
}
}

View File

@@ -10,26 +10,25 @@ import com.jetbrains.python.PyBundle
import com.jetbrains.python.inspections.requirement.RunningPackagingTasksListener
import com.jetbrains.python.packaging.PyPackageManagerUI
import com.jetbrains.python.sdk.pythonSdk
import com.jetbrains.python.sdk.setAssociationToModule
import com.jetbrains.python.ui.pyModalBlocking
import com.jetbrains.python.sdk.setAssociationToModuleAsync
internal class UvAssociationQuickFix : LocalQuickFix {
private val quickFixName = PyBundle.message("python.sdk.quickfix.use.uv.name")
override fun getFamilyName() = quickFixName
override fun applyFix(project: Project, descriptor: ProblemDescriptor): Unit = pyModalBlocking {
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement
if (element == null) {
return@pyModalBlocking
return
}
val module = ModuleUtilCore.findModuleForPsiElement(element)
if (module == null) {
return@pyModalBlocking
return
}
module.pythonSdk?.setAssociationToModule(module)
module.pythonSdk?.setAssociationToModuleAsync(module)
}
}
@@ -47,7 +46,9 @@ class UvInstallQuickFix : LocalQuickFix {
}
}
override fun getFamilyName() = PyBundle.message("python.sdk.intention.family.name.install.requirements.from.uv.lock")
override fun getFamilyName(): String {
return PyBundle.message("python.sdk.intention.family.name.install.requirements.from.uv.lock")
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement

View File

@@ -32,6 +32,7 @@ import com.jetbrains.python.sdk.PySdkSettings
import com.jetbrains.python.sdk.PythonSdkCoroutineService
import com.jetbrains.python.sdk.add.*
import com.jetbrains.python.sdk.basePath
import com.jetbrains.python.sdk.persistSync
import com.jetbrains.python.sdk.uv.UV_ICON
import com.jetbrains.python.sdk.uv.getPyProjectTomlForUv
import com.jetbrains.python.sdk.uv.impl.detectUvExecutable
@@ -41,6 +42,7 @@ import com.jetbrains.python.sdk.uv.setupNewUvSdkAndEnvUnderProgress
import com.jetbrains.python.sdk.uv.validateSdks
import com.jetbrains.python.statistics.InterpreterTarget
import com.jetbrains.python.statistics.InterpreterType
import com.jetbrains.python.util.runWithModalBlockingOrInBackground
import com.jetbrains.python.venvReader.tryResolvePath
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -177,11 +179,12 @@ class PyAddNewUvPanel(
setUvExecutable(it)
}
val sdk = runBlockingCancellable {
val sdk = runWithModalBlockingOrInBackground(project, PyBundle.message("python.sdk.uv.setting.up.uv.environment")) {
setupNewUvSdkAndEnvUnderProgress(project, path, existingSdks, python)
}
sdk.onSuccess {
sdk.onSuccess { sdk ->
sdk.persistSync();
PySdkSettings.instance.preferredVirtualEnvBaseSdk = baseSdkField.selectedSdk.homePath
}
@@ -240,7 +243,7 @@ class PyAddNewUvPanel(
class PyAddUvSdkProvider : PyAddSdkProvider {
override fun createView(
project: Project?,
project: Project,
module: Module?,
newProjectPath: String?,
existingSdks: List<Sdk>,

View File

@@ -8,7 +8,6 @@ import com.intellij.util.concurrency.annotations.RequiresBlockingContext
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.jetbrains.python.PySdkBundle
import kotlinx.coroutines.runBlocking
import org.jetbrains.annotations.ApiStatus
import javax.swing.SwingUtilities
/**
@@ -25,9 +24,7 @@ fun <T> pyModalBlocking(modalTaskOwner: ModalTaskOwner = ModalTaskOwner.guess(),
/**
* It is *not* recommended to use this function. Prefer suspend functions.
*/
@ApiStatus.Obsolete
@ApiStatus.Internal
fun <T> pyMayBeModalBlocking(modalTaskOwner: ModalTaskOwner = ModalTaskOwner.guess(), code: suspend () -> T): T =
internal fun <T> pyMayBeModalBlocking(modalTaskOwner: ModalTaskOwner = ModalTaskOwner.guess(), code: suspend () -> T): T =
if (SwingUtilities.isEventDispatchThread()) {
pyModalBlocking(modalTaskOwner, code)
}