PY-50160 Implement the redesigned dialog for package installation failures

IJ-CR-13328

GitOrigin-RevId: 2cfa6c468411391772639441f0d2aeda4fddc435
This commit is contained in:
Lada Gagina
2021-08-11 17:39:25 +03:00
committed by intellij-monorepo-bot
parent 590618ec71
commit 18d0c28655
14 changed files with 467 additions and 74 deletions

View File

@@ -175,7 +175,12 @@ public class InstalledPackagesPanel extends JPanel {
myPackagesTable.clearSelection();
doUpdatePackages(myPackageManagementService);
}
});
}, createNotificationPanel());
}
@NotNull
protected PackagesNotificationPanel createNotificationPanel() {
return new PackagesNotificationPanel();
}
private void upgradeAction() {

View File

@@ -78,6 +78,12 @@ public class ManagePackagesDialog extends DialogWrapper {
public ManagePackagesDialog(@NotNull Project project, final PackageManagementService packageManagementService,
@Nullable final PackageManagementService.Listener packageListener) {
this(project, packageManagementService, packageListener, new PackagesNotificationPanel());
}
public ManagePackagesDialog(@NotNull Project project, final PackageManagementService packageManagementService,
@Nullable final PackageManagementService.Listener packageListener,
@NotNull final PackagesNotificationPanel notificationPanel) {
super(project, true);
myProject = project;
myController = packageManagementService;
@@ -86,7 +92,7 @@ public class ManagePackagesDialog extends DialogWrapper {
init();
setTitle(IdeBundle.message("available.packages.dialog.title"));
myPackages = new JBList();
myNotificationArea = new PackagesNotificationPanel();
myNotificationArea = notificationPanel;
myNotificationsAreaPlaceholder.add(myNotificationArea.getComponent(), BorderLayout.CENTER);
final AnActionButton reloadButton =

View File

@@ -16,6 +16,7 @@ import javax.swing.event.HyperlinkEvent;
import java.awt.*;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
public class PackagesNotificationPanel {
@@ -25,6 +26,10 @@ public class PackagesNotificationPanel {
private PackageManagementService.ErrorDescription myErrorDescription;
public PackagesNotificationPanel() {
this(PackagesNotificationPanel::showError);
}
public PackagesNotificationPanel(@NotNull BiConsumer<String, PackageManagementService.ErrorDescription> showErrorFunction) {
myHtmlViewer = SwingHelper.createHtmlViewer(true, null, null, null);
myHtmlViewer.setVisible(false);
myHtmlViewer.setOpaque(true);
@@ -36,7 +41,7 @@ public class PackagesNotificationPanel {
handler.run();
}
else if (myErrorTitle != null && myErrorDescription != null) {
showError(myErrorTitle, myErrorDescription);
showErrorFunction.accept(myErrorTitle, myErrorDescription);
}
}
});

View File

@@ -35,7 +35,7 @@ public class PyManagePackagesDialog extends DialogWrapper {
final JComboBox sdkComboBox = new JComboBox(new CollectionComboBoxModel(sdks, sdk));
sdkComboBox.setRenderer(new PySdkListCellRenderer());
PackagesNotificationPanel notificationPanel = new PackagesNotificationPanel();
PackagesNotificationPanel notificationPanel = new PyPackagesNotificationPanel();
final PyInstalledPackagesPanel packagesPanel = new PyInstalledPackagesPanel(project, notificationPanel);
packagesPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0));
packagesPanel.updatePackages(PyPackageManagers.getInstance().getManagementService(project, sdk));

View File

@@ -9,6 +9,7 @@ import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.PyPackageManagers;
import com.jetbrains.python.packaging.PyPackagesNotificationPanel;
import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
@@ -45,7 +46,7 @@ public class PythonSdkEditorAdditionalOptionsProvider extends SdkEditorAdditiona
@Nullable
@Override
public JComponent createComponent() {
final PackagesNotificationPanel notificationsArea = new PackagesNotificationPanel();
final PackagesNotificationPanel notificationsArea = new PyPackagesNotificationPanel();
final JComponent notificationsComponent = notificationsArea.getComponent();
JPanel panel = new JPanel(new BorderLayout());

View File

@@ -18,6 +18,7 @@ python.sdk.packaging.package.management.for.python.not.supported=Package managem
python.sdk.packaging.invalid.output.format=Invalid output format
python.sdk.packaging.tools.not.found=Python packaging tools not found
python.sdk.packaging.enter.your.password.to.make.changes=Please enter your password to make changes in system packages:
python.sdk.packaging.unknown.package.data=Unknown
python.sdk.use.python.version.supported.by.this.package=Make sure that you use a version of Python supported by this package. Currently, you are using Python {0}.
python.sdk.check.python.development.packages.installed=Make sure that you have installed Python development packages for your operating system.
python.sdk.try.to.run.command.from.system.terminal=Try to run this command from the system terminal. Make sure that you use the correct version of ''pip'' installed for your Python interpreter located at ''{0}''.

View File

@@ -47,6 +47,7 @@ import com.intellij.util.ui.JBUI;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.packaging.PyPackageManagers;
import com.jetbrains.python.packaging.PyPackagesNotificationPanel;
import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel;
import com.jetbrains.python.sdk.*;
import org.jetbrains.annotations.NotNull;
@@ -104,7 +105,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable {
mySdkCombo = buildSdkComboBox(this::onShowAllSelected, this::onSdkSelected);
final PackagesNotificationPanel packagesNotificationPanel = new PackagesNotificationPanel();
final PackagesNotificationPanel packagesNotificationPanel = new PyPackagesNotificationPanel();
myPackagesPanel = new PyInstalledPackagesPanel(myProject, packagesNotificationPanel);
myPackagesPanel.setShowGrid(false);

View File

@@ -36,18 +36,13 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGeneratorBase;
import com.intellij.util.BooleanFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.webcore.packaging.PackageManagementService.ErrorDescription;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyPsiPackageUtil;
import com.jetbrains.python.packaging.PyPackage;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.packaging.PyPackageUtil;
import com.jetbrains.python.packaging.*;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.remote.*;
import com.jetbrains.python.sdk.PyLazySdk;
import com.jetbrains.python.sdk.PythonSdkUtil;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -298,25 +293,31 @@ public abstract class PythonProjectGenerator<T extends PyNewProjectSettings> ext
protected static void reportPackageInstallationFailure(@NotNull final String frameworkName,
@Nullable final Pair<Sdk, ExecutionException> sdkAndException) {
final ErrorDescription errorDescription = getErrorDescription(sdkAndException);
final PyPackageManagementService.PyPackageInstallationErrorDescription errorDescription =
getErrorDescription(sdkAndException, frameworkName);
final Application app = ApplicationManager.getApplication();
app.invokeLater(() -> PackagesNotificationPanel.showError(PyBundle.message("python.new.project.install.failed.title", frameworkName),
errorDescription));
app.invokeLater(() -> {
PyPackagesNotificationPanel.showPackageInstallationError(PyBundle.message("python.new.project.install.failed.title", frameworkName),
errorDescription);
});
}
@NotNull
private static ErrorDescription getErrorDescription(@Nullable final Pair<Sdk, ExecutionException> sdkAndException) {
ErrorDescription errorDescription = null;
private static PyPackageManagementService.PyPackageInstallationErrorDescription getErrorDescription(@Nullable final Pair<Sdk, ExecutionException> sdkAndException,
@NotNull String packageName) {
PyPackageManagementService.PyPackageInstallationErrorDescription errorDescription = null;
if (sdkAndException != null) {
final ExecutionException exception = sdkAndException.second;
errorDescription = PyPackageManagementService.toErrorDescription(Collections.singletonList(exception), sdkAndException.first);
errorDescription =
PyPackageManagementService.toErrorDescription(Collections.singletonList(exception), sdkAndException.first, packageName);
if (errorDescription == null) {
errorDescription = ErrorDescription.fromMessage(exception.getMessage());
errorDescription = PyPackageManagementService.PyPackageInstallationErrorDescription.createFromMessage(exception.getMessage());
}
}
if (errorDescription == null) {
errorDescription = ErrorDescription.fromMessage(PyBundle.message("python.new.project.error.solution.another.sdk"));
errorDescription = PyPackageManagementService.PyPackageInstallationErrorDescription.createFromMessage(
PyBundle.message("python.new.project.error.solution.another.sdk"));
}
return errorDescription;
}

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.packaging.PyPackageInstallationErrorDialog">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="6" column-count="9" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="10">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="2228" height="929"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<vspacer id="de4c">
<constraints>
<grid row="1" column="8" row-span="3" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<grid id="8cad" binding="myCommandOutputPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="9" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="4e743" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<labelFor value="6fb53"/>
<text resource-bundle="messages/LangBundle" key="label.packaging.command.output"/>
</properties>
</component>
<scrollpane id="6fb53" class="com.intellij.ui.components.JBScrollPane">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="700" height="300"/>
<maximum-size width="700" height="300"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="307b8" class="javax.swing.JTextArea" binding="myCommandOutput">
<constraints/>
<properties>
<editable value="false"/>
<lineWrap value="true"/>
<text resource-bundle="messages/LangBundle" key="text.area.packaging.error.no.output"/>
<wrapStyleWord value="true"/>
</properties>
</component>
</children>
</scrollpane>
</children>
</grid>
<component id="abb" class="com.intellij.ui.components.JBLabel" binding="packageName">
<constraints>
<grid row="1" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<anchor value=""/>
<text value=""/>
</properties>
</component>
<component id="d7910" class="com.intellij.ui.components.JBLabel" binding="pythonVersion">
<constraints>
<grid row="3" column="1" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<anchor value=""/>
<text value=""/>
</properties>
<clientProperties>
<html.disable class="java.lang.Boolean" value="false"/>
</clientProperties>
</component>
<component id="d745d" class="com.intellij.ui.components.JBLabel" binding="interpreterPath">
<constraints>
<grid row="2" column="1" row-span="1" col-span="3" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<anchor value=""/>
<horizontalAlignment value="10"/>
<text value=""/>
<toolTipText value=""/>
</properties>
</component>
<component id="6573b" class="javax.swing.JLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Package:"/>
</properties>
</component>
<component id="13f9b" class="javax.swing.JLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Interpreter path:"/>
</properties>
</component>
<component id="73a40" class="javax.swing.JLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Python version:"/>
</properties>
</component>
<component id="af132" class="javax.swing.JSeparator">
<constraints>
<grid row="4" column="0" row-span="1" col-span="9" vsize-policy="4" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<grid id="3c0fd" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="5" column="0" row-span="1" col-span="9" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="9b938" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<font style="1"/>
<text value="Troubleshooting steps:"/>
</properties>
</component>
<vspacer id="de3ff">
<constraints>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="52088" class="com.intellij.ui.components.JBLabel" binding="myLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;div&gt;1. Try reading PyCharm guide on &lt;a href=&quot;https://www.jetbrains.com/help/pycharm/package-installation-issues.html&quot;&gt;troubleshooting package installation&lt;/a&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;"/>
</properties>
</component>
<component id="3dd1c" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="2" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="1" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="2. Try installing this package by running the following commands in the terminal."/>
</properties>
</component>
<component id="c81ab" class="com.intellij.ui.components.JBLabel">
<constraints>
<grid row="3" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="1" anchor="8" fill="0" indent="2" use-parent-layout="false"/>
</constraints>
<properties>
<text value="If the command crashes in the terminal, the problem is out of IDE control."/>
</properties>
</component>
<scrollpane id="69c18" class="com.intellij.ui.components.JBScrollPane">
<constraints>
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="700" height="50"/>
<maximum-size width="700" height="50"/>
</grid>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="3008a" class="javax.swing.JTextArea" binding="myCommandToTry" default-binding="true">
<constraints/>
<properties/>
</component>
</children>
</scrollpane>
</children>
</grid>
<hspacer id="94ada">
<constraints>
<grid row="3" column="3" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
</hspacer>
</children>
</grid>
</form>

View File

@@ -0,0 +1,80 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.packaging;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.components.JBLabel;
import com.jetbrains.python.PySdkBundle;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import com.jetbrains.python.run.PyVirtualEnvReaderKt;
import kotlin.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class PyPackageInstallationErrorDialog extends DialogWrapper {
private JPanel myMainPanel;
private JTextArea myCommandOutput;
private JPanel myCommandOutputPanel;
private JBLabel packageName;
private JBLabel interpreterPath;
private JBLabel pythonVersion;
private JBLabel myLabel;
private JTextArea myCommandToTry;
public PyPackageInstallationErrorDialog(@NotNull @NlsContexts.DialogTitle String title,
@NotNull PyPackageManagementService.PyPackageInstallationErrorDescription errorDescription) {
super(false);
init();
setResizable(false);
setTitle(title);
final String output = errorDescription.getOutput();
final String packageName = errorDescription.getPackageName();
final String interpreterPath = errorDescription.getInterpreterPath();
final String pythonVersion = errorDescription.getPythonVersion();
final String command = errorDescription.getCommand();
String activate = null;
if (errorDescription.getSdk() != null) {
final Pair<String, String> activateScript = PyVirtualEnvReaderKt.findActivateScript(interpreterPath, null);
if (activateScript != null) {
final String activateScriptPath = activateScript.component1();
final String condaEnvFolder = activateScript.component2();
if (condaEnvFolder != null) {
activate = "conda activate " + condaEnvFolder;
}
else if (activateScriptPath != null) {
if (SystemInfo.isWindows) {
activate = activateScriptPath;
}
else {
activate = "source " + activateScriptPath;
}
}
}
}
myCommandToTry.setText(activate != null ? activate + "\n" + command : command);
myLabel.setCopyable(true);
myCommandOutputPanel.setVisible(output != null);
if (output != null) {
myCommandOutput.setText(output);
}
this.packageName.setText(packageName != null ? packageName : PySdkBundle.message("python.sdk.packaging.unknown.package.data"));
this.packageName.setCopyable(true);
this.interpreterPath.setText(interpreterPath != null ? interpreterPath : PySdkBundle.message("python.sdk.packaging.unknown.package.data"));
this.interpreterPath.setCopyable(true);
this.pythonVersion.setText(pythonVersion != null ? pythonVersion : PySdkBundle.message("python.sdk.packaging.unknown.package.data"));
this.pythonVersion.setCopyable(true);
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myMainPanel;
}
}

View File

@@ -31,10 +31,11 @@ import org.jetbrains.annotations.PropertyKey;
import javax.swing.event.HyperlinkEvent;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author vlan
*/
* @author vlan
*/
public final class PyPackageManagerUI {
@NotNull private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class);
@@ -182,10 +183,16 @@ public final class PyPackageManagerUI {
final Ref<Notification> notificationRef = new Ref<>(null);
if (exceptions.isEmpty()) {
notificationRef.set(new PackagingNotification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(),
NotificationType.INFORMATION, null));
NotificationType.INFORMATION, null));
}
else {
final PackageManagementService.ErrorDescription description = PyPackageManagementService.toErrorDescription(exceptions, mySdk);
final List<String> requirements = this instanceof InstallTask && ((InstallTask)this).myRequirements != null
? ContainerUtil.map(((InstallTask)this).myRequirements, req -> req.getName()) : null;
final String packageNames = exceptions.stream()
.flatMap(e -> (e instanceof PyExecutionException) ? ((PyExecutionException)e).getArgs().stream() : null)
.filter(str -> str != null && requirements != null && requirements.contains(str)).collect(Collectors.joining(", "));
final PyPackageManagementService.PyPackageInstallationErrorDescription description = PyPackageManagementService.
toErrorDescription(exceptions, mySdk, packageNames);
if (description != null) {
final String firstLine = PyBundle.message("python.packaging.notification.title.error.occurred", getTitle());
final NotificationListener listener = new NotificationListener() {
@@ -194,12 +201,14 @@ public final class PyPackageManagerUI {
@NotNull HyperlinkEvent event) {
assert myProject != null;
final String title = StringUtil.capitalizeWords(getFailureTitle(), true);
PackagesNotificationPanel.showError(title, description);
final PyPackageInstallationErrorDialog dialog =
new PyPackageInstallationErrorDialog(title, description);
dialog.show();
}
};
String content = wrapIntoLink(firstLine, "python.packaging.notification.description.details.link");
notificationRef.set(new PackagingNotification(PACKAGING_GROUP_ID, getFailureTitle(), content,
NotificationType.ERROR, listener));
NotificationType.ERROR, listener));
}
}
ApplicationManager.getApplication().invokeLater(() -> {
@@ -236,10 +245,10 @@ public final class PyPackageManagerUI {
@NotNull private final List<String> myExtraArgs;
InstallTask(@Nullable Project project,
@NotNull Sdk sdk,
@Nullable List<PyRequirement> requirements,
@NotNull List<String> extraArgs,
@Nullable Listener listener) {
@NotNull Sdk sdk,
@Nullable List<PyRequirement> requirements,
@NotNull List<String> extraArgs,
@Nullable Listener listener) {
super(project, sdk, PyBundle.message("python.packaging.progress.title.installing.packages"), listener);
myRequirements = requirements;
myExtraArgs = extraArgs;
@@ -317,8 +326,8 @@ public final class PyPackageManagerUI {
private static class InstallManagementTask extends InstallTask {
InstallManagementTask(@Nullable Project project,
@NotNull Sdk sdk,
@Nullable Listener listener) {
@NotNull Sdk sdk,
@Nullable Listener listener) {
super(project, sdk, Collections.emptyList(), Collections.emptyList(), listener);
}
@@ -350,9 +359,9 @@ public final class PyPackageManagerUI {
@NotNull private final List<PyPackage> myPackages;
UninstallTask(@Nullable Project project,
@NotNull Sdk sdk,
@Nullable Listener listener,
@NotNull List<PyPackage> packages) {
@NotNull Sdk sdk,
@Nullable Listener listener,
@NotNull List<PyPackage> packages) {
super(project, sdk, PyBundle.message("python.packaging.progress.title.uninstalling.packages"), listener);
myPackages = packages;
}

View File

@@ -0,0 +1,30 @@
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.packaging;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.webcore.packaging.PackageManagementService;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.intellij.webcore.packaging.PackagingErrorDialog;
import com.jetbrains.python.packaging.ui.PyPackageManagementService;
import org.jetbrains.annotations.NotNull;
public class PyPackagesNotificationPanel extends PackagesNotificationPanel {
public PyPackagesNotificationPanel() {
super(PyPackagesNotificationPanel::showPackageInstallationError);
}
public static void showPackageInstallationError(@NotNull @NlsContexts.DialogTitle String title,
@NotNull PackageManagementService.ErrorDescription description) {
if (description instanceof PyPackageManagementService.PyPackageInstallationErrorDescription) {
final PyPackageManagementService.PyPackageInstallationErrorDescription errorDescription =
(PyPackageManagementService.PyPackageInstallationErrorDescription)description;
final PyPackageInstallationErrorDialog dialog = new PyPackageInstallationErrorDialog(title, errorDescription);
dialog.show();
} else {
final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description);
dialog.show();
}
}
}

View File

@@ -14,10 +14,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.text.HtmlBuilder;
import com.intellij.ui.ToggleActionButton;
import com.intellij.webcore.packaging.InstalledPackage;
import com.intellij.webcore.packaging.InstalledPackagesPanel;
import com.intellij.webcore.packaging.PackageManagementService;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.intellij.webcore.packaging.*;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PySdkBundle;
import com.jetbrains.python.packaging.*;
@@ -68,9 +65,11 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
public void finished(List<ExecutionException> exceptions) {
myPackagesTable.setPaintBusy(false);
PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
final PackageManagementService.ErrorDescription description = PyPackageManagementService.toErrorDescription(exceptions, sdk);
final PyPackageManagementService.PyPackageInstallationErrorDescription description =
PyPackageManagementService.toErrorDescription(exceptions, sdk, "packaging tools");
if (description != null) {
PackagesNotificationPanel.showError(PyBundle.message("python.packaging.failed.to.install.packaging.tools.title"), description);
PyPackagesNotificationPanel.showPackageInstallationError(
PyBundle.message("python.packaging.failed.to.install.packaging.tools.title"), description);
}
packageManager.refresh();
updatePackages(PyPackageManagers.getInstance().getManagementService(myProject, sdk));
@@ -93,7 +92,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement();
application.invokeLater(() -> updateUninstallUpgrade(), ModalityState.any());
if (!myHasManagement) {
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.tools.not.found"), "pip", Collections.emptyList(), "", "", 0,
throw new PyExecutionException(PySdkBundle.message("python.sdk.packaging.tools.not.found"), "pip", Collections.emptyList(), "",
"", 0,
ImmutableList.of(new PyInstallPackageManagementFix()));
}
}
@@ -185,31 +185,33 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
@Override
protected ToggleActionButton @NotNull [] getExtraActions() {
final ToggleActionButton useCondaButton = new DumbAwareToggleActionButton(PyBundle.messagePointer("action.AnActionButton.text.use.conda.package.manager"), PythonIcons.Python.Anaconda) {
@Override
public boolean isSelected(AnActionEvent e) {
final Sdk sdk = getSelectedSdk();
return sdk != null && PyPackageManager.getInstance(sdk) instanceof PyCondaPackageManagerImpl &&
((PyCondaPackageManagerImpl)PyPackageManager.getInstance(sdk)).useConda();
}
@Override
public void setSelected(AnActionEvent e, boolean state) {
final Sdk sdk = getSelectedSdk();
if (sdk == null) return;
final PyPackageManager manager = PyPackageManager.getInstance(sdk);
if (manager instanceof PyCondaPackageManagerImpl) {
((PyCondaPackageManagerImpl)manager).useConda(state);
final ToggleActionButton useCondaButton =
new DumbAwareToggleActionButton(PyBundle.messagePointer("action.AnActionButton.text.use.conda.package.manager"),
PythonIcons.Python.Anaconda) {
@Override
public boolean isSelected(AnActionEvent e) {
final Sdk sdk = getSelectedSdk();
return sdk != null && PyPackageManager.getInstance(sdk) instanceof PyCondaPackageManagerImpl &&
((PyCondaPackageManagerImpl)PyPackageManager.getInstance(sdk)).useConda();
}
updatePackages(myPackageManagementService);
}
@Override
public boolean isVisible() {
final Sdk sdk = getSelectedSdk();
return sdk != null && PythonSdkUtil.isConda(sdk);
}
};
@Override
public void setSelected(AnActionEvent e, boolean state) {
final Sdk sdk = getSelectedSdk();
if (sdk == null) return;
final PyPackageManager manager = PyPackageManager.getInstance(sdk);
if (manager instanceof PyCondaPackageManagerImpl) {
((PyCondaPackageManagerImpl)manager).useConda(state);
}
updatePackages(myPackageManagementService);
}
@Override
public boolean isVisible() {
final Sdk sdk = getSelectedSdk();
return sdk != null && PythonSdkUtil.isConda(sdk);
}
};
final ToggleActionButton showEarlyReleasesButton =
new DumbAwareToggleActionButton(PyBundle.messagePointer("action.AnActionButton.text.show.early.releases"), AllIcons.Actions.Show) {
@@ -233,4 +235,9 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
super(text, icon);
}
}
@Override
protected @NotNull PackagesNotificationPanel createNotificationPanel() {
return new PyPackagesNotificationPanel();
}
}

View File

@@ -5,7 +5,9 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.RunCanceledByUserException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.openapi.util.NlsContexts.DetailedDescription;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.scale.JBUIScale;
@@ -156,8 +158,9 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
@Override
public String getInstallToUserText() {
String userSiteText = PyBundle.message("button.install.to.user.site.packages.directory");
if (!PythonSdkUtil.isRemote(mySdk))
if (!PythonSdkUtil.isRemote(mySdk)) {
userSiteText += " (" + PythonSdkUtil.getUserSite() + ")";
}
return userSiteText;
}
@@ -179,8 +182,12 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
}
@Override
public void installPackage(@NotNull RepoPackage repoPackage, @Nullable String version, boolean forceUpgrade, @Nullable String extraOptions,
@NotNull Listener listener, boolean installToUser) {
public void installPackage(@NotNull RepoPackage repoPackage,
@Nullable String version,
boolean forceUpgrade,
@Nullable String extraOptions,
@NotNull Listener listener,
boolean installToUser) {
final String packageName = repoPackage.getName();
final String repository = PyPIPackageUtil.isPyPIRepository(repoPackage.getRepoUrl()) ? null : repoPackage.getRepoUrl();
final List<String> extraArgs = new ArrayList<>();
@@ -210,7 +217,7 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
@Override
public void finished(@Nullable List<ExecutionException> exceptions) {
listener.operationFinished(packageName, toErrorDescription(exceptions, mySdk));
listener.operationFinished(packageName, toErrorDescription(exceptions, mySdk, repoPackage.getName()));
}
});
ui.install(Collections.singletonList(req), extraArgs);
@@ -218,8 +225,15 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
@Nullable
public static ErrorDescription toErrorDescription(@Nullable List<ExecutionException> exceptions, @Nullable Sdk sdk) {
return toErrorDescription(exceptions, sdk, null);
}
@Nullable
public static PyPackageInstallationErrorDescription toErrorDescription(@Nullable List<ExecutionException> exceptions,
@Nullable Sdk sdk,
@Nullable String packageName) {
if (exceptions != null && !exceptions.isEmpty() && !isCancelled(exceptions)) {
return createDescription(exceptions.get(0), sdk);
return createDescription(exceptions.get(0), sdk, packageName);
}
return null;
}
@@ -316,18 +330,21 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
}
@NotNull
private static ErrorDescription createDescription(@NotNull ExecutionException e, @Nullable Sdk sdk) {
private static PyPackageInstallationErrorDescription createDescription(@NotNull ExecutionException e,
@Nullable Sdk sdk,
@Nullable String packageName) {
if (e instanceof PyExecutionException) {
final PyExecutionException ee = (PyExecutionException)e;
final String stdoutCause = findErrorCause(ee.getStdout());
final String stderrCause = findErrorCause(ee.getStderr());
final String cause = stdoutCause != null ? stdoutCause : stderrCause;
final String message = cause != null ? cause : ee.getMessage();
final String message = cause != null ? cause : ee.getMessage();
final String command = ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " ");
return new ErrorDescription(message, command, ee.getStdout() + "\n" + ee.getStderr(), findErrorSolution(ee, cause, sdk));
return new PyPackageInstallationErrorDescription(message, command, ee.getStdout() + "\n" + ee.getStderr(),
findErrorSolution(ee, cause, sdk), packageName, sdk);
}
else {
return ErrorDescription.fromMessage(e.getMessage());
return new PyPackageInstallationErrorDescription(e.getMessage(), null, null, null, packageName, sdk);
}
}
@@ -405,4 +422,45 @@ public class PyPackageManagementService extends PackageManagementServiceEx {
public String getID() {
return "Python";
}
public static class PyPackageInstallationErrorDescription extends ErrorDescription {
@Nullable private final String myPackageName;
@Nullable private final String myPythonVersion;
@Nullable private final String myInterpreterPath;
@Nullable private final Sdk mySdk;
public PyPackageInstallationErrorDescription(@NotNull @DetailedDescription String message,
@Nullable String command,
@Nullable String output,
@Nullable @DetailedDescription String solution,
@Nullable String packageName,
@Nullable Sdk sdk) {
super(message, command, output, solution);
myPackageName = packageName;
mySdk = sdk;
myPythonVersion = sdk != null ? sdk.getVersionString() : null;
myInterpreterPath = sdk != null ? sdk.getHomePath() : null;
}
@Nullable
public static PyPackageInstallationErrorDescription createFromMessage(@Nullable @NlsContexts.DetailedDescription String message) {
return message != null ? new PyPackageInstallationErrorDescription(message, null, null, null, null, null) : null;
}
public @Nullable @NlsSafe String getPackageName() {
return myPackageName;
}
public @Nullable @NlsSafe String getPythonVersion() {
return myPythonVersion;
}
public @Nullable @NlsSafe String getInterpreterPath() {
return myInterpreterPath;
}
public @Nullable Sdk getSdk() {
return mySdk;
}
}
}