mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[python] Convert "install package in background" tools to futures.
fixup! Fix after review: use another `install` signature [python] Convert "install package in background" tools to futures. For things like PY-64402 we might need lots of packages to be installed in the background. This change substitutes callback spaghetti with `Future` that could be used for `suspend` function using `await`. It introduces "facade" named `FrameworkInstaller` to do that. Unlike callbacks that weren't called in case of error, `Future` completes with error, and thrown exception caught and logged by IJ platform. `DsKnownPackages` enum is also added not to hardcode string literals Merge-request: IJ-MR-129535 Merged-by: Ilya Kazakevich <ilya.kazakevich@jetbrains.com> GitOrigin-RevId: 1e1a0121972119d36cd3bb53e876b3af86c09f8c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b39dd25583
commit
bea035291c
@@ -0,0 +1,53 @@
|
||||
// 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.newProject
|
||||
|
||||
import com.intellij.openapi.application.EDT
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.asContextElement
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.util.io.await
|
||||
import com.jetbrains.python.newProject.FrameworkInstaller.Companion.installFrameworksInBackground
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
|
||||
/**
|
||||
* `suspend`-friendly frontend for [PythonProjectGenerator]
|
||||
* Installs frameworks in the background, shows errors as messages.
|
||||
* Use [installFrameworksInBackground]
|
||||
*/
|
||||
@Service(Service.Level.PROJECT)
|
||||
@ApiStatus.Internal
|
||||
class FrameworkInstaller private constructor(private val projectScope: CoroutineScope) {
|
||||
companion object {
|
||||
private val log = Logger.getInstance(FrameworkInstaller::class.java)
|
||||
|
||||
/**
|
||||
* Install python [frameworks] (as in `requirements.txt`) on [sdk] as background a task.
|
||||
* `null` in [sdk] will lead to an error displayed in UI (for ar backward compatibility).
|
||||
*
|
||||
* This function is a wrapper for [PythonProjectGenerator.installFrameworkInBackground]
|
||||
*/
|
||||
fun installFrameworksInBackground(project: Project, sdk: Sdk?, vararg frameworks: @NonNls String) {
|
||||
val taskName = "Python framework installer for ${frameworks.joinToString(",")}"
|
||||
log.info("Started $taskName")
|
||||
// Do not wait for a modal dialog to close
|
||||
val context = Dispatchers.EDT + ModalityState.current().asContextElement() + CoroutineName(taskName)
|
||||
project.service<FrameworkInstaller>().projectScope.launch(context) {
|
||||
for (framework in frameworks) {
|
||||
log.info("installing $framework")
|
||||
PythonProjectGenerator.installFrameworkInBackground(project, framework, framework, sdk, true, null).await()
|
||||
log.info("$framework installed")
|
||||
}
|
||||
log.info("Completed $taskName")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,8 @@ import java.awt.event.MouseListener;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
@@ -360,36 +362,40 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
|
||||
* @param requirement name of requirement to install (i.e. "django")
|
||||
* @param forceInstallFramework pass true if you are sure required framework is missing
|
||||
* @param callback to be called after installation (or instead of is framework is installed) on AWT thread
|
||||
* @return future to be used instead of callback.
|
||||
*/
|
||||
public static void installFrameworkIfNeeded(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
@Nullable final Runnable callback) {
|
||||
installFrameworkIfNeeded(project, frameworkName, requirement, sdk, forceInstallFramework, false, callback);
|
||||
public static @NotNull Future<Void> installFrameworkIfNeeded(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
@Nullable final Runnable callback) {
|
||||
return installFrameworkIfNeeded(project, frameworkName, requirement, sdk, forceInstallFramework, false, callback);
|
||||
}
|
||||
|
||||
public static void installFrameworkInBackground(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
@Nullable final Runnable callback) {
|
||||
installFrameworkIfNeeded(project, frameworkName, requirement, sdk, forceInstallFramework, true, callback);
|
||||
public static @NotNull Future<Void> installFrameworkInBackground(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
@Nullable final Runnable callback) {
|
||||
return installFrameworkIfNeeded(project, frameworkName, requirement, sdk, forceInstallFramework, true, callback);
|
||||
}
|
||||
|
||||
private static void installFrameworkIfNeeded(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
boolean asBackgroundTask,
|
||||
@Nullable final Runnable callback) {
|
||||
@NotNull
|
||||
private static Future<Void> installFrameworkIfNeeded(@NotNull final Project project,
|
||||
@NotNull final String frameworkName,
|
||||
@NotNull final String requirement,
|
||||
@Nullable final Sdk sdk,
|
||||
final boolean forceInstallFramework,
|
||||
boolean asBackgroundTask,
|
||||
@Nullable final Runnable callback) {
|
||||
|
||||
var future = new CompletableFuture<Void>();
|
||||
if (sdk == null) {
|
||||
reportPackageInstallationFailure(frameworkName, null);
|
||||
return;
|
||||
future.completeExceptionally(new RuntimeException(("No SDK provided")));
|
||||
return future;
|
||||
}
|
||||
|
||||
// For remote SDK we are not sure if framework exists or not, so we'll check it anyway
|
||||
@@ -403,8 +409,14 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
|
||||
installPackages(frameworkName, forceInstallFramework, indicator, requirement, sdk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThrowable(@NotNull Throwable error) {
|
||||
future.completeExceptionally(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
future.complete(null);
|
||||
// Installed / checked successfully, call callback on AWT
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
@@ -420,8 +432,14 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
|
||||
installPackages(frameworkName, forceInstallFramework, indicator, requirement, sdk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThrowable(@NotNull Throwable error) {
|
||||
future.completeExceptionally(error);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
future.complete(null);
|
||||
// Installed / checked successfully, call callback on AWT
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
@@ -431,12 +449,14 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
|
||||
}
|
||||
}
|
||||
else {
|
||||
future.complete(null);
|
||||
// No need to install, but still need to call callback on AWT
|
||||
if (callback != null) {
|
||||
assert SwingUtilities.isEventDispatchThread();
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user