Added getFixes() to PyExternalProcessException instead of withHandler()

These exceptions and fixes are now used for displaying notifications in
the notification area of the package management panel.
This commit is contained in:
Andrey Vlasovskikh
2014-09-23 17:05:44 +04:00
parent f4d21231ee
commit 00cdaa2b96
5 changed files with 142 additions and 116 deletions
@@ -0,0 +1,27 @@
/*
* 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.openapi.projectRoots.Sdk;
import org.jetbrains.annotations.NotNull;
/**
* @author vlan
*/
public interface PyExecutionFix {
@NotNull String getName();
void run(@NotNull Sdk sdk);
}
@@ -16,11 +16,11 @@
package com.jetbrains.python.packaging;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
@@ -30,19 +30,22 @@ import java.util.regex.Pattern;
public class PyExternalProcessException extends ExecutionException {
private static final Pattern WITH_CR_DELIMITER_PATTERN = Pattern.compile("(?<=\r|\n|\r\n)");
private final int myRetcode;
@NotNull private String myName;
@NotNull private List<String> myArgs;
@NotNull private String myMessage;
@NotNull private final List<? extends PyExecutionFix> myFixes;
private Pair<String, Runnable> myHandler = null;
public PyExternalProcessException(@NotNull String name, @NotNull List<String> args, @NotNull String message) {
this(name, args, message, Collections.<PyExecutionFix>emptyList());
}
public PyExternalProcessException(int retcode, @NotNull String name, @NotNull List<String> args, @NotNull String message) {
public PyExternalProcessException(@NotNull String name, @NotNull List<String> args, @NotNull String message,
@NotNull List<? extends PyExecutionFix> fixes) {
super(String.format("External process error '%s %s':\n%s", name, StringUtil.join(args, " "), message));
myRetcode = retcode;
myName = name;
myArgs = args;
myMessage = stripLinesWithoutLineFeeds(message);
myFixes = fixes;
}
@Override
@@ -57,10 +60,6 @@ public class PyExternalProcessException extends ExecutionException {
return b.toString();
}
public int getRetcode() {
return myRetcode;
}
@NotNull
public String getName() {
return myName;
@@ -88,17 +87,8 @@ public class PyExternalProcessException extends ExecutionException {
return StringUtil.join(result, "");
}
public PyExternalProcessException withHandler(@NotNull String name, @NotNull Runnable handler) {
myHandler = Pair.create(name, handler);
return this;
}
public boolean hasHandler() {
return myHandler != null;
}
public Pair<String, Runnable> getHandler() {
return myHandler;
@NotNull
public List<? extends PyExecutionFix> getFixes() {
return myFixes;
}
}
@@ -424,11 +424,11 @@ public class PyPackageManagerImpl extends PyPackageManager {
final ProcessOutput output = getPythonProcessOutput(path, args, askForSudo, showProgress, workingDir);
final int exitCode = output.getExitCode();
if (output.isTimeout()) {
throw new PyExternalProcessException(ERROR_TIMEOUT, path, args, "Timed out");
throw new PyExternalProcessException(path, args, "Timed out");
}
else if (exitCode != 0) {
final String message = output.getStderr() + "\n" + output.getStdout();
throw new PyExternalProcessException(exitCode, path, args, message);
throw new PyExternalProcessException(path, args, message);
}
return output.getStdout();
}
@@ -499,7 +499,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
if (StringUtil.isEmptyOrSpaces(message)) {
message = "Failed to perform action. Permission denied.";
}
throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
throw new PyExternalProcessException(helperPath, args, message);
}
return result;
}
@@ -507,10 +507,10 @@ public class PyPackageManagerImpl extends PyPackageManager {
throw e;
}
catch (ExecutionException e) {
throw new PyExternalProcessException(ERROR_EXECUTION, helperPath, args, e.getMessage());
throw new PyExternalProcessException(helperPath, args, e.getMessage());
}
catch (IOException e) {
throw new PyExternalProcessException(ERROR_ACCESS_DENIED, helperPath, args, e.getMessage());
throw new PyExternalProcessException(helperPath, args, e.getMessage());
}
}
@@ -521,8 +521,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
for (String line : lines) {
final List<String> fields = StringUtil.split(line, "\t");
if (fields.size() < 3) {
throw new PyExternalProcessException(ERROR_INVALID_OUTPUT, PACKAGING_TOOL, Collections.<String>emptyList(),
"Invalid output format");
throw new PyExternalProcessException(PACKAGING_TOOL, Collections.<String>emptyList(), "Invalid output format");
}
final String name = fields.get(0);
final String version = fields.get(1);
@@ -17,6 +17,7 @@ package com.jetbrains.python.packaging;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.diagnostic.Logger;
@@ -95,28 +96,33 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
}
catch (final ExecutionException e) {
if (e.getCause() instanceof VagrantNotStartedException) {
throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
LAUNCH_VAGRANT +
"\">Launch vagrant</a>")
.withHandler(LAUNCH_VAGRANT, new Runnable() {
@Override
public void run() {
final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
if (manager != null) {
final List<? extends PyExecutionFix> fixes = ImmutableList.of(new PyExecutionFix() {
@NotNull
@Override
public String getName() {
return "Launch Vagrant";
}
try {
manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
clearCaches();
}
catch (ExecutionException e1) {
throw new RuntimeException(e1);
}
@Override
public void run(@NotNull Sdk sdk) {
final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
if (manager != null) {
try {
manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
clearCaches();
}
catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
});
}
});
throw new PyExternalProcessException(helperPath, args, "Vagrant instance is down. <a href=\"" +
LAUNCH_VAGRANT +
"\">Launch vagrant</a>", fixes);
}
else {
throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
throw new PyExternalProcessException(helperPath, args, e.getMessage());
}
}
final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
@@ -148,16 +154,16 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
return processOutput;
}
catch (ExecutionException e) {
throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage());
throw new PyExternalProcessException(helperPath, args, "Error running SDK: " + e.getMessage());
}
}
else {
throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args,
throw new PyExternalProcessException(helperPath, args,
PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
}
}
else {
throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Invalid remote SDK");
throw new PyExternalProcessException(helperPath, args, "Invalid remote SDK");
}
}
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.packaging.ui;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.intellij.execution.ExecutionException;
import com.intellij.openapi.application.Application;
@@ -29,11 +30,10 @@ import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.packaging.*;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.sdk.flavors.IronPythonSdkFlavor;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -41,9 +41,6 @@ import java.util.Set;
* @author yole
*/
public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
public static final String INSTALL_MANAGEMENT = "installManagement";
public static final String CREATE_VENV = "createVEnv";
private boolean myHasManagement = false;
public PyInstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
@@ -55,6 +52,42 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
return service != null ? service.getSdk() : null;
}
class PyInstallPackageManagementFix implements PyExecutionFix {
@NotNull
@Override
public String getName() {
return "Install packaging tools";
}
@Override
public void run(@NotNull final Sdk sdk) {
final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
myPackagesTable.setPaintBusy(true);
}
@Override
public void finished(List<ExecutionException> exceptions) {
myPackagesTable.setPaintBusy(false);
PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
if (!exceptions.isEmpty()) {
final String firstLine = "Install Python packaging tools failed. ";
final String description = PyPackageManagerUI.createDescription(exceptions, firstLine);
PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description);
}
packageManager.refresh();
updatePackages(new PyPackageManagementService(myProject, sdk));
for (Consumer<Sdk> listener : myPathChangedListeners) {
listener.consume(sdk);
}
updateNotifications(sdk);
}
});
ui.installManagement();
}
}
public void updateNotifications(@Nullable final Sdk selectedSdk) {
if (selectedSdk == null) {
myNotificationArea.hide();
@@ -64,50 +97,48 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
application.executeOnPooledThread(new Runnable() {
@Override
public void run() {
PyPackageManager packageManager = PyPackageManager.getInstance(selectedSdk);
myHasManagement = packageManager.hasManagement(false);
PyExternalProcessException exception = null;
try {
myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(false);
if (!myHasManagement) {
throw new PyExternalProcessException("pip", Collections.<String>emptyList(), "Python packaging tools not found",
ImmutableList.of(new PyInstallPackageManagementFix()));
}
}
catch (PyExternalProcessException e) {
exception = e;
}
final PyExternalProcessException problem = exception;
application.invokeLater(new Runnable() {
@Override
public void run() {
if (selectedSdk == getSelectedSdk()) {
final PythonSdkFlavor flavor = PythonSdkFlavor.getFlavor(selectedSdk);
final boolean invalid = PythonSdkType.isInvalid(selectedSdk);
boolean allowCreateVirtualEnv =
!(PythonSdkType.isRemote(selectedSdk) || flavor instanceof IronPythonSdkFlavor) &&
!PythonSdkType.isVirtualEnv(selectedSdk) &&
myNotificationArea.hasLinkHandler(CREATE_VENV);
final String createVirtualEnvLink = "<a href=\"" + CREATE_VENV + "\">create new VirtualEnv</a>";
myNotificationArea.hide();
if (!invalid) {
String text = null;
if (!myHasManagement) {
myNotificationArea.addLinkHandler(INSTALL_MANAGEMENT,
new Runnable() {
@Override
public void run() {
final Sdk sdk = getSelectedSdk();
if (sdk != null) {
installManagementTools(sdk);
}
myNotificationArea.removeLinkHandler(INSTALL_MANAGEMENT);
updateNotifications(selectedSdk);
}
}
);
}
if (!myHasManagement) {
text = "Python packaging tools not found. <a href=\"" + INSTALL_MANAGEMENT + "\">Install packaging tools</a>";
}
if (text != null) {
if (allowCreateVirtualEnv) {
text += " or " + createVirtualEnvLink;
if (problem != null) {
final boolean invalid = PythonSdkType.isInvalid(selectedSdk);
if (!invalid) {
final StringBuilder builder = new StringBuilder(problem.getMessage());
builder.append(". ");
for (final PyExecutionFix fix : problem.getFixes()) {
final String key = "id" + fix.hashCode();
final String link = "<a href=\"" + key + "\">" + fix.getName() + "</a>";
builder.append(link);
builder.append(" ");
myNotificationArea.addLinkHandler(key, new Runnable() {
@Override
public void run() {
final Sdk sdk = getSelectedSdk();
if (sdk != null) {
fix.run(sdk);
myNotificationArea.removeLinkHandler(key);
}
}
});
}
myNotificationArea.showWarning(text);
myNotificationArea.showWarning(builder.toString());
}
myInstallButton.setEnabled(!invalid && myHasManagement);
}
myInstallButton.setEnabled(!invalid && myHasManagement);
}
}
}, ModalityState.any());
@@ -120,33 +151,6 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
return Sets.newHashSet("pip", "distutils", "setuptools");
}
private void installManagementTools(@NotNull final Sdk sdk) {
final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
myPackagesTable.setPaintBusy(true);
}
@Override
public void finished(List<ExecutionException> exceptions) {
myPackagesTable.setPaintBusy(false);
PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
if (!exceptions.isEmpty()) {
final String firstLine = "Install Python packaging tools failed. ";
final String description = PyPackageManagerUI.createDescription(exceptions, firstLine);
PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description);
}
packageManager.refresh();
updatePackages(new PyPackageManagementService(myProject, sdk));
for (Consumer<Sdk> listener : myPathChangedListeners) {
listener.consume(sdk);
}
updateNotifications(sdk);
}
});
ui.installManagement();
}
@Override
protected boolean canUninstallPackage(InstalledPackage pkg) {
if (!myHasManagement) return false;