diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index 35ef43ea9320..523f99bb0f61 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -2,7 +2,6 @@ package com.jetbrains.python.packaging; import com.intellij.execution.ExecutionException; -import com.intellij.execution.RunCanceledByUserException; import com.intellij.ide.IdeBundle; import com.intellij.model.SideEffectGuard; import com.intellij.notification.Notification; @@ -260,43 +259,18 @@ public final class PyPackageManagerUI { @Override protected @NotNull List runTask(@NotNull ProgressIndicator indicator) { final List exceptions = new ArrayList<>(); - final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk); - if (myRequirements == null) { - indicator.setText(PyBundle.message("python.packaging.installing.packages")); - indicator.setIndeterminate(true); - try { - manager.install(null, myExtraArgs); - } - catch (ExecutionException e) { - exceptions.add(e); - } + + if (myProject == null) { + return exceptions; } - else { - final int size = myRequirements.size(); - for (int i = 0; i < size; i++) { - final PyRequirement requirement = myRequirements.get(i); - indicator.setText(PyBundle.message("python.packaging.progress.text.installing.specific.package", - requirement.getPresentableText())); - if (i == 0) { - indicator.setIndeterminate(true); - } - else { - indicator.setIndeterminate(false); - indicator.setFraction((double)i / size); - } - try { - manager.install(Collections.singletonList(requirement), myExtraArgs); - } - catch (RunCanceledByUserException e) { - exceptions.add(e); - break; - } - catch (ExecutionException e) { - exceptions.add(e); - } - } - } - manager.refresh(); + + PythonPackagesInstallerAsync.Companion.installPackages( + myProject, + myRequirements, + myExtraArgs, + indicator + ); + return exceptions; } diff --git a/python/src/com/jetbrains/python/packaging/management/PythonPackageInstallerAsync.kt b/python/src/com/jetbrains/python/packaging/management/PythonPackageInstallerAsync.kt new file mode 100644 index 000000000000..38c60681b75a --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/management/PythonPackageInstallerAsync.kt @@ -0,0 +1,76 @@ +// 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.packaging + +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.project.Project +import com.jetbrains.python.PyBundle +import com.jetbrains.python.packaging.common.PythonPackageSpecification +import com.jetbrains.python.packaging.common.PythonPackageSpecificationBase +import com.jetbrains.python.packaging.common.PythonSimplePackageSpecification +import com.jetbrains.python.packaging.toolwindow.PyPackagingToolWindowService +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class PythonPackagesInstallerAsync { + + companion object { + fun installPackages( + project: Project, + requirements: List?, + extraArgs: List, + indicator: ProgressIndicator, + ) { + val packageService = PyPackagingToolWindowService.getInstance(project) + + packageService.serviceScope.launch(Dispatchers.IO) { + if (requirements.isNullOrEmpty()) { + installWithoutRequirements(packageService, extraArgs, indicator) + } + else { + installWithRequirements(packageService, requirements, extraArgs, indicator) + } + } + } + + private suspend fun installWithoutRequirements( + packageService: PyPackagingToolWindowService, + extraArgs: List, + indicator: ProgressIndicator, + ) { + indicator.text = PyBundle.message("python.packaging.installing.packages") + indicator.isIndeterminate = true + + val emptySpecification = PythonPackageSpecificationBase("", null, null, null) + packageService.installPackage(emptySpecification, extraArgs) + } + + private suspend fun installWithRequirements( + packageService: PyPackagingToolWindowService, + requirements: List, + extraArgs: List, + indicator: ProgressIndicator, + ) { + requirements.forEachIndexed { index, requirement -> + indicator.text = PyBundle.message("python.packaging.progress.text.installing.specific.package", requirement.presentableText) + updateProgress(indicator, index, requirements.size) + + val specification = createSpecificationForRequirement(requirement) + packageService.installPackage(specification, extraArgs) + } + } + + private fun updateProgress(indicator: ProgressIndicator, index: Int, total: Int) { + indicator.isIndeterminate = index == 0 + if (total > 0) { + indicator.fraction = index.toDouble() / total + } + } + + private fun createSpecificationForRequirement( + requirement: PyRequirement, + ): PythonPackageSpecification { + val packageName = requirement.name + return PythonSimplePackageSpecification(packageName, null, null) + } + } +}