summaryrefslogtreecommitdiff
path: root/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java')
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java367
1 files changed, 367 insertions, 0 deletions
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
new file mode 100644
index 000000000000..28dfa6c834a4
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.packaging;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Function;
+import com.intellij.webcore.packaging.PackagesNotificationPanel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.event.HyperlinkEvent;
+import java.util.*;
+
+/**
+* @author vlan
+*/
+public class PyPackageManagerUI {
+ private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class);
+ @Nullable private Listener myListener;
+ @NotNull private Project myProject;
+ @NotNull private Sdk mySdk;
+
+ public interface Listener {
+ void started();
+
+ void finished(List<PyExternalProcessException> exceptions);
+ }
+
+ public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) {
+ myProject = project;
+ mySdk = sdk;
+ myListener = listener;
+ }
+
+ public void installManagement() {
+ ProgressManager.getInstance().run(new InstallManagementTask(myProject, mySdk, myListener));
+ }
+
+ public void install(@NotNull final List<PyRequirement> requirements, @NotNull final List<String> extraArgs) {
+ ProgressManager.getInstance().run(new InstallTask(myProject, mySdk, requirements, extraArgs, myListener));
+ }
+
+ public void uninstall(@NotNull final List<PyPackage> packages) {
+ if (checkDependents(packages)) {
+ return;
+ }
+ ProgressManager.getInstance().run(new UninstallTask(myProject, mySdk, myListener, packages));
+ }
+
+ private boolean checkDependents(@NotNull final List<PyPackage> packages) {
+ try {
+ final Map<String, Set<PyPackage>> dependentPackages = collectDependents(packages, mySdk);
+ final int[] warning = {0};
+ if (!dependentPackages.isEmpty()) {
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (dependentPackages.size() == 1) {
+ String message = "You are attempting to uninstall ";
+ List<String> dep = new ArrayList<String>();
+ int size = 1;
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ final Set<PyPackage> value = entry.getValue();
+ size = value.size();
+ dep.add(entry.getKey() + " package which is required for " + StringUtil.join(value, ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += size == 1 ? " package" : " packages";
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ else {
+ String message = "You are attempting to uninstall packages which are required for another packages.\n\n";
+ List<String> dep = new ArrayList<String>();
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ dep.add(entry.getKey() + " -> " + StringUtil.join(entry.getValue(), ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ }
+ }, ModalityState.current());
+ }
+ if (warning[0] != Messages.YES) return true;
+ }
+ catch (PyExternalProcessException e) {
+ LOG.info("Error loading packages dependents: " + e.getMessage(), e);
+ }
+ return false;
+ }
+
+ private static Map<String, Set<PyPackage>> collectDependents(@NotNull final List<PyPackage> packages, Sdk sdk)
+ throws PyExternalProcessException {
+ Map<String, Set<PyPackage>> dependentPackages = new HashMap<String, Set<PyPackage>>();
+ for (PyPackage pkg : packages) {
+ final Set<PyPackage> dependents = PyPackageManager.getInstance(sdk).getDependents(pkg);
+ if (dependents != null && !dependents.isEmpty()) {
+ for (PyPackage dependent : dependents) {
+ if (!packages.contains(dependent)) {
+ dependentPackages.put(pkg.getName(), dependents);
+ }
+ }
+ }
+ }
+ return dependentPackages;
+ }
+
+ private abstract static class PackagingTask extends Task.Backgroundable {
+ private static final String PACKAGING_GROUP_ID = "Packaging";
+
+ @Nullable protected final Listener myListener;
+
+ public PackagingTask(@Nullable Project project, @NotNull String title, @Nullable Listener listener) {
+ super(project, title);
+ myListener = listener;
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ taskStarted(indicator);
+ taskFinished(runTask(indicator));
+ }
+
+ @NotNull
+ protected abstract List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator);
+
+ @NotNull
+ protected abstract String getSuccessTitle();
+
+ @NotNull
+ protected abstract String getSuccessDescription();
+
+ @NotNull
+ protected abstract String getFailureTitle();
+
+ protected void taskStarted(@NotNull ProgressIndicator indicator) {
+ indicator.setText(getTitle() + "...");
+ if (myListener != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myListener.started();
+ }
+ });
+ }
+ }
+
+ protected void taskFinished(@NotNull final List<PyExternalProcessException> exceptions) {
+ final Ref<Notification> notificationRef = new Ref<Notification>(null);
+ if (exceptions.isEmpty()) {
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(),
+ NotificationType.INFORMATION));
+ }
+ else {
+ final String firstLine = getTitle() + ": error occurred.";
+ final String description = createDescription(exceptions, firstLine);
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(),
+ firstLine + " <a href=\"xxx\">Details...</a>",
+ NotificationType.ERROR,
+ new NotificationListener() {
+ @Override
+ public void hyperlinkUpdate(@NotNull Notification notification,
+ @NotNull HyperlinkEvent event) {
+ assert myProject != null;
+ PackagesNotificationPanel.showError(myProject, getFailureTitle(), description);
+ }
+ }
+ ));
+ }
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (myListener != null) {
+ myListener.finished(exceptions);
+ }
+ final Notification notification = notificationRef.get();
+ if (notification != null) {
+ notification.notify(myProject);
+ }
+ }
+ });
+ }
+ }
+
+ private static class InstallTask extends PackagingTask {
+ @NotNull protected final Sdk mySdk;
+ @NotNull private final List<PyRequirement> myRequirements;
+ @NotNull private final List<String> myExtraArgs;
+
+ public InstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @NotNull List<PyRequirement> requirements,
+ @NotNull List<String> extraArgs,
+ @Nullable Listener listener) {
+ super(project, "Installing packages", listener);
+ mySdk = sdk;
+ myRequirements = requirements;
+ myExtraArgs = extraArgs;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final int size = myRequirements.size();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ for (int i = 0; i < size; i++) {
+ final PyRequirement requirement = myRequirements.get(i);
+ indicator.setText(String.format("Installing package '%s'...", requirement));
+ indicator.setFraction((double)i / size);
+ try {
+ manager.install(Arrays.asList(requirement), myExtraArgs);
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages installed successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed packages: " + PyPackageUtil.requirementsToString(myRequirements);
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Install packages failed";
+ }
+ }
+
+ private static class InstallManagementTask extends InstallTask {
+
+ public InstallManagementTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener) {
+ super(project, sdk, Collections.<PyRequirement>emptyList(), Collections.<String>emptyList(), listener);
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ indicator.setText("Installing packaging tools...");
+ indicator.setIndeterminate(true);
+ try {
+ manager.installManagement();
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed Python packaging tools";
+ }
+ }
+
+ private static class UninstallTask extends PackagingTask {
+ @NotNull private final Sdk mySdk;
+ @NotNull private final List<PyPackage> myPackages;
+
+ public UninstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener,
+ @NotNull List<PyPackage> packages) {
+ super(project, "Uninstalling packages", listener);
+ mySdk = sdk;
+ myPackages = packages;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ try {
+ manager.uninstall(myPackages);
+ return Arrays.asList();
+ }
+ catch (PyExternalProcessException e) {
+ return Arrays.asList(e);
+ }
+ finally {
+ manager.refresh();
+ }
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages uninstalled successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ final String packagesString = StringUtil.join(myPackages, new Function<PyPackage, String>() {
+ @Override
+ public String fun(PyPackage pkg) {
+ return "'" + pkg.getName() + "'";
+ }
+ }, ", ");
+ return "Uninstalled packages: " + packagesString;
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Uninstall packages failed";
+ }
+ }
+
+ public static String createDescription(List<PyExternalProcessException> exceptions, String firstLine) {
+ final StringBuilder b = new StringBuilder();
+ b.append(firstLine);
+ b.append("\n\n");
+ for (PyExternalProcessException exception : exceptions) {
+ b.append(exception.toString());
+ b.append("\n");
+ }
+ return b.toString();
+ }
+}