From 90fdcf32761b627e1a010e32c5e5b59247b9f548 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 22 Sep 2014 18:25:02 +0400 Subject: [PATCH 01/24] Use ExecutionException in signatures instead of more specific PyExternalProcessException --- .../python/packaging/PyPackageManager.java | 17 +++---- .../PyProcessCancelledException.java | 29 ------------ .../templateLanguages/PyTemplatesUtil.java | 6 +-- .../com/jetbrains/rest/RestPythonUtil.java | 4 +- .../PyIntegratedToolsConfigurable.java | 3 +- .../PyPackageRequirementsInspection.java | 9 ++-- .../packaging/PyPackageManagerImpl.java | 45 ++++++++++--------- .../python/packaging/PyPackageManagerUI.java | 42 ++++++++--------- .../packaging/PyRemotePackageManagerImpl.java | 6 +-- .../ui/PyInstalledPackagesPanel.java | 3 +- .../ui/PyPackageManagementService.java | 9 ++-- .../python/sdk/CreateVirtualEnvDialog.java | 4 +- .../sdk/skeletons/PySkeletonRefresher.java | 3 +- .../testing/VFSTestFrameworkListener.java | 4 +- .../pytest/PyTestConfigurationProducer.java | 4 +- .../jetbrains/env/python/PyPackagingTest.java | 11 ++--- 16 files changed, 87 insertions(+), 112 deletions(-) delete mode 100644 python/openapi/src/com/jetbrains/python/packaging/PyProcessCancelledException.java diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java index 82c566fd1249..770fb7037c56 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.packaging; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.Key; @@ -40,20 +41,20 @@ public abstract class PyPackageManager { return PyPackageManagers.getInstance().forSdk(sdk); } - public abstract void installManagement() throws PyExternalProcessException; + public abstract void installManagement() throws ExecutionException; public abstract boolean hasManagement(boolean cachedOnly); - public abstract void install(@NotNull String requirementString) throws PyExternalProcessException; - public abstract void install(@NotNull List requirements, @NotNull List extraArgs) throws PyExternalProcessException; - public abstract void uninstall(@NotNull List packages) throws PyExternalProcessException; + public abstract void install(@NotNull String requirementString) throws ExecutionException; + public abstract void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException; + public abstract void uninstall(@NotNull List packages) throws ExecutionException; public abstract void refresh(); @NotNull - public abstract String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws PyExternalProcessException; + public abstract String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws ExecutionException; @Nullable - public abstract List getPackages(boolean cachedOnly) throws PyExternalProcessException; + public abstract List getPackages(boolean cachedOnly) throws ExecutionException; @Nullable - public abstract PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws PyExternalProcessException; + public abstract PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException; @Nullable public abstract List getRequirements(@NotNull Module module); @Nullable - public abstract Set getDependents(@NotNull PyPackage pkg) throws PyExternalProcessException; + public abstract Set getDependents(@NotNull PyPackage pkg) throws ExecutionException; } diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyProcessCancelledException.java b/python/openapi/src/com/jetbrains/python/packaging/PyProcessCancelledException.java deleted file mode 100644 index 9721bec0bb86..000000000000 --- a/python/openapi/src/com/jetbrains/python/packaging/PyProcessCancelledException.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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 org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * @author vlan - */ -public class PyProcessCancelledException extends PyExternalProcessException { - public PyProcessCancelledException(@NotNull String name, @NotNull List args) { - super(255, name, args, "Process cancelled"); - } -} diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java b/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java index c37c65ed4144..a770220886e0 100644 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java +++ b/python/openapi/src/com/jetbrains/python/templateLanguages/PyTemplatesUtil.java @@ -15,9 +15,9 @@ */ package com.jetbrains.python.templateLanguages; +import com.intellij.execution.ExecutionException; import com.intellij.facet.ui.ValidationResult; import com.intellij.openapi.projectRoots.Sdk; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; import org.jetbrains.annotations.NonNls; @@ -44,7 +44,7 @@ public class PyTemplatesUtil { if (installedPackage == null) return new ValidationResult(templateBinding + " will be installed on selected interpreter"); } - catch (PyExternalProcessException ignored) { + catch (ExecutionException ignored) { } } } @@ -55,7 +55,7 @@ public class PyTemplatesUtil { return new ValidationResult(language + " will be installed on selected interpreter"); } } - catch (PyExternalProcessException ignored) { + catch (ExecutionException ignored) { } } return null; diff --git a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java index e6d379f03676..f7468d82a426 100644 --- a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java +++ b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java @@ -15,6 +15,7 @@ */ package com.jetbrains.rest; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.LangDataKeys; @@ -23,7 +24,6 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.sdk.PythonSdkType; @@ -52,7 +52,7 @@ public class RestPythonUtil { final PyPackage sphinx = manager.findPackage("Sphinx", false); presentation.setEnabled(sphinx != null); } - catch (PyExternalProcessException ignored) { + catch (ExecutionException ignored) { } } } diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java index e9585a5cd952..3a29f623dae4 100644 --- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java @@ -17,6 +17,7 @@ package com.jetbrains.python.configuration; import com.google.common.collect.Lists; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.execution.ExecutionException; import com.intellij.facet.impl.ui.FacetErrorPanel; import com.intellij.facet.ui.FacetConfigurationQuickFix; import com.intellij.facet.ui.FacetEditorValidator; @@ -161,7 +162,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable, No public void started() {} @Override - public void finished(List exceptions) { + public void finished(List exceptions) { if (exceptions.isEmpty()) { VFSTestFrameworkListener.getInstance().testInstalled(true, sdk.getHomePath(), name); facetErrorPanel.getValidatorsManager().validate(); diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java index 165a3ee04e2c..cc89d0b49444 100644 --- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java @@ -18,6 +18,7 @@ package com.jetbrains.python.inspections; import com.google.common.collect.ImmutableSet; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ui.ListEditForm; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Document; @@ -212,7 +213,7 @@ public class PyPackageRequirementsInspection extends PyInspection { try { packages = PyPackageManager.getInstance(sdk).getPackages(PySdkUtil.isRemote(sdk)); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { return null; } if (packages == null) return null; @@ -248,7 +249,7 @@ public class PyPackageRequirementsInspection extends PyInspection { try { packages = manager.getPackages(PySdkUtil.isRemote(sdk)); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { return null; } if (packages == null) return null; @@ -330,7 +331,7 @@ public class PyPackageRequirementsInspection extends PyInspection { if (installManagement) { final PyPackageManagerUI ui = new PyPackageManagerUI(project, mySdk, new UIListener(myModule) { @Override - public void finished(List exceptions) { + public void finished(List exceptions) { super.finished(exceptions); if (exceptions.isEmpty()) { installRequirements(project, chosen); @@ -363,7 +364,7 @@ public class PyPackageRequirementsInspection extends PyInspection { } @Override - public void finished(List exceptions) { + public void finished(List exceptions) { setRunningPackagingTasks(myModule, false); } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 070a89fcb3fe..d2581fd4dec2 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -18,6 +18,7 @@ package com.jetbrains.python.packaging; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.intellij.execution.ExecutionException; +import com.intellij.execution.RunCanceledByUserException; import com.intellij.execution.process.*; import com.intellij.execution.util.ExecUtil; import com.intellij.openapi.application.Application; @@ -91,7 +92,7 @@ public class PyPackageManagerImpl extends PyPackageManager { private final Object myCacheLock = new Object(); private List myPackagesCache = null; - private PyExternalProcessException myExceptionCache = null; + private ExecutionException myExceptionCache = null; protected Sdk mySdk; @@ -117,7 +118,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Override - public void installManagement() throws PyExternalProcessException { + public void installManagement() throws ExecutionException { final boolean pre26 = PythonSdkType.getLanguageLevelForSdk(mySdk).isOlderThan(LanguageLevel.PYTHON26); if (!hasPackage(SETUPTOOLS, false) && !hasPackage(DISTRIBUTE, false)) { final String name = SETUPTOOLS + "-" + (pre26 ? SETUPTOOLS_PRE_26_VERSION : SETUPTOOLS_VERSION); @@ -135,7 +136,7 @@ public class PyPackageManagerImpl extends PyPackageManager { hasPackage(PIP, cachedOnly); } - protected void installManagement(@NotNull String name) throws PyExternalProcessException { + protected void installManagement(@NotNull String name) throws ExecutionException { final String dirName = extractHelper(name + ".tar.gz"); try { final String fileName = dirName + name + File.separatorChar + "setup.py"; @@ -148,7 +149,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @NotNull - private String extractHelper(@NotNull String name) throws PyExternalProcessException { + private String extractHelper(@NotNull String name) throws ExecutionException { final String helperPath = getHelperPath(name); final ArrayList args = Lists.newArrayList(UNTAR, helperPath); final String result = getHelperResult(PACKAGING_TOOL, args, false, false, null); @@ -163,7 +164,7 @@ public class PyPackageManagerImpl extends PyPackageManager { try { return findPackage(name, cachedOnly) != null; } - catch (PyExternalProcessException ignored) { + catch (ExecutionException ignored) { return false; } } @@ -184,13 +185,13 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Override - public void install(@NotNull String requirementString) throws PyExternalProcessException { + public void install(@NotNull String requirementString) throws ExecutionException { installManagement(); install(Collections.singletonList(PyRequirement.fromString(requirementString)), Collections.emptyList()); } @Override - public void install(@NotNull List requirements, @NotNull List extraArgs) throws PyExternalProcessException { + public void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException { final List args = new ArrayList(); args.add(INSTALL); final File buildDir; @@ -198,7 +199,7 @@ public class PyPackageManagerImpl extends PyPackageManager { buildDir = FileUtil.createTempDirectory("pycharm-packaging", null); } catch (IOException e) { - throw new PyExternalProcessException(ERROR_ACCESS_DENIED, PACKAGING_TOOL, args, "Cannot create temporary build directory"); + throw new ExecutionException("Cannot create temporary build directory"); } if (!extraArgs.contains(BUILD_DIR_OPTION)) { args.addAll(Arrays.asList(BUILD_DIR_OPTION, buildDir.getAbsolutePath())); @@ -224,7 +225,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } } - public void uninstall(@NotNull List packages) throws PyExternalProcessException { + public void uninstall(@NotNull List packages) throws ExecutionException { try { final List args = new ArrayList(); args.add(UNINSTALL); @@ -246,7 +247,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Nullable - public List getPackages(boolean cachedOnly) throws PyExternalProcessException { + public List getPackages(boolean cachedOnly) throws ExecutionException { synchronized (myCacheLock) { if (myPackagesCache != null) { return myPackagesCache; @@ -266,7 +267,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } return packages; } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { synchronized (myCacheLock) { myExceptionCache = e; } @@ -275,7 +276,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Nullable - public Set getDependents(@NotNull PyPackage pkg) throws PyExternalProcessException { + public Set getDependents(@NotNull PyPackage pkg) throws ExecutionException { final List packages = getPackages(false); if (packages != null) { final Set dependents = new HashSet(); @@ -294,7 +295,7 @@ public class PyPackageManagerImpl extends PyPackageManager { @Override @Nullable - public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws PyExternalProcessException { + public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException { final List packages = getPackages(cachedOnly); if (packages != null) { for (PyPackage pkg : packages) { @@ -307,7 +308,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @NotNull - public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws PyExternalProcessException { + public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws ExecutionException { final List args = new ArrayList(); final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(mySdk); final boolean usePyVenv = languageLevel.isAtLeast(LanguageLevel.PYTHON33); @@ -404,10 +405,10 @@ public class PyPackageManagerImpl extends PyPackageManager { @NotNull private String getHelperResult(@NotNull String helper, @NotNull List args, boolean askForSudo, - boolean showProgress, @Nullable String parentDir) throws PyExternalProcessException { + boolean showProgress, @Nullable String parentDir) throws ExecutionException { final String helperPath = getHelperPath(helper); if (helperPath == null) { - throw new PyExternalProcessException(ERROR_TOOL_NOT_FOUND, helper, args, "Cannot find external tool"); + throw new ExecutionException("Cannot find external tool: " + helper); } return getPythonProcessResult(helperPath, args, askForSudo, showProgress, parentDir); } @@ -419,7 +420,7 @@ public class PyPackageManagerImpl extends PyPackageManager { @NotNull private String getPythonProcessResult(@NotNull String path, @NotNull List args, boolean askForSudo, - boolean showProgress, @Nullable String workingDir) throws PyExternalProcessException { + boolean showProgress, @Nullable String workingDir) throws ExecutionException { final ProcessOutput output = getPythonProcessOutput(path, args, askForSudo, showProgress, workingDir); final int exitCode = output.getExitCode(); if (output.isTimeout()) { @@ -434,10 +435,10 @@ public class PyPackageManagerImpl extends PyPackageManager { @NotNull protected ProcessOutput getPythonProcessOutput(@NotNull String helperPath, @NotNull List args, boolean askForSudo, - boolean showProgress, @Nullable String workingDir) throws PyExternalProcessException { + boolean showProgress, @Nullable String workingDir) throws ExecutionException { final String homePath = mySdk.getHomePath(); if (homePath == null) { - throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK"); + throw new ExecutionException("Cannot find Python interpreter for SDK " + mySdk.getName()); } if (workingDir == null) { workingDir = new File(homePath).getParent(); @@ -487,7 +488,7 @@ public class PyPackageManagerImpl extends PyPackageManager { result = handler.runProcess(TIMEOUT); } if (result.isCancelled()) { - throw new PyProcessCancelledException(helperPath, args); + throw new RunCanceledByUserException(); } String message = result.getStderr(); if (result.getExitCode() != 0) { @@ -502,7 +503,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } return result; } - catch (PyProcessCancelledException e) { + catch (RunCanceledByUserException e) { throw e; } catch (ExecutionException e) { @@ -514,7 +515,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @NotNull - private static List parsePackagingToolOutput(@NotNull String s) throws PyExternalProcessException { + private static List parsePackagingToolOutput(@NotNull String s) throws ExecutionException { final String[] lines = StringUtil.splitByLines(s); final List packages = new ArrayList(); for (String line : lines) { diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index f504cbae33b9..1ddb2a9c1ae3 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -15,6 +15,8 @@ */ package com.jetbrains.python.packaging; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.RunCanceledByUserException; import com.intellij.icons.AllIcons; import com.intellij.notification.Notification; import com.intellij.notification.NotificationListener; @@ -50,7 +52,7 @@ public class PyPackageManagerUI { public interface Listener { void started(); - void finished(List exceptions); + void finished(List exceptions); } public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) { @@ -113,14 +115,14 @@ public class PyPackageManagerUI { } if (warning[0] != Messages.YES) return true; } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { LOG.info("Error loading packages dependents: " + e.getMessage(), e); } return false; } - private static Map> collectDependents(@NotNull final List packages, Sdk sdk) - throws PyExternalProcessException { + private static Map> collectDependents(@NotNull final List packages, + Sdk sdk) throws ExecutionException { Map> dependentPackages = new HashMap>(); for (PyPackage pkg : packages) { final Set dependents = PyPackageManager.getInstance(sdk).getDependents(pkg); @@ -152,7 +154,7 @@ public class PyPackageManagerUI { } @NotNull - protected abstract List runTask(@NotNull ProgressIndicator indicator); + protected abstract List runTask(@NotNull ProgressIndicator indicator); @NotNull protected abstract String getSuccessTitle(); @@ -175,7 +177,7 @@ public class PyPackageManagerUI { } } - protected void taskFinished(@NotNull final List exceptions) { + protected void taskFinished(@NotNull final List exceptions) { final Ref notificationRef = new Ref(null); if (exceptions.isEmpty()) { notificationRef.set(new Notification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(), @@ -211,9 +213,9 @@ public class PyPackageManagerUI { }); } - private static boolean isCancelled(@NotNull List exceptions) { - for (PyExternalProcessException e : exceptions) { - if (e instanceof PyProcessCancelledException) { + private static boolean isCancelled(@NotNull List exceptions) { + for (ExecutionException e : exceptions) { + if (e instanceof RunCanceledByUserException) { return true; } } @@ -239,8 +241,8 @@ public class PyPackageManagerUI { @NotNull @Override - protected List runTask(@NotNull ProgressIndicator indicator) { - final List exceptions = new ArrayList(); + protected List runTask(@NotNull ProgressIndicator indicator) { + final List exceptions = new ArrayList(); final int size = myRequirements.size(); final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk); for (int i = 0; i < size; i++) { @@ -256,11 +258,11 @@ public class PyPackageManagerUI { try { manager.install(Arrays.asList(requirement), myExtraArgs); } - catch (PyProcessCancelledException e) { + catch (RunCanceledByUserException e) { exceptions.add(e); break; } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { exceptions.add(e); } } @@ -297,15 +299,15 @@ public class PyPackageManagerUI { @NotNull @Override - protected List runTask(@NotNull ProgressIndicator indicator) { - final List exceptions = new ArrayList(); + protected List runTask(@NotNull ProgressIndicator indicator) { + final List exceptions = new ArrayList(); final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk); indicator.setText("Installing packaging tools..."); indicator.setIndeterminate(true); try { manager.installManagement(); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { exceptions.add(e); } manager.refresh(); @@ -334,14 +336,14 @@ public class PyPackageManagerUI { @NotNull @Override - protected List runTask(@NotNull ProgressIndicator indicator) { + protected List runTask(@NotNull ProgressIndicator indicator) { final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk); indicator.setIndeterminate(true); try { manager.uninstall(myPackages); return Arrays.asList(); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { return Arrays.asList(e); } finally { @@ -374,11 +376,11 @@ public class PyPackageManagerUI { } } - public static String createDescription(List exceptions, String firstLine) { + public static String createDescription(List exceptions, String firstLine) { final StringBuilder b = new StringBuilder(); b.append(firstLine); b.append("\n\n"); - for (PyExternalProcessException exception : exceptions) { + for (ExecutionException exception : exceptions) { b.append(exception.toString()); b.append("\n"); } diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index bf199d6ded80..2a579f16d44d 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -78,10 +78,10 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { protected ProcessOutput getPythonProcessOutput(@NotNull String helperPath, @NotNull List args, boolean askForSudo, - boolean showProgress, @Nullable String workingDir) throws PyExternalProcessException { + boolean showProgress, @Nullable String workingDir) throws ExecutionException { final String homePath = mySdk.getHomePath(); if (homePath == null) { - throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK"); + throw new ExecutionException("Cannot find Python interpreter for SDK " + mySdk.getName()); } final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData(); if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter @@ -167,7 +167,7 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { } @Override - protected void installManagement(@NotNull String name) throws PyExternalProcessException { + protected void installManagement(@NotNull String name) throws ExecutionException { super.installManagement(name); // TODO: remove temp directory for remote interpreter } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 6bb976b6b910..e100d4444d1e 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -16,6 +16,7 @@ package com.jetbrains.python.packaging.ui; import com.google.common.collect.Sets; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; @@ -127,7 +128,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { } @Override - public void finished(List exceptions) { + public void finished(List exceptions) { myPackagesTable.setPaintBusy(false); PyPackageManager packageManager = PyPackageManager.getInstance(sdk); if (!exceptions.isEmpty()) { diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 802a5ab612f4..d3e23c594ed8 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.packaging.ui; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.text.StringUtil; @@ -142,7 +143,7 @@ public class PyPackageManagementService extends PackageManagementService { }); } } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { throw new IOException(e); } return packages != null ? new ArrayList(packages) : new ArrayList(); @@ -183,14 +184,14 @@ public class PyPackageManagementService extends PackageManagementService { } @Override - public void finished(@Nullable List exceptions) { + public void finished(@Nullable List exceptions) { listener.operationFinished(packageName, toErrorDescription(exceptions)); } }); ui.install(Collections.singletonList(req), extraArgs); } - private String toErrorDescription(List exceptions) { + private String toErrorDescription(List exceptions) { String errorDescription = null; if (exceptions != null && exceptions.size() > 0) { errorDescription = PyPackageManagerUI.createDescription(exceptions, ""); @@ -208,7 +209,7 @@ public class PyPackageManagementService extends PackageManagementService { } @Override - public void finished(final List exceptions) { + public void finished(final List exceptions) { listener.operationFinished(packageName, toErrorDescription(exceptions)); } }); diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java index 1d0a4bc3ad54..ebb8813f08c9 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java @@ -17,6 +17,7 @@ package com.jetbrains.python.sdk; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; +import com.intellij.execution.ExecutionException; import com.intellij.facet.ui.FacetEditorValidator; import com.intellij.facet.ui.FacetValidatorsManager; import com.intellij.openapi.application.Application; @@ -49,7 +50,6 @@ import com.intellij.util.NullableConsumer; import com.intellij.util.PathUtil; import com.intellij.util.PlatformUtils; import com.intellij.webcore.packaging.PackagesNotificationPanel; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageService; import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; @@ -421,7 +421,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog { indicator.setText("Creating virtual environment for " + basicSdk.getName()); myPath = packageManager.createVirtualEnv(getDestination(), useGlobalSitePackages()); } - catch (final PyExternalProcessException e) { + catch (final ExecutionException e) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java index b1e8ca19acf8..0ce96cf42ea6 100644 --- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java @@ -42,7 +42,6 @@ import com.intellij.util.io.ZipUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.psi.resolve.PythonSdkPathCache; import com.jetbrains.python.remote.PythonRemoteInterpreterManager; @@ -351,7 +350,7 @@ public class PySkeletonRefresher { // Force loading packages PyPackageManager.getInstance(mySdk).getPackages(false); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { // ignore - already logged } } diff --git a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java index 64adf2093028..c121dacdeaf8 100644 --- a/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java +++ b/python/src/com/jetbrains/python/testing/VFSTestFrameworkListener.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.testing; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.components.ServiceManager; @@ -33,7 +34,6 @@ import com.intellij.util.messages.MessageBus; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import com.jetbrains.python.PyNames; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; @@ -134,7 +134,7 @@ public class VFSTestFrameworkListener implements ApplicationComponent { try { return packageManager.findPackage(testPackageName, false) != null; } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { LOG.info("Can't load package list " + e.getMessage()); } return null; diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java index 8dc27d4fd995..575690c52dae 100644 --- a/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java +++ b/python/src/com/jetbrains/python/testing/pytest/PyTestConfigurationProducer.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.testing.pytest; +import com.intellij.execution.ExecutionException; import com.intellij.execution.Location; import com.intellij.execution.actions.ConfigurationContext; import com.intellij.openapi.module.Module; @@ -28,7 +29,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.webcore.packaging.PackageVersionComparator; -import com.jetbrains.python.packaging.PyExternalProcessException; import com.jetbrains.python.packaging.PyPackage; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.psi.PyClass; @@ -106,7 +106,7 @@ public class PyTestConfigurationProducer extends PythonTestConfigurationProducer keywords = pyClass.getName() + "." + keywords; } } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { keywords = pyClass.getName() + "." + keywords; } } diff --git a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java index c4616e5e3add..c15d0dcb871d 100644 --- a/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java +++ b/python/testSrc/com/jetbrains/env/python/PyPackagingTest.java @@ -1,6 +1,7 @@ package com.jetbrains.env.python; import com.google.common.collect.Sets; +import com.intellij.execution.ExecutionException; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; @@ -44,11 +45,7 @@ public class PyPackagingTest extends PyEnvTestCase { try { packages = PyPackageManager.getInstance(sdk).getPackages(false); } - catch (PyExternalProcessException e) { - final int retcode = e.getRetcode(); - if (retcode != PyPackageManagerImpl.ERROR_NO_PIP && retcode != PyPackageManagerImpl.ERROR_NO_SETUPTOOLS) { - fail(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage())); - } + catch (ExecutionException ignored) { } if (packages != null) { assertTrue(packages.size() > 0); @@ -87,7 +84,7 @@ public class PyPackagingTest extends PyEnvTestCase { catch (IOException e) { throw new RuntimeException(e); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { throw new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e); } } @@ -123,7 +120,7 @@ public class PyPackagingTest extends PyEnvTestCase { final PyPackage pip2 = findPackage("pip", packages3); assertNull(pip2); } - catch (PyExternalProcessException e) { + catch (ExecutionException e) { new RuntimeException(String.format("Error for interpreter '%s': %s", sdk.getHomePath(), e.getMessage()), e); } catch (IOException e) { From f4d21231ee8d326e7ad21b117e9bafd0d6db72cb Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 22 Sep 2014 19:02:05 +0400 Subject: [PATCH 02/24] Cause is used only in the message passed to the constructor of PyExternalProcessException --- .../python/packaging/PyExternalProcessException.java | 8 -------- .../python/packaging/PyRemotePackageManagerImpl.java | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java index 06e233717fd6..0b4515cc77de 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java @@ -45,14 +45,6 @@ public class PyExternalProcessException extends ExecutionException { myMessage = stripLinesWithoutLineFeeds(message); } - public PyExternalProcessException(int retcode, @NotNull String name, @NotNull List args, @NotNull String message, Throwable cause) { - super(String.format("External process error '%s %s':\n%s", name, StringUtil.join(args, " "), message), cause); - myRetcode = retcode; - myName = name; - myArgs = args; - myMessage = stripLinesWithoutLineFeeds(message); - } - @Override public String toString() { final StringBuilder b = new StringBuilder(); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 2a579f16d44d..ce97a2d18773 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -148,7 +148,7 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return processOutput; } catch (ExecutionException e) { - throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e); + throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage()); } } else { From 00cdaa2b96bff7beb555297b616c3604fba381e2 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Sep 2014 17:05:44 +0400 Subject: [PATCH 03/24] 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. --- .../python/packaging/PyExecutionFix.java | 27 ++++ .../packaging/PyExternalProcessException.java | 32 ++-- .../packaging/PyPackageManagerImpl.java | 13 +- .../packaging/PyRemotePackageManagerImpl.java | 46 +++--- .../ui/PyInstalledPackagesPanel.java | 140 +++++++++--------- 5 files changed, 142 insertions(+), 116 deletions(-) create mode 100644 python/openapi/src/com/jetbrains/python/packaging/PyExecutionFix.java diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionFix.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionFix.java new file mode 100644 index 000000000000..439a3dee62fb --- /dev/null +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionFix.java @@ -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); +} diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java index 0b4515cc77de..f4b404910639 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java @@ -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 myArgs; @NotNull private String myMessage; + @NotNull private final List myFixes; - private Pair myHandler = null; + public PyExternalProcessException(@NotNull String name, @NotNull List args, @NotNull String message) { + this(name, args, message, Collections.emptyList()); + } - public PyExternalProcessException(int retcode, @NotNull String name, @NotNull List args, @NotNull String message) { + public PyExternalProcessException(@NotNull String name, @NotNull List args, @NotNull String message, + @NotNull List 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 getHandler() { - return myHandler; + @NotNull + public List getFixes() { + return myFixes; } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index d2581fd4dec2..9c0049b887ae 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -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 fields = StringUtil.split(line, "\t"); if (fields.size() < 3) { - throw new PyExternalProcessException(ERROR_INVALID_OUTPUT, PACKAGING_TOOL, Collections.emptyList(), - "Invalid output format"); + throw new PyExternalProcessException(PACKAGING_TOOL, Collections.emptyList(), "Invalid output format"); } final String name = fields.get(0); final String version = fields.get(1); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index ce97a2d18773..8b239f8039cc 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -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. Launch vagrant") - .withHandler(LAUNCH_VAGRANT, new Runnable() { - @Override - public void run() { - final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); - if (manager != null) { + final List 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. Launch vagrant", 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"); } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index e100d4444d1e..8b945da7c061 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -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 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 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.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 = "create new VirtualEnv"; 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. Install packaging tools"; - } - 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 = "" + fix.getName() + ""; + 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 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 listener : myPathChangedListeners) { - listener.consume(sdk); - } - updateNotifications(sdk); - } - }); - ui.installManagement(); - } - @Override protected boolean canUninstallPackage(InstalledPackage pkg) { if (!myHasManagement) return false; From c669ff8ce84f349d3c4450dfe760cb436a7a91a8 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Sep 2014 17:06:13 +0400 Subject: [PATCH 04/24] Renamed PyExternalProcessException to PyExecutionException --- ...ocessException.java => PyExecutionException.java} | 8 ++++---- .../python/packaging/PyPackageManagerImpl.java | 12 ++++++------ .../python/packaging/PyRemotePackageManagerImpl.java | 10 +++++----- .../packaging/ui/PyInstalledPackagesPanel.java | 8 ++++---- 4 files changed, 19 insertions(+), 19 deletions(-) rename python/openapi/src/com/jetbrains/python/packaging/{PyExternalProcessException.java => PyExecutionException.java} (86%) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java similarity index 86% rename from python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java rename to python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java index f4b404910639..3b1041ae4302 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExternalProcessException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java @@ -27,7 +27,7 @@ import java.util.regex.Pattern; /** * @author vlan */ -public class PyExternalProcessException extends ExecutionException { +public class PyExecutionException extends ExecutionException { private static final Pattern WITH_CR_DELIMITER_PATTERN = Pattern.compile("(?<=\r|\n|\r\n)"); @NotNull private String myName; @@ -35,12 +35,12 @@ public class PyExternalProcessException extends ExecutionException { @NotNull private String myMessage; @NotNull private final List myFixes; - public PyExternalProcessException(@NotNull String name, @NotNull List args, @NotNull String message) { + public PyExecutionException(@NotNull String name, @NotNull List args, @NotNull String message) { this(name, args, message, Collections.emptyList()); } - public PyExternalProcessException(@NotNull String name, @NotNull List args, @NotNull String message, - @NotNull List fixes) { + public PyExecutionException(@NotNull String name, @NotNull List args, @NotNull String message, + @NotNull List fixes) { super(String.format("External process error '%s %s':\n%s", name, StringUtil.join(args, " "), message)); myName = name; myArgs = args; diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 9c0049b887ae..f6f3e3b91b9a 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -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(path, args, "Timed out"); + throw new PyExecutionException(path, args, "Timed out"); } else if (exitCode != 0) { final String message = output.getStderr() + "\n" + output.getStdout(); - throw new PyExternalProcessException(path, args, message); + throw new PyExecutionException(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(helperPath, args, message); + throw new PyExecutionException(helperPath, args, message); } return result; } @@ -507,10 +507,10 @@ public class PyPackageManagerImpl extends PyPackageManager { throw e; } catch (ExecutionException e) { - throw new PyExternalProcessException(helperPath, args, e.getMessage()); + throw new PyExecutionException(helperPath, args, e.getMessage()); } catch (IOException e) { - throw new PyExternalProcessException(helperPath, args, e.getMessage()); + throw new PyExecutionException(helperPath, args, e.getMessage()); } } @@ -521,7 +521,7 @@ public class PyPackageManagerImpl extends PyPackageManager { for (String line : lines) { final List fields = StringUtil.split(line, "\t"); if (fields.size() < 3) { - throw new PyExternalProcessException(PACKAGING_TOOL, Collections.emptyList(), "Invalid output format"); + throw new PyExecutionException(PACKAGING_TOOL, Collections.emptyList(), "Invalid output format"); } final String name = fields.get(0); final String version = fields.get(1); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 8b239f8039cc..59efdec20d39 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -117,12 +117,12 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { } } }); - throw new PyExternalProcessException(helperPath, args, "Vagrant instance is down. Launch vagrant", fixes); } else { - throw new PyExternalProcessException(helperPath, args, e.getMessage()); + throw new PyExecutionException(helperPath, args, e.getMessage()); } } final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); @@ -154,16 +154,16 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return processOutput; } catch (ExecutionException e) { - throw new PyExternalProcessException(helperPath, args, "Error running SDK: " + e.getMessage()); + throw new PyExecutionException(helperPath, args, "Error running SDK: " + e.getMessage()); } } else { - throw new PyExternalProcessException(helperPath, args, + throw new PyExecutionException(helperPath, args, PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED); } } else { - throw new PyExternalProcessException(helperPath, args, "Invalid remote SDK"); + throw new PyExecutionException(helperPath, args, "Invalid remote SDK"); } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 8b945da7c061..1d0ca880100e 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -97,18 +97,18 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { application.executeOnPooledThread(new Runnable() { @Override public void run() { - PyExternalProcessException exception = null; + PyExecutionException exception = null; try { myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(false); if (!myHasManagement) { - throw new PyExternalProcessException("pip", Collections.emptyList(), "Python packaging tools not found", + throw new PyExecutionException("pip", Collections.emptyList(), "Python packaging tools not found", ImmutableList.of(new PyInstallPackageManagementFix())); } } - catch (PyExternalProcessException e) { + catch (PyExecutionException e) { exception = e; } - final PyExternalProcessException problem = exception; + final PyExecutionException problem = exception; application.invokeLater(new Runnable() { @Override public void run() { From 9191f7831658a3a46515f58f5d08f94c13425a25 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Sep 2014 17:23:17 +0400 Subject: [PATCH 05/24] Removed the action link from the description of the problem Links are added by traversing the list of available fixes. --- .../python/packaging/PyRemotePackageManagerImpl.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 59efdec20d39..9df1dc1dd8cd 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -117,9 +117,7 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { } } }); - throw new PyExecutionException(helperPath, args, "Vagrant instance is down. Launch vagrant", fixes); + throw new PyExecutionException(helperPath, args, "Vagrant instance is down", fixes); } else { throw new PyExecutionException(helperPath, args, e.getMessage()); From 8eb722ceee75adcf449d6d9eb4076fa833b614e9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Sep 2014 17:26:10 +0400 Subject: [PATCH 06/24] Throw PyExecutionException unchanged, since it may carry useful details --- .../com/jetbrains/python/packaging/PyPackageManagerImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index f6f3e3b91b9a..21afa145fa46 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -506,6 +506,9 @@ public class PyPackageManagerImpl extends PyPackageManager { catch (RunCanceledByUserException e) { throw e; } + catch (PyExecutionException e) { + throw e; + } catch (ExecutionException e) { throw new PyExecutionException(helperPath, args, e.getMessage()); } From 161ab6898550c46423438638b7d94b3968e3649d Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 23 Sep 2014 17:33:19 +0400 Subject: [PATCH 07/24] PyPackageManager.hasManagement() throws exceptions with PyExecutionFix for launching Vagrant --- .../actions/AbstractProjectSettingsStep.java | 9 ++++++++- .../jetbrains/python/packaging/PyPackageManager.java | 2 +- .../inspections/PyPackageRequirementsInspection.java | 8 +++++++- .../python/packaging/PyPackageManagerImpl.java | 11 +++-------- .../python/packaging/ui/PyInstalledPackagesPanel.java | 3 +++ 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java index 6514741fda45..5c440f5fad0d 100644 --- a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java +++ b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java @@ -1,5 +1,6 @@ package com.jetbrains.python.newProject.actions; +import com.intellij.execution.ExecutionException; import com.intellij.facet.ui.ValidationResult; import com.intellij.icons.AllIcons; import com.intellij.ide.impl.ProjectUtil; @@ -322,7 +323,13 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane String warningText = frameworkName + " will be installed on selected interpreter"; myInstallFramework = true; final PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - if (!packageManager.hasManagement(PySdkUtil.isRemote(sdk))) { + boolean hasManagement = false; + try { + hasManagement = packageManager.hasManagement(PySdkUtil.isRemote(sdk)); + } + catch (ExecutionException ignored) { + } + if (!hasManagement) { warningText = "Python packaging tools and " + warningText; } setWarningText(warningText); diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java index 770fb7037c56..add5b199016d 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManager.java @@ -42,7 +42,7 @@ public abstract class PyPackageManager { } public abstract void installManagement() throws ExecutionException; - public abstract boolean hasManagement(boolean cachedOnly); + public abstract boolean hasManagement(boolean cachedOnly) throws ExecutionException; public abstract void install(@NotNull String requirementString) throws ExecutionException; public abstract void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException; public abstract void uninstall(@NotNull List packages) throws ExecutionException; diff --git a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java index cc89d0b49444..300b91bf73c1 100644 --- a/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPackageRequirementsInspection.java @@ -304,7 +304,13 @@ public class PyPackageRequirementsInspection extends PyInspection { public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) { boolean installManagement = false; final PyPackageManager manager = PyPackageManager.getInstance(mySdk); - if (!manager.hasManagement(false)) { + boolean hasManagement = false; + try { + hasManagement = manager.hasManagement(false); + } + catch (ExecutionException ignored) { + } + if (!hasManagement) { final int result = Messages.showYesNoDialog(project, "Python packaging tools are required for installing packages. Do you want to " + "install 'pip' and 'setuptools' for your interpreter?", diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 21afa145fa46..2b78c69aee69 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -131,7 +131,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Override - public boolean hasManagement(boolean cachedOnly) { + public boolean hasManagement(boolean cachedOnly) throws ExecutionException { return (hasPackage(SETUPTOOLS, cachedOnly) || hasPackage(DISTRIBUTE, cachedOnly)) && hasPackage(PIP, cachedOnly); } @@ -160,13 +160,8 @@ public class PyPackageManagerImpl extends PyPackageManager { return dirName; } - private boolean hasPackage(@NotNull String name, boolean cachedOnly) { - try { - return findPackage(name, cachedOnly) != null; - } - catch (ExecutionException ignored) { - return false; - } + private boolean hasPackage(@NotNull String name, boolean cachedOnly) throws ExecutionException { + return findPackage(name, cachedOnly) != null; } PyPackageManagerImpl(@NotNull Sdk sdk) { diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 1d0ca880100e..1e4b05ecef2b 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -108,6 +108,9 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { catch (PyExecutionException e) { exception = e; } + catch (ExecutionException e) { + return; + } final PyExecutionException problem = exception; application.invokeLater(new Runnable() { @Override From 6307009906698554afe2dd81572c33b6e11384a4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 16:01:31 +0400 Subject: [PATCH 08/24] Restored "Launch Vagrant" fix for packages list panel --- .../packaging/PyPackageManagerImpl.java | 2 +- .../packaging/PyRemotePackageManagerImpl.java | 77 +++++++++++-------- .../ui/PyInstalledPackagesPanel.java | 2 + 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 2b78c69aee69..5ad0905a026f 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -409,7 +409,7 @@ public class PyPackageManagerImpl extends PyPackageManager { } @Nullable - protected String getHelperPath(String helper) { + protected String getHelperPath(String helper) throws ExecutionException { return PythonHelpersLocator.getHelperPath(helper); } diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 9df1dc1dd8cd..08a313654b60 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -36,6 +36,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -54,7 +55,7 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { @Nullable @Override - protected String getHelperPath(String helper) { + protected String getHelperPath(String helper) throws ExecutionException { final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData(); if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData(); @@ -67,9 +68,12 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return null; } } - catch (Exception e) { + catch (InterruptedException e) { LOG.error(e); } + catch (ExecutionException e) { + throw analyzeException(e, helper, Collections.emptyList()); + } } return null; } @@ -92,36 +96,10 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { } catch (InterruptedException e) { LOG.error(e); - remoteSdkCredentials = null; - } - catch (final ExecutionException e) { - if (e.getCause() instanceof VagrantNotStartedException) { - final List fixes = ImmutableList.of(new PyExecutionFix() { - @NotNull - @Override - public String getName() { - return "Launch Vagrant"; - } - - @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 PyExecutionException(helperPath, args, "Vagrant instance is down", fixes); - } - else { - throw new PyExecutionException(helperPath, args, e.getMessage()); + remoteSdkCredentials = null; } + catch (ExecutionException e) { + throw analyzeException(e, helperPath, args); } final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); if (manager != null && remoteSdkCredentials != null) { @@ -165,6 +143,15 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { } } + private ExecutionException analyzeException(ExecutionException exception, String command, List args) { + final Throwable cause = exception.getCause(); + if (cause instanceof VagrantNotStartedException) { + final String vagrantFolder = ((VagrantNotStartedException)cause).getVagrantFolder(); + return new PyExecutionException(command, args, "Vagrant instance is down", ImmutableList.of(new LaunchVagrantFix(vagrantFolder))); + } + return exception; + } + @Override protected void subscribeToLocalChanges(Sdk sdk) { // Local VFS changes aren't needed @@ -179,4 +166,32 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { private static String quoteIfNeeded(String arg) { return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode } + + private class LaunchVagrantFix implements PyExecutionFix { + @NotNull private final String myVagrantFolder; + + public LaunchVagrantFix(@NotNull String vagrantFolder) { + myVagrantFolder = vagrantFolder; + } + + @NotNull + @Override + public String getName() { + return "Launch Vagrant"; + } + + @Override + public void run(@NotNull Sdk sdk) { + final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance(); + if (manager != null) { + try { + manager.runVagrant(myVagrantFolder); + clearCaches(); + } + catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + } + } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 1e4b05ecef2b..d8ffc600627a 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -134,6 +134,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { if (sdk != null) { fix.run(sdk); myNotificationArea.removeLinkHandler(key); + updatePackages(new PyPackageManagementService(myProject, sdk)); + updateNotifications(sdk); } } }); From 82dc5b88c35205ec6b93e81009061beb6ad28853 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 16:41:30 +0400 Subject: [PATCH 09/24] Restored "Install packaging tools" fix when there is no 'setuptools' installed --- python/helpers/packaging_tool.py | 4 +-- .../packaging/PyExecutionException.java | 26 ++++++++++------- .../packaging/PyPackageManagerImpl.java | 29 +++++++++++++------ .../packaging/PyRemotePackageManagerImpl.java | 9 +++--- .../ui/PyInstalledPackagesPanel.java | 2 +- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/python/helpers/packaging_tool.py b/python/helpers/packaging_tool.py index b2b2e81a5acd..58b60a342859 100644 --- a/python/helpers/packaging_tool.py +++ b/python/helpers/packaging_tool.py @@ -35,7 +35,7 @@ def error(message, retcode): def error_no_pip(): tb = sys.exc_traceback if tb is not None and tb.tb_next is None: - error("Python package management tool 'pip' not found", ERROR_NO_PIP) + error("Python packaging tool 'pip' not found", ERROR_NO_PIP) else: error(traceback.format_exc(), ERROR_EXCEPTION) @@ -44,7 +44,7 @@ def do_list(): try: import pkg_resources except ImportError: - error("Python package management tool 'setuptools' or 'distribute' not found", ERROR_NO_SETUPTOOLS) + error("Python packaging tool 'setuptools' not found", ERROR_NO_SETUPTOOLS) for pkg in pkg_resources.working_set: requires = ':'.join([str(x) for x in pkg.requires()]) sys.stdout.write('\t'.join([pkg.project_name, pkg.version, pkg.location, requires])+chr(10)) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java index 3b1041ae4302..af4e5e09716a 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java @@ -30,20 +30,22 @@ import java.util.regex.Pattern; public class PyExecutionException extends ExecutionException { private static final Pattern WITH_CR_DELIMITER_PATTERN = Pattern.compile("(?<=\r|\n|\r\n)"); - @NotNull private String myName; + @NotNull private String myCommand; @NotNull private List myArgs; + private final int myReturnCode; @NotNull private String myMessage; @NotNull private final List myFixes; - public PyExecutionException(@NotNull String name, @NotNull List args, @NotNull String message) { - this(name, args, message, Collections.emptyList()); + public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, int returnCode) { + this(message, command, args, returnCode, Collections.emptyList()); } - public PyExecutionException(@NotNull String name, @NotNull List args, @NotNull String message, - @NotNull List fixes) { - super(String.format("External process error '%s %s':\n%s", name, StringUtil.join(args, " "), message)); - myName = name; + public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, + int returnCode, @NotNull List fixes) { + super(message); + myCommand = command; myArgs = args; + myReturnCode = returnCode; myMessage = stripLinesWithoutLineFeeds(message); myFixes = fixes; } @@ -52,7 +54,7 @@ public class PyExecutionException extends ExecutionException { public String toString() { final StringBuilder b = new StringBuilder(); b.append("The following command was executed:\n\n"); - final String command = getName() + " " + StringUtil.join(getArgs(), " "); + final String command = getCommand() + " " + StringUtil.join(getArgs(), " "); b.append(command); b.append("\n\n"); b.append("The error output of the command:\n\n"); @@ -61,8 +63,8 @@ public class PyExecutionException extends ExecutionException { } @NotNull - public String getName() { - return myName; + public String getCommand() { + return myCommand; } @NotNull @@ -91,4 +93,8 @@ public class PyExecutionException extends ExecutionException { public List getFixes() { return myFixes; } + + public int getReturnCode() { + return myReturnCode; + } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 5ad0905a026f..820c24f09f02 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -120,7 +120,7 @@ public class PyPackageManagerImpl extends PyPackageManager { @Override public void installManagement() throws ExecutionException { final boolean pre26 = PythonSdkType.getLanguageLevelForSdk(mySdk).isOlderThan(LanguageLevel.PYTHON26); - if (!hasPackage(SETUPTOOLS, false) && !hasPackage(DISTRIBUTE, false)) { + if (!hasSetuptools(false)) { final String name = SETUPTOOLS + "-" + (pre26 ? SETUPTOOLS_PRE_26_VERSION : SETUPTOOLS_VERSION); installManagement(name); } @@ -132,8 +132,19 @@ public class PyPackageManagerImpl extends PyPackageManager { @Override public boolean hasManagement(boolean cachedOnly) throws ExecutionException { - return (hasPackage(SETUPTOOLS, cachedOnly) || hasPackage(DISTRIBUTE, cachedOnly)) && - hasPackage(PIP, cachedOnly); + return hasSetuptools(cachedOnly) && hasPackage(PIP, cachedOnly); + } + + private boolean hasSetuptools(boolean cachedOnly) throws ExecutionException { + try { + return hasPackage(SETUPTOOLS, cachedOnly) || hasPackage(DISTRIBUTE, cachedOnly); + } + catch (PyExecutionException e) { + if (e.getReturnCode() == ERROR_NO_SETUPTOOLS) { + return false; + } + throw e; + } } protected void installManagement(@NotNull String name) throws ExecutionException { @@ -419,11 +430,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 PyExecutionException(path, args, "Timed out"); + throw new PyExecutionException("Timed out", path, args, exitCode); } else if (exitCode != 0) { final String message = output.getStderr() + "\n" + output.getStdout(); - throw new PyExecutionException(path, args, message); + throw new PyExecutionException(message, path, args, exitCode); } return output.getStdout(); } @@ -494,7 +505,7 @@ public class PyPackageManagerImpl extends PyPackageManager { if (StringUtil.isEmptyOrSpaces(message)) { message = "Failed to perform action. Permission denied."; } - throw new PyExecutionException(helperPath, args, message); + throw new PyExecutionException(message, helperPath, args, result.getExitCode()); } return result; } @@ -505,10 +516,10 @@ public class PyPackageManagerImpl extends PyPackageManager { throw e; } catch (ExecutionException e) { - throw new PyExecutionException(helperPath, args, e.getMessage()); + throw new PyExecutionException(e.getMessage(), helperPath, args, 0); } catch (IOException e) { - throw new PyExecutionException(helperPath, args, e.getMessage()); + throw new PyExecutionException(e.getMessage(), helperPath, args, 0); } } @@ -519,7 +530,7 @@ public class PyPackageManagerImpl extends PyPackageManager { for (String line : lines) { final List fields = StringUtil.split(line, "\t"); if (fields.size() < 3) { - throw new PyExecutionException(PACKAGING_TOOL, Collections.emptyList(), "Invalid output format"); + throw new PyExecutionException("Invalid output format", PACKAGING_TOOL, Collections.emptyList(), 0); } final String name = fields.get(0); final String version = fields.get(1); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 08a313654b60..eafdbe053a1b 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -130,16 +130,15 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return processOutput; } catch (ExecutionException e) { - throw new PyExecutionException(helperPath, args, "Error running SDK: " + e.getMessage()); + throw new PyExecutionException("Error running SDK: " + e.getMessage(), helperPath, args, 0); } } else { - throw new PyExecutionException(helperPath, args, - PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED); + throw new PyExecutionException(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED, helperPath, args, 0); } } else { - throw new PyExecutionException(helperPath, args, "Invalid remote SDK"); + throw new PyExecutionException("Invalid remote SDK", helperPath, args, 0); } } @@ -147,7 +146,7 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { final Throwable cause = exception.getCause(); if (cause instanceof VagrantNotStartedException) { final String vagrantFolder = ((VagrantNotStartedException)cause).getVagrantFolder(); - return new PyExecutionException(command, args, "Vagrant instance is down", ImmutableList.of(new LaunchVagrantFix(vagrantFolder))); + return new PyExecutionException("Vagrant instance is down", command, args, 0, ImmutableList.of(new LaunchVagrantFix(vagrantFolder))); } return exception; } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index d8ffc600627a..5875c16e1f73 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -101,7 +101,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { try { myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(false); if (!myHasManagement) { - throw new PyExecutionException("pip", Collections.emptyList(), "Python packaging tools not found", + throw new PyExecutionException("Python packaging tools not found", "pip", Collections.emptyList(), 0, ImmutableList.of(new PyInstallPackageManagementFix())); } } From c4a75002ec148acfe164f6acdb183cf4b25273b0 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 16:42:26 +0400 Subject: [PATCH 10/24] Removed unused constants --- .../jetbrains/python/packaging/PyPackageManagerImpl.java | 7 ------- .../python/packaging/PyRemotePackageManagerImpl.java | 4 ---- 2 files changed, 11 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 820c24f09f02..f74b1cceaa1c 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -70,14 +70,7 @@ public class PyPackageManagerImpl extends PyPackageManager { public static final String VIRTUALENV_VERSION = "1.11.6"; public static final int OK = 0; - public static final int ERROR_NO_PIP = 2; public static final int ERROR_NO_SETUPTOOLS = 3; - public static final int ERROR_INVALID_SDK = -1; - public static final int ERROR_TOOL_NOT_FOUND = -2; - public static final int ERROR_TIMEOUT = -3; - public static final int ERROR_INVALID_OUTPUT = -4; - public static final int ERROR_ACCESS_DENIED = -5; - public static final int ERROR_EXECUTION = -6; private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index eafdbe053a1b..b5bb22c5c588 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -43,10 +43,6 @@ import java.util.List; * @author vlan */ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { - private static final String LAUNCH_VAGRANT = "launchVagrant"; - public static final int ERROR_VAGRANT_NOT_LAUNCHED = 101; - public static final int ERROR_REMOTE_ACCESS = 102; - private static final Logger LOG = Logger.getInstance(PyRemotePackageManagerImpl.class); PyRemotePackageManagerImpl(@NotNull Sdk sdk) { From 72dbf4f682e0307d266690889b94faa83935a92d Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 16:50:27 +0400 Subject: [PATCH 11/24] Don't substitute ExecutionExeption with PyExecutionException --- .../packaging/PyPackageManagerImpl.java | 9 ------ .../packaging/PyRemotePackageManagerImpl.java | 31 ++++++++----------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index f74b1cceaa1c..95624c3d44be 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -502,15 +502,6 @@ public class PyPackageManagerImpl extends PyPackageManager { } return result; } - catch (RunCanceledByUserException e) { - throw e; - } - catch (PyExecutionException e) { - throw e; - } - catch (ExecutionException e) { - throw new PyExecutionException(e.getMessage(), helperPath, args, 0); - } catch (IOException e) { throw new PyExecutionException(e.getMessage(), helperPath, args, 0); } diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index b5bb22c5c588..22981e53b9ec 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -108,26 +108,21 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return quoteIfNeeded(input); } })); - try { - if (askForSudo) { - askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath()); - } - ProcessOutput processOutput; - do { - PathMappingSettings mappings = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null); - processOutput = - manager.runRemoteProcess(null, remoteSdkCredentials, mappings, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo); - if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) { - continue; - } - break; - } - while (true); - return processOutput; + if (askForSudo) { + askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath()); } - catch (ExecutionException e) { - throw new PyExecutionException("Error running SDK: " + e.getMessage(), helperPath, args, 0); + ProcessOutput processOutput; + do { + PathMappingSettings mappings = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null); + processOutput = + manager.runRemoteProcess(null, remoteSdkCredentials, mappings, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo); + if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) { + continue; + } + break; } + while (true); + return processOutput; } else { throw new PyExecutionException(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED, helperPath, args, 0); From 0cb60b64b88cc2a249e2523d314d0fa4dc5a641b Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 16:53:55 +0400 Subject: [PATCH 12/24] Renamed PyExecutionException.getReturnCode() to getExitCode() for consistency with ProcessOutput --- .../python/packaging/PyExecutionException.java | 14 +++++++------- .../python/packaging/PyPackageManagerImpl.java | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java index af4e5e09716a..5c2d56dfe5e2 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java @@ -32,20 +32,20 @@ public class PyExecutionException extends ExecutionException { @NotNull private String myCommand; @NotNull private List myArgs; - private final int myReturnCode; + private final int myExitCode; @NotNull private String myMessage; @NotNull private final List myFixes; - public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, int returnCode) { - this(message, command, args, returnCode, Collections.emptyList()); + public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, int exitCode) { + this(message, command, args, exitCode, Collections.emptyList()); } public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, - int returnCode, @NotNull List fixes) { + int exitCode, @NotNull List fixes) { super(message); myCommand = command; myArgs = args; - myReturnCode = returnCode; + myExitCode = exitCode; myMessage = stripLinesWithoutLineFeeds(message); myFixes = fixes; } @@ -94,7 +94,7 @@ public class PyExecutionException extends ExecutionException { return myFixes; } - public int getReturnCode() { - return myReturnCode; + public int getExitCode() { + return myExitCode; } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 95624c3d44be..48e2f2e779ac 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -133,7 +133,7 @@ public class PyPackageManagerImpl extends PyPackageManager { return hasPackage(SETUPTOOLS, cachedOnly) || hasPackage(DISTRIBUTE, cachedOnly); } catch (PyExecutionException e) { - if (e.getReturnCode() == ERROR_NO_SETUPTOOLS) { + if (e.getExitCode() == ERROR_NO_SETUPTOOLS) { return false; } throw e; From f249aa6e8dccd2d725b3ba5e7923c33fa3f2dbfe Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 29 Sep 2014 17:56:31 +0400 Subject: [PATCH 13/24] Pass stdout and stderr to PyExecutionException --- .../packaging/PyExecutionException.java | 30 ++++++++++++++++--- .../packaging/PyPackageManagerImpl.java | 11 ++++--- .../packaging/PyRemotePackageManagerImpl.java | 7 +++-- .../ui/PyInstalledPackagesPanel.java | 2 +- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java index 5c2d56dfe5e2..cbaccb3901ab 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java @@ -16,6 +16,7 @@ package com.jetbrains.python.packaging; import com.intellij.execution.ExecutionException; +import com.intellij.execution.process.ProcessOutput; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; @@ -32,19 +33,28 @@ public class PyExecutionException extends ExecutionException { @NotNull private String myCommand; @NotNull private List myArgs; + @NotNull private final String myStdout; + @NotNull private final String myStderr; private final int myExitCode; @NotNull private String myMessage; @NotNull private final List myFixes; - public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, int exitCode) { - this(message, command, args, exitCode, Collections.emptyList()); + public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args) { + this(message, command, args, "", "", 0, Collections.emptyList()); + } + + public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, @NotNull ProcessOutput output) { + this(message, command, args, output.getStdout(), output.getStderr(), output.getExitCode(), Collections.emptyList()); } public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args, - int exitCode, @NotNull List fixes) { + @NotNull String stdout, @NotNull String stderr, int exitCode, + @NotNull List fixes) { super(message); myCommand = command; myArgs = args; + myStdout = stdout; + myStderr = stderr; myExitCode = exitCode; myMessage = stripLinesWithoutLineFeeds(message); myFixes = fixes; @@ -58,7 +68,9 @@ public class PyExecutionException extends ExecutionException { b.append(command); b.append("\n\n"); b.append("The error output of the command:\n\n"); - b.append(getMessage()); + b.append(myStdout); + b.append("\n"); + b.append(myStderr); return b.toString(); } @@ -97,4 +109,14 @@ public class PyExecutionException extends ExecutionException { public int getExitCode() { return myExitCode; } + + @NotNull + public String getStdout() { + return myStdout; + } + + @NotNull + public String getStderr() { + return myStderr; + } } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 48e2f2e779ac..bcebf64b827b 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -423,11 +423,10 @@ public class PyPackageManagerImpl extends PyPackageManager { final ProcessOutput output = getPythonProcessOutput(path, args, askForSudo, showProgress, workingDir); final int exitCode = output.getExitCode(); if (output.isTimeout()) { - throw new PyExecutionException("Timed out", path, args, exitCode); + throw new PyExecutionException("Timed out", path, args, output); } else if (exitCode != 0) { - final String message = output.getStderr() + "\n" + output.getStdout(); - throw new PyExecutionException(message, path, args, exitCode); + throw new PyExecutionException("Non-zero exit code", path, args, output); } return output.getStdout(); } @@ -498,12 +497,12 @@ public class PyPackageManagerImpl extends PyPackageManager { if (StringUtil.isEmptyOrSpaces(message)) { message = "Failed to perform action. Permission denied."; } - throw new PyExecutionException(message, helperPath, args, result.getExitCode()); + throw new PyExecutionException(message, helperPath, args, result); } return result; } catch (IOException e) { - throw new PyExecutionException(e.getMessage(), helperPath, args, 0); + throw new PyExecutionException(e.getMessage(), helperPath, args); } } @@ -514,7 +513,7 @@ public class PyPackageManagerImpl extends PyPackageManager { for (String line : lines) { final List fields = StringUtil.split(line, "\t"); if (fields.size() < 3) { - throw new PyExecutionException("Invalid output format", PACKAGING_TOOL, Collections.emptyList(), 0); + throw new PyExecutionException("Invalid output format", PACKAGING_TOOL, Collections.emptyList()); } final String name = fields.get(0); final String version = fields.get(1); diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java index 22981e53b9ec..22045d1565ea 100644 --- a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java @@ -125,11 +125,11 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { return processOutput; } else { - throw new PyExecutionException(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED, helperPath, args, 0); + throw new PyExecutionException(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED, helperPath, args); } } else { - throw new PyExecutionException("Invalid remote SDK", helperPath, args, 0); + throw new PyExecutionException("Invalid remote SDK", helperPath, args); } } @@ -137,7 +137,8 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl { final Throwable cause = exception.getCause(); if (cause instanceof VagrantNotStartedException) { final String vagrantFolder = ((VagrantNotStartedException)cause).getVagrantFolder(); - return new PyExecutionException("Vagrant instance is down", command, args, 0, ImmutableList.of(new LaunchVagrantFix(vagrantFolder))); + return new PyExecutionException("Vagrant instance is down", command, args, "", "", 0, + ImmutableList.of(new LaunchVagrantFix(vagrantFolder))); } return exception; } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 5875c16e1f73..8d8c163fee29 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -101,7 +101,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { try { myHasManagement = PyPackageManager.getInstance(selectedSdk).hasManagement(false); if (!myHasManagement) { - throw new PyExecutionException("Python packaging tools not found", "pip", Collections.emptyList(), 0, + throw new PyExecutionException("Python packaging tools not found", "pip", Collections.emptyList(), "", "", 0, ImmutableList.of(new PyInstallPackageManagementFix())); } } From 004d6cf844c33f99119ed4db172ee903aaaecf39 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 30 Sep 2014 17:35:47 +0400 Subject: [PATCH 14/24] New package operation failure dialog --- .../packaging/PyExecutionException.java | 23 ---- .../packaging/PyPackageManagerImpl.java | 13 +- .../python/packaging/PyPackageManagerUI.java | 65 +++++++++- .../ui/PyPackageNotificationDialog.form | 122 ++++++++++++++++++ .../ui/PyPackageNotificationDialog.java | 63 +++++++++ 5 files changed, 250 insertions(+), 36 deletions(-) create mode 100644 python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form create mode 100644 python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java index cbaccb3901ab..e2a7ae1697d1 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyExecutionException.java @@ -20,23 +20,18 @@ import com.intellij.execution.process.ProcessOutput; 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; /** * @author vlan */ public class PyExecutionException extends ExecutionException { - private static final Pattern WITH_CR_DELIMITER_PATTERN = Pattern.compile("(?<=\r|\n|\r\n)"); - @NotNull private String myCommand; @NotNull private List myArgs; @NotNull private final String myStdout; @NotNull private final String myStderr; private final int myExitCode; - @NotNull private String myMessage; @NotNull private final List myFixes; public PyExecutionException(@NotNull String message, @NotNull String command, @NotNull List args) { @@ -56,7 +51,6 @@ public class PyExecutionException extends ExecutionException { myStdout = stdout; myStderr = stderr; myExitCode = exitCode; - myMessage = stripLinesWithoutLineFeeds(message); myFixes = fixes; } @@ -84,23 +78,6 @@ public class PyExecutionException extends ExecutionException { return myArgs; } - @NotNull - public String getMessage() { - return myMessage; - } - - @NotNull - private static String stripLinesWithoutLineFeeds(@NotNull String s) { - final String[] lines = WITH_CR_DELIMITER_PATTERN.split(s); - final List result = new ArrayList(); - for (String line : lines) { - if (!line.endsWith("\r")) { - result.add(line); - } - } - return StringUtil.join(result, ""); - } - @NotNull public List getFixes() { return myFixes; diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index bcebf64b827b..e17fe0b28467 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -488,15 +488,10 @@ public class PyPackageManagerImpl extends PyPackageManager { if (result.isCancelled()) { throw new RunCanceledByUserException(); } - String message = result.getStderr(); - if (result.getExitCode() != 0) { - final String stdout = result.getStdout(); - if (StringUtil.isEmptyOrSpaces(message)) { - message = stdout; - } - if (StringUtil.isEmptyOrSpaces(message)) { - message = "Failed to perform action. Permission denied."; - } + final int exitCode = result.getExitCode(); + if (exitCode != 0) { + final String message = StringUtil.isEmptyOrSpaces(result.getStdout()) && StringUtil.isEmptyOrSpaces(result.getStderr()) ? + "Permission denied" : "Non-zero exit code"; throw new PyExecutionException(message, helperPath, args, result); } return result; diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index 1ddb2a9c1ae3..c911e8fa0931 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -33,18 +33,22 @@ 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 com.jetbrains.python.packaging.ui.PyPackageNotificationDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author vlan */ public class PyPackageManagerUI { - private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class); + @NotNull private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class); + @NotNull private static final Pattern PATTERN_ERROR_LINE = Pattern.compile(".*error:.*", Pattern.CASE_INSENSITIVE); + @Nullable private Listener myListener; @NotNull private Project myProject; @NotNull private Sdk mySdk; @@ -55,6 +59,33 @@ public class PyPackageManagerUI { void finished(List exceptions); } + public static class ExecutionFailure { + @NotNull private final String myMessage; + @Nullable private final String myCommand; + @Nullable private final String myOutput; + + public ExecutionFailure(@NotNull String message, @Nullable String command, @Nullable String output) { + myMessage = message; + myCommand = command; + myOutput = output; + } + + @NotNull + public String getMessage() { + return myMessage; + } + + @Nullable + public String getCommand() { + return myCommand; + } + + @Nullable + public String getOutput() { + return myOutput; + } + } + public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) { myProject = project; mySdk = sdk; @@ -184,8 +215,8 @@ public class PyPackageManagerUI { NotificationType.INFORMATION)); } else if (!isCancelled(exceptions)) { + final ExecutionFailure failure = analyzeException(exceptions.get(0)); final String firstLine = getTitle() + ": error occurred."; - final String description = createDescription(exceptions, firstLine); notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), firstLine + " Details...", NotificationType.ERROR, @@ -194,7 +225,9 @@ public class PyPackageManagerUI { public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { assert myProject != null; - PackagesNotificationPanel.showError(myProject, getFailureTitle(), description); + final String title = StringUtil.capitalizeWords(getFailureTitle(), true); + final PyPackageNotificationDialog dialog = new PyPackageNotificationDialog(title, failure); + dialog.show(); } } )); @@ -386,4 +419,28 @@ public class PyPackageManagerUI { } return b.toString(); } + + @NotNull + private static ExecutionFailure analyzeException(@NotNull ExecutionException e) { + if (e instanceof PyExecutionException) { + final PyExecutionException ee = (PyExecutionException)e; + final String stdoutCause = findErrorCause(ee.getStdout()); + final String stderrCause = findErrorCause(ee.getStderr()); + final String message = stdoutCause != null ? stdoutCause : stderrCause != null ? stderrCause : ee.getMessage(); + return new ExecutionFailure(message, ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "), ee.getStdout()); + } + else { + return new ExecutionFailure(e.getMessage(), null, null); + } + } + + @Nullable + private static String findErrorCause(@NotNull String output) { + final Matcher m = PATTERN_ERROR_LINE.matcher(output); + if (m.find()) { + final String result = m.group(); + return result != null ? result.trim() : null; + } + return null; + } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form new file mode 100644 index 000000000000..bb2c95609a76 --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form @@ -0,0 +1,122 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java new file mode 100644 index 000000000000..0c9fba0c6dc2 --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java @@ -0,0 +1,63 @@ +/* + * 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.ui; + +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.ui.components.JBLabel; +import com.jetbrains.python.packaging.PyPackageManagerUI; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author vlan + */ +public class PyPackageNotificationDialog extends DialogWrapper { + private JPanel myMainPanel; + private JBLabel myErrorMessage; + private JTextArea myCommand; + private JTextArea myCommandOutput; + private JPanel myCommandOutputPanel; + private JPanel myCommandPanel; + + public PyPackageNotificationDialog(@NotNull String title, @NotNull PyPackageManagerUI.ExecutionFailure failure) { + super(false); + init(); + setResizable(false); + setTitle(title); + final String command = failure.getCommand(); + final String output = failure.getOutput(); + final String message = failure.getMessage(); + + myCommandPanel.setVisible(command != null); + myCommandOutputPanel.setVisible(output != null); + + myErrorMessage.setText(message); + if (command != null) { + myCommand.setText(command); + } + if (output != null) { + myCommandOutput.setText(output); + } + } + + @Nullable + @Override + protected JComponent createCenterPanel() { + return myMainPanel; + } +} From 2e8380321ab7df8450a1a279303d6f810ae2ea1c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Oct 2014 15:54:47 +0400 Subject: [PATCH 15/24] Show simplified installation command and proposed solution on installation failure --- .../packaging/PyPackageManagerImpl.java | 18 ++- .../python/packaging/PyPackageManagerUI.java | 37 ++++-- .../ui/PyPackageNotificationDialog.form | 118 +++++++++++------- .../ui/PyPackageNotificationDialog.java | 9 +- 4 files changed, 120 insertions(+), 62 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index e17fe0b28467..1dd52aa92b34 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -218,6 +218,19 @@ public class PyPackageManagerImpl extends PyPackageManager { try { getHelperResult(PACKAGING_TOOL, args, !useUserSite, true, null); } + catch (PyExecutionException e) { + final List simplifiedArgs = new ArrayList(); + simplifiedArgs.add("install"); + if (proxyString != null) { + simplifiedArgs.add("--proxy"); + simplifiedArgs.add(proxyString); + } + simplifiedArgs.addAll(extraArgs); + for (PyRequirement req : requirements) { + simplifiedArgs.addAll(req.toOptions()); + } + throw new PyExecutionException(e.getMessage(), "pip", simplifiedArgs, e.getStdout(), e.getStderr(), e.getExitCode(), e.getFixes()); + } finally { clearCaches(); FileUtil.delete(buildDir); @@ -225,8 +238,8 @@ public class PyPackageManagerImpl extends PyPackageManager { } public void uninstall(@NotNull List packages) throws ExecutionException { + final List args = new ArrayList(); try { - final List args = new ArrayList(); args.add(UNINSTALL); boolean canModify = true; for (PyPackage pkg : packages) { @@ -240,6 +253,9 @@ public class PyPackageManagerImpl extends PyPackageManager { } getHelperResult(PACKAGING_TOOL, args, !canModify, true, null); } + catch (PyExecutionException e) { + throw new PyExecutionException(e.getMessage(), "pip", args, e.getStdout(), e.getStderr(), e.getExitCode(), e.getFixes()); + } finally { clearCaches(); } diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index c911e8fa0931..df13fdb489c3 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -63,11 +63,13 @@ public class PyPackageManagerUI { @NotNull private final String myMessage; @Nullable private final String myCommand; @Nullable private final String myOutput; + @Nullable private final String mySolution; - public ExecutionFailure(@NotNull String message, @Nullable String command, @Nullable String output) { + public ExecutionFailure(@NotNull String message, @Nullable String command, @Nullable String output, @Nullable String solution) { myMessage = message; myCommand = command; myOutput = output; + mySolution = solution; } @NotNull @@ -84,6 +86,11 @@ public class PyPackageManagerUI { public String getOutput() { return myOutput; } + + @Nullable + public String getSolution() { + return mySolution; + } } public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) { @@ -171,10 +178,12 @@ public class PyPackageManagerUI { private abstract static class PackagingTask extends Task.Backgroundable { private static final String PACKAGING_GROUP_ID = "Packaging"; + @NotNull protected final Sdk mySdk; @Nullable protected final Listener myListener; - public PackagingTask(@Nullable Project project, @NotNull String title, @Nullable Listener listener) { + public PackagingTask(@Nullable Project project, @NotNull Sdk sdk, @NotNull String title, @Nullable Listener listener) { super(project, title); + mySdk = sdk; myListener = listener; } @@ -215,7 +224,7 @@ public class PyPackageManagerUI { NotificationType.INFORMATION)); } else if (!isCancelled(exceptions)) { - final ExecutionFailure failure = analyzeException(exceptions.get(0)); + final ExecutionFailure failure = analyzeException(exceptions.get(0), mySdk); final String firstLine = getTitle() + ": error occurred."; notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), firstLine + " Details...", @@ -257,7 +266,6 @@ public class PyPackageManagerUI { } private static class InstallTask extends PackagingTask { - @NotNull protected final Sdk mySdk; @NotNull private final List myRequirements; @NotNull private final List myExtraArgs; @@ -266,8 +274,7 @@ public class PyPackageManagerUI { @NotNull List requirements, @NotNull List extraArgs, @Nullable Listener listener) { - super(project, "Installing packages", listener); - mySdk = sdk; + super(project, sdk, "Installing packages", listener); myRequirements = requirements; myExtraArgs = extraArgs; } @@ -355,15 +362,13 @@ public class PyPackageManagerUI { } private static class UninstallTask extends PackagingTask { - @NotNull private final Sdk mySdk; @NotNull private final List myPackages; public UninstallTask(@Nullable Project project, @NotNull Sdk sdk, @Nullable Listener listener, @NotNull List packages) { - super(project, "Uninstalling packages", listener); - mySdk = sdk; + super(project, sdk, "Uninstalling packages", listener); myPackages = packages; } @@ -421,16 +426,24 @@ public class PyPackageManagerUI { } @NotNull - private static ExecutionFailure analyzeException(@NotNull ExecutionException e) { + private static ExecutionFailure analyzeException(@NotNull ExecutionException e, @NotNull Sdk sdk) { if (e instanceof PyExecutionException) { final PyExecutionException ee = (PyExecutionException)e; final String stdoutCause = findErrorCause(ee.getStdout()); final String stderrCause = findErrorCause(ee.getStderr()); final String message = stdoutCause != null ? stdoutCause : stderrCause != null ? stderrCause : ee.getMessage(); - return new ExecutionFailure(message, ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "), ee.getStdout()); + final String solution; + if ("pip".equals(ee.getCommand())) { + solution = "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 '" + sdk.getHomePath() + "'."; + } + else { + solution = null; + } + return new ExecutionFailure(message, ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "), ee.getStdout(), solution); } else { - return new ExecutionFailure(e.getMessage(), null, null); + return new ExecutionFailure(e.getMessage(), null, null, null); } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form index bb2c95609a76..e9a0ca36ae8a 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form @@ -1,49 +1,22 @@
- + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -81,7 +54,7 @@ - + @@ -94,27 +67,76 @@ - + - - - + + + - + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java index 0c9fba0c6dc2..908e79c20bb2 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java @@ -29,10 +29,12 @@ import javax.swing.*; public class PyPackageNotificationDialog extends DialogWrapper { private JPanel myMainPanel; private JBLabel myErrorMessage; - private JTextArea myCommand; private JTextArea myCommandOutput; private JPanel myCommandOutputPanel; private JPanel myCommandPanel; + private JTextPane myCommand; + private JPanel mySolutionPanel; + private JTextPane mySolution; public PyPackageNotificationDialog(@NotNull String title, @NotNull PyPackageManagerUI.ExecutionFailure failure) { super(false); @@ -42,9 +44,11 @@ public class PyPackageNotificationDialog extends DialogWrapper { final String command = failure.getCommand(); final String output = failure.getOutput(); final String message = failure.getMessage(); + final String solution = failure.getSolution(); myCommandPanel.setVisible(command != null); myCommandOutputPanel.setVisible(output != null); + mySolutionPanel.setVisible(solution != null); myErrorMessage.setText(message); if (command != null) { @@ -53,6 +57,9 @@ public class PyPackageNotificationDialog extends DialogWrapper { if (output != null) { myCommandOutput.setText(output); } + if (solution != null) { + mySolution.setText(solution); + } } @Nullable From 59902bac843bb8a4dec3aa6437fb6caf33c7f4ba Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Oct 2014 15:57:29 +0400 Subject: [PATCH 16/24] Fixed CME in PyPackageManagerImpl.findPackage() (EA-61108) --- .../com/jetbrains/python/packaging/PyPackageManagerImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java index 1dd52aa92b34..1c518cf40a58 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java @@ -265,7 +265,7 @@ public class PyPackageManagerImpl extends PyPackageManager { public List getPackages(boolean cachedOnly) throws ExecutionException { synchronized (myCacheLock) { if (myPackagesCache != null) { - return myPackagesCache; + return new ArrayList(myPackagesCache); } if (myExceptionCache != null) { throw myExceptionCache; @@ -279,8 +279,8 @@ public class PyPackageManagerImpl extends PyPackageManager { final List packages = parsePackagingToolOutput(output); synchronized (myCacheLock) { myPackagesCache = packages; + return new ArrayList(myPackagesCache); } - return packages; } catch (ExecutionException e) { synchronized (myCacheLock) { From 98c11c5ced2afd949866eb2b58eedf783383965d Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 1 Oct 2014 18:11:48 +0400 Subject: [PATCH 17/24] PackageManagementService uses ErrorDescription instead of String for error reporting --- .../packaging/InstalledPackagesPanel.java | 10 ++-- .../packaging/ManagePackagesDialog.java | 5 +- .../packaging/PackageManagementService.java | 57 ++++++++++++++++++- .../packaging/PackagesNotificationPanel.java | 7 +-- .../ui/PyPackageManagementService.java | 9 +-- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index eb2936a058d5..069b3f646096 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -152,7 +152,7 @@ public class InstalledPackagesPanel extends JPanel { @Override public void operationFinished(String packageName, - @Nullable String errorDescription) { + @Nullable PackageManagementService.ErrorDescription errorDescription) { myNotificationArea.showResult(packageName, errorDescription); myPackagesTable.clearSelection(); doUpdatePackages(myPackageManagementService); @@ -230,7 +230,8 @@ public class InstalledPackagesPanel extends JPanel { } @Override - public void operationFinished(final String packageName, @Nullable final String errorDescription) { + public void operationFinished(final String packageName, + @Nullable final PackageManagementService.ErrorDescription errorDescription) { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { @@ -244,7 +245,7 @@ public class InstalledPackagesPanel extends JPanel { else { myNotificationArea.showError("Upgrade packages failed. Details...", "Upgrade Packages Failed", - "Upgrade packages failed.\n" + errorDescription); + "Upgrade packages failed.\n" + errorDescription.getMessage()); } if (myCurrentlyInstalling.isEmpty() && !myWaitingToUpgrade.isEmpty()) { @@ -340,7 +341,8 @@ public class InstalledPackagesPanel extends JPanel { } @Override - public void operationFinished(final String packageName, @Nullable final String errorDescription) { + public void operationFinished(final String packageName, + @Nullable final PackageManagementService.ErrorDescription errorDescription) { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java index f66982282fa0..13a34cfefce2 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java @@ -247,7 +247,8 @@ public class ManagePackagesDialog extends DialogWrapper { } @Override - public void operationFinished(final String packageName, @Nullable final String errorDescription) { + public void operationFinished(final String packageName, + @Nullable final PackageManagementService.ErrorDescription errorDescription) { if (!ApplicationManager.getApplication().isDispatchThread()) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override @@ -277,7 +278,7 @@ public class ManagePackagesDialog extends DialogWrapper { myPackages.repaint(); } - private void handleInstallationFinished(String packageName, String errorDescription) { + private void handleInstallationFinished(String packageName, PackageManagementService.ErrorDescription errorDescription) { if (myPackageListener != null) { myPackageListener.operationFinished(packageName, errorDescription); } diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackageManagementService.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackageManagementService.java index 2276bb71db31..d1f775023a3e 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackageManagementService.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackageManagementService.java @@ -1,6 +1,7 @@ package com.intellij.webcore.packaging; import com.intellij.util.CatchingConsumer; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -130,10 +131,60 @@ public abstract class PackageManagementService { /** * Fired when the installation of the specified package has been completed (successfully or unsuccessfully). - * - * @param packageName the name of the installed package. + * @param packageName the name of the installed package. * @param errorDescription null if the package has been installed successfully, error message otherwise. */ - void operationFinished(String packageName, @Nullable String errorDescription); + void operationFinished(String packageName, @Nullable ErrorDescription errorDescription); + } + + public static class ErrorDescription { + @NotNull private final String myMessage; + @Nullable private final String myCommand; + @Nullable private final String myOutput; + @Nullable private final String mySolution; + + @Nullable + public static ErrorDescription fromMessage(@Nullable String message) { + return message != null ? new ErrorDescription(message, null, null, null) : null; + } + + public ErrorDescription(@NotNull String message, @Nullable String command, @Nullable String output, @Nullable String solution) { + myMessage = message; + myCommand = command; + myOutput = output; + mySolution = solution; + } + + /** + * The reason message that explains why the error has occurred. + */ + @NotNull + public String getMessage() { + return myMessage; + } + + /** + * The packaging command that has been executed, if it is meaningful to the user. + */ + @Nullable + public String getCommand() { + return myCommand; + } + + /** + * The output of the packaging command. + */ + @Nullable + public String getOutput() { + return myOutput; + } + + /** + * A possible solution of this packaging problem for the user. + */ + @Nullable + public String getSolution() { + return mySolution; + } } } diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index 1513bb451aa2..426864911e53 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -4,7 +4,6 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.HyperlinkAdapter; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLabel; @@ -74,8 +73,8 @@ public class PackagesNotificationPanel { builder.show(); } - public void showResult(String packageName, @Nullable String errorDescription) { - if (StringUtil.isEmpty(errorDescription)) { + public void showResult(String packageName, @Nullable PackageManagementService.ErrorDescription errorDescription) { + if (errorDescription == null) { String message = "Package installed successfully"; if (packageName != null) { message = "Package '" + packageName + "' installed successfully"; @@ -90,7 +89,7 @@ public class PackagesNotificationPanel { String firstLine = "Error occurred when installing package '" + packageName + "'. "; showError(firstLine + "Details...", title, - firstLine + errorDescription); + firstLine + errorDescription.getMessage()); } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index d3e23c594ed8..53c493468d0a 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -191,12 +191,13 @@ public class PyPackageManagementService extends PackageManagementService { ui.install(Collections.singletonList(req), extraArgs); } - private String toErrorDescription(List exceptions) { - String errorDescription = null; + @Nullable + private static ErrorDescription toErrorDescription(@Nullable List exceptions) { + String message = null; if (exceptions != null && exceptions.size() > 0) { - errorDescription = PyPackageManagerUI.createDescription(exceptions, ""); + message = PyPackageManagerUI.createDescription(exceptions, ""); } - return errorDescription; + return ErrorDescription.fromMessage(message); } @Override From 8fcb9a01c399614aa12afe7bc4f40ab8fd2efeb4 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 14:03:53 +0400 Subject: [PATCH 18/24] Use ErrorDescription in PackagesNotificationPanel for error reporting --- .../packaging/InstalledPackagesPanel.java | 10 ++++------ .../packaging/PackagesNotificationPanel.java | 20 +++++++++---------- .../python/packaging/PyPackageManagerUI.java | 6 ++++-- .../ui/PyInstalledPackagesPanel.java | 3 ++- .../ui/PyPackageManagementService.java | 5 ++--- .../python/sdk/CreateVirtualEnvDialog.java | 4 +++- 6 files changed, 25 insertions(+), 23 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java index 069b3f646096..649a46a5a6ea 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/InstalledPackagesPanel.java @@ -243,9 +243,8 @@ public class InstalledPackagesPanel extends JPanel { myNotificationArea.showSuccess("Package " + packageName + " successfully upgraded"); } else { - myNotificationArea.showError("Upgrade packages failed. Details...", - "Upgrade Packages Failed", - "Upgrade packages failed.\n" + errorDescription.getMessage()); + myNotificationArea.showError("Upgrade packages failed. Details...", "Upgrade Packages Failed", + errorDescription); } if (myCurrentlyInstalling.isEmpty() && !myWaitingToUpgrade.isEmpty()) { @@ -358,9 +357,8 @@ public class InstalledPackagesPanel extends JPanel { } } else { - myNotificationArea.showError("Uninstall packages failed. Details...", - "Uninstall Packages Failed", - "Uninstall packages failed.\n" + errorDescription); + myNotificationArea.showError("Uninstall packages failed. Details...", "Uninstall Packages Failed", + errorDescription); } } }); diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index 426864911e53..afd034b4c0d8 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -26,7 +26,7 @@ public class PackagesNotificationPanel { private final JEditorPane myHtmlViewer; private final Map myLinkHandlers = new HashMap(); private String myErrorTitle; - private String myErrorDescription; + private PackageManagementService.ErrorDescription myErrorDescription; public PackagesNotificationPanel(@NotNull Project project) { myProject = project; @@ -46,19 +46,21 @@ public class PackagesNotificationPanel { }); } - public static void showError(@NotNull Project project, @NotNull String title, @NotNull String description) { + public static void showError(@NotNull Project project, @NotNull String title, + @NotNull PackageManagementService.ErrorDescription description) { doShowError(title, description, new DialogBuilder(project)); } - public static void showError(@NotNull Component owner, @NotNull String title, @NotNull String description) { + public static void showError(@NotNull Component owner, @NotNull String title, + @NotNull PackageManagementService.ErrorDescription description) { doShowError(title, description, new DialogBuilder(owner)); } - private static void doShowError(String title, String description, DialogBuilder builder) { + private static void doShowError(String title, @NotNull PackageManagementService.ErrorDescription description, DialogBuilder builder) { builder.setTitle(title); final JTextArea textArea = new JTextArea(); textArea.setEditable(false); - textArea.setText(description); + textArea.setText(description.getMessage()); textArea.setWrapStyleWord(false); textArea.setLineWrap(true); final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(textArea); @@ -87,9 +89,7 @@ public class PackagesNotificationPanel { title = "Failed to install package '" + packageName + "'"; } String firstLine = "Error occurred when installing package '" + packageName + "'. "; - showError(firstLine + "Details...", - title, - firstLine + errorDescription.getMessage()); + showError(firstLine + "Details...", title, errorDescription); } } @@ -118,10 +118,10 @@ public class PackagesNotificationPanel { myErrorDescription = null; } - public void showError(String text, final String detailsTitle, final String detailsDescription) { + public void showError(String text, final String detailsTitle, final PackageManagementService.ErrorDescription errorDescription) { showContent(text, MessageType.ERROR.getPopupBackground()); myErrorTitle = detailsTitle; - myErrorDescription = detailsDescription; + myErrorDescription = errorDescription; } public void showWarning(String text) { diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index df13fdb489c3..c6ba2c9c14b1 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -33,6 +33,7 @@ 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.PackageManagementService; import com.jetbrains.python.packaging.ui.PyPackageNotificationDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -414,7 +415,8 @@ public class PyPackageManagerUI { } } - public static String createDescription(List exceptions, String firstLine) { + @NotNull + public static PackageManagementService.ErrorDescription createDescription(List exceptions, String firstLine) { final StringBuilder b = new StringBuilder(); b.append(firstLine); b.append("\n\n"); @@ -422,7 +424,7 @@ public class PyPackageManagerUI { b.append(exception.toString()); b.append("\n"); } - return b.toString(); + return PackageManagementService.ErrorDescription.fromMessage(b.toString()); } @NotNull diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 8d8c163fee29..5f3d287b785e 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -26,6 +26,7 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.util.Consumer; 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.jetbrains.python.packaging.*; import com.jetbrains.python.sdk.PySdkUtil; @@ -73,7 +74,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { PyPackageManager packageManager = PyPackageManager.getInstance(sdk); if (!exceptions.isEmpty()) { final String firstLine = "Install Python packaging tools failed. "; - final String description = PyPackageManagerUI.createDescription(exceptions, firstLine); + final PackageManagementService.ErrorDescription description = PyPackageManagerUI.createDescription(exceptions, firstLine); PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description); } packageManager.refresh(); diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 53c493468d0a..a6abbb92b08e 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -193,11 +193,10 @@ public class PyPackageManagementService extends PackageManagementService { @Nullable private static ErrorDescription toErrorDescription(@Nullable List exceptions) { - String message = null; if (exceptions != null && exceptions.size() > 0) { - message = PyPackageManagerUI.createDescription(exceptions, ""); + return PyPackageManagerUI.createDescription(exceptions, ""); } - return ErrorDescription.fromMessage(message); + return null; } @Override diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java index ebb8813f08c9..fe60b1146144 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java @@ -49,6 +49,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.util.NullableConsumer; import com.intellij.util.PathUtil; import com.intellij.util.PlatformUtils; +import com.intellij.webcore.packaging.PackageManagementService; import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageService; @@ -425,7 +426,8 @@ public class CreateVirtualEnvDialog extends IdeaDialog { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - PackagesNotificationPanel.showError(getOwner(), "Failed to Create Virtual Environment", e.toString()); + PackagesNotificationPanel.showError(getOwner(), "Failed to Create Virtual Environment", + PackageManagementService.ErrorDescription.fromMessage(e.toString())); } }, ModalityState.any()); } From 676ac2d222a6a750fc7968c5fadee91186580a39 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 14:51:22 +0400 Subject: [PATCH 19/24] Moved generation of ErrorDescription from PyPackageManagementUI to PyPackageManagementService --- .../python/packaging/PyPackageManagerUI.java | 134 ++++-------------- .../ui/PyInstalledPackagesPanel.java | 5 +- .../ui/PyPackageManagementService.java | 63 ++++++-- .../ui/PyPackageNotificationDialog.java | 12 +- 4 files changed, 93 insertions(+), 121 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index c6ba2c9c14b1..958e5a088c74 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -34,21 +34,19 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.Function; import com.intellij.webcore.packaging.PackageManagementService; +import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.packaging.ui.PyPackageNotificationDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.event.HyperlinkEvent; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * @author vlan */ public class PyPackageManagerUI { @NotNull private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class); - @NotNull private static final Pattern PATTERN_ERROR_LINE = Pattern.compile(".*error:.*", Pattern.CASE_INSENSITIVE); @Nullable private Listener myListener; @NotNull private Project myProject; @@ -60,40 +58,6 @@ public class PyPackageManagerUI { void finished(List exceptions); } - public static class ExecutionFailure { - @NotNull private final String myMessage; - @Nullable private final String myCommand; - @Nullable private final String myOutput; - @Nullable private final String mySolution; - - public ExecutionFailure(@NotNull String message, @Nullable String command, @Nullable String output, @Nullable String solution) { - myMessage = message; - myCommand = command; - myOutput = output; - mySolution = solution; - } - - @NotNull - public String getMessage() { - return myMessage; - } - - @Nullable - public String getCommand() { - return myCommand; - } - - @Nullable - public String getOutput() { - return myOutput; - } - - @Nullable - public String getSolution() { - return mySolution; - } - } - public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) { myProject = project; mySdk = sdk; @@ -224,45 +188,39 @@ public class PyPackageManagerUI { notificationRef.set(new Notification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(), NotificationType.INFORMATION)); } - else if (!isCancelled(exceptions)) { - final ExecutionFailure failure = analyzeException(exceptions.get(0), mySdk); - final String firstLine = getTitle() + ": error occurred."; - notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), - firstLine + " Details...", - NotificationType.ERROR, - new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, - @NotNull HyperlinkEvent event) { - assert myProject != null; - final String title = StringUtil.capitalizeWords(getFailureTitle(), true); - final PyPackageNotificationDialog dialog = new PyPackageNotificationDialog(title, failure); - dialog.show(); - } - } - )); - } - 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); - } + else { + final PackageManagementService.ErrorDescription description = PyPackageManagementService.toErrorDescription(exceptions, mySdk); + if (description != null) { + final String firstLine = getTitle() + ": error occurred."; + final NotificationListener listener = new NotificationListener() { + @Override + public void hyperlinkUpdate(@NotNull Notification notification, + @NotNull HyperlinkEvent event) { + assert myProject != null; + final String title = StringUtil.capitalizeWords(getFailureTitle(), true); + final PyPackageNotificationDialog dialog = new PyPackageNotificationDialog(title, description); + dialog.show(); + } + }; + notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), firstLine + " Details...", + NotificationType.ERROR, listener)); } - }); - } + else { + 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 boolean isCancelled(@NotNull List exceptions) { - for (ExecutionException e : exceptions) { - if (e instanceof RunCanceledByUserException) { - return true; } } - return false; } } @@ -426,36 +384,4 @@ public class PyPackageManagerUI { } return PackageManagementService.ErrorDescription.fromMessage(b.toString()); } - - @NotNull - private static ExecutionFailure analyzeException(@NotNull ExecutionException e, @NotNull Sdk sdk) { - if (e instanceof PyExecutionException) { - final PyExecutionException ee = (PyExecutionException)e; - final String stdoutCause = findErrorCause(ee.getStdout()); - final String stderrCause = findErrorCause(ee.getStderr()); - final String message = stdoutCause != null ? stdoutCause : stderrCause != null ? stderrCause : ee.getMessage(); - final String solution; - if ("pip".equals(ee.getCommand())) { - solution = "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 '" + sdk.getHomePath() + "'."; - } - else { - solution = null; - } - return new ExecutionFailure(message, ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "), ee.getStdout(), solution); - } - else { - return new ExecutionFailure(e.getMessage(), null, null, null); - } - } - - @Nullable - private static String findErrorCause(@NotNull String output) { - final Matcher m = PATTERN_ERROR_LINE.matcher(output); - if (m.find()) { - final String result = m.group(); - return result != null ? result.trim() : null; - } - return null; - } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index 5f3d287b785e..c4aaa8562a90 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -72,9 +72,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { public void finished(List exceptions) { myPackagesTable.setPaintBusy(false); PyPackageManager packageManager = PyPackageManager.getInstance(sdk); - if (!exceptions.isEmpty()) { - final String firstLine = "Install Python packaging tools failed. "; - final PackageManagementService.ErrorDescription description = PyPackageManagerUI.createDescription(exceptions, firstLine); + final PackageManagementService.ErrorDescription description = PyPackageManagementService.toErrorDescription(exceptions, sdk); + if (description != null) { PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description); } packageManager.refresh(); diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index a6abbb92b08e..2e28aa450df6 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -16,6 +16,7 @@ package com.jetbrains.python.packaging.ui; 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.text.StringUtil; @@ -34,11 +35,15 @@ import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.URL; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author yole */ public class PyPackageManagementService extends PackageManagementService { + @NotNull private static final Pattern PATTERN_ERROR_LINE = Pattern.compile(".*error:.*", Pattern.CASE_INSENSITIVE); + private final Project myProject; private final Sdk mySdk; @@ -85,7 +90,7 @@ public class PyPackageManagementService extends PackageManagementService { private static List versionMapToPackageList(Map packageToVersionMap) { final boolean customRepoConfigured = !PyPackageService.getInstance().additionalRepositories.isEmpty(); - String url = customRepoConfigured? PyPIPackageUtil.PYPI_URL : ""; + String url = customRepoConfigured ? PyPIPackageUtil.PYPI_URL : ""; List packages = new ArrayList(); for (Map.Entry entry : packageToVersionMap.entrySet()) { packages.add(new RepoPackage(entry.getKey(), url, entry.getValue())); @@ -150,7 +155,7 @@ public class PyPackageManagementService extends PackageManagementService { } @Override - public void installPackage(final RepoPackage repoPackage,String version, boolean forceUpgrade, String extraOptions, + public void installPackage(final RepoPackage repoPackage, String version, boolean forceUpgrade, String extraOptions, final Listener listener, boolean installToUser) { final String packageName = repoPackage.getName(); final String repository = PyPIPackageUtil.PYPI_URL.equals(repoPackage.getRepoUrl()) ? null : repoPackage.getRepoUrl(); @@ -185,16 +190,16 @@ public class PyPackageManagementService extends PackageManagementService { @Override public void finished(@Nullable List exceptions) { - listener.operationFinished(packageName, toErrorDescription(exceptions)); + listener.operationFinished(packageName, toErrorDescription(exceptions, mySdk)); } }); ui.install(Collections.singletonList(req), extraArgs); } @Nullable - private static ErrorDescription toErrorDescription(@Nullable List exceptions) { - if (exceptions != null && exceptions.size() > 0) { - return PyPackageManagerUI.createDescription(exceptions, ""); + public static ErrorDescription toErrorDescription(@Nullable List exceptions, @NotNull Sdk sdk) { + if (exceptions != null && !exceptions.isEmpty() && !isCancelled(exceptions)) { + return createDescription(exceptions.get(0), sdk); } return null; } @@ -210,14 +215,14 @@ public class PyPackageManagementService extends PackageManagementService { @Override public void finished(final List exceptions) { - listener.operationFinished(packageName, toErrorDescription(exceptions)); + listener.operationFinished(packageName, toErrorDescription(exceptions, mySdk)); } }); List pyPackages = new ArrayList(); for (InstalledPackage aPackage : installedPackages) { if (aPackage instanceof PyPackage) { - pyPackages.add((PyPackage) aPackage); + pyPackages.add((PyPackage)aPackage); } } ui.uninstall(pyPackages); @@ -302,4 +307,46 @@ public class PyPackageManagementService extends PackageManagementService { private static String composeHref(String vendorUrl) { return HTML_PREFIX + vendorUrl + "\">" + vendorUrl + HTML_SUFFIX; } + + private static boolean isCancelled(@NotNull List exceptions) { + for (ExecutionException e : exceptions) { + if (e instanceof RunCanceledByUserException) { + return true; + } + } + return false; + } + + @NotNull + private static ErrorDescription createDescription(@NotNull ExecutionException e, @NotNull Sdk sdk) { + if (e instanceof PyExecutionException) { + final PyExecutionException ee = (PyExecutionException)e; + final String stdoutCause = findErrorCause(ee.getStdout()); + final String stderrCause = findErrorCause(ee.getStderr()); + final String message = stdoutCause != null ? stdoutCause : stderrCause != null ? stderrCause : ee.getMessage(); + final String solution; + if ("pip".equals(ee.getCommand())) { + solution = "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 '" + sdk.getHomePath() + "'."; + } + else { + solution = null; + } + final String command = ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "); + return new ErrorDescription(message, command, ee.getStdout(), solution); + } + else { + return ErrorDescription.fromMessage(e.getMessage()); + } + } + + @Nullable + private static String findErrorCause(@NotNull String output) { + final Matcher m = PATTERN_ERROR_LINE.matcher(output); + if (m.find()) { + final String result = m.group(); + return result != null ? result.trim() : null; + } + return null; + } } diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java index 908e79c20bb2..79dfe2d1c90c 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java @@ -17,7 +17,7 @@ package com.jetbrains.python.packaging.ui; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.ui.components.JBLabel; -import com.jetbrains.python.packaging.PyPackageManagerUI; +import com.intellij.webcore.packaging.PackageManagementService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,15 +36,15 @@ public class PyPackageNotificationDialog extends DialogWrapper { private JPanel mySolutionPanel; private JTextPane mySolution; - public PyPackageNotificationDialog(@NotNull String title, @NotNull PyPackageManagerUI.ExecutionFailure failure) { + public PyPackageNotificationDialog(@NotNull String title, @NotNull PackageManagementService.ErrorDescription errorDescription) { super(false); init(); setResizable(false); setTitle(title); - final String command = failure.getCommand(); - final String output = failure.getOutput(); - final String message = failure.getMessage(); - final String solution = failure.getSolution(); + final String command = errorDescription.getCommand(); + final String output = errorDescription.getOutput(); + final String message = errorDescription.getMessage(); + final String solution = errorDescription.getSolution(); myCommandPanel.setVisible(command != null); myCommandOutputPanel.setVisible(output != null); From c92dd27c505b72b91318900a8fea170b0ccb3382 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 15:21:39 +0400 Subject: [PATCH 20/24] Use PackageErrorDialog for ErrorDescriptions that contain extended error information --- .../packaging/PackagesNotificationPanel.java | 41 +++++++++++-------- .../packaging/PackagingErrorDialog.form | 2 +- .../packaging/PackagingErrorDialog.java | 7 ++-- .../python/packaging/PyPackageManagerUI.java | 4 +- 4 files changed, 30 insertions(+), 24 deletions(-) rename python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form => platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form (98%) rename python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java => platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java (86%) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index afd034b4c0d8..dc978dd570f3 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -56,23 +56,30 @@ public class PackagesNotificationPanel { doShowError(title, description, new DialogBuilder(owner)); } - private static void doShowError(String title, @NotNull PackageManagementService.ErrorDescription description, DialogBuilder builder) { - builder.setTitle(title); - final JTextArea textArea = new JTextArea(); - textArea.setEditable(false); - textArea.setText(description.getMessage()); - textArea.setWrapStyleWord(false); - textArea.setLineWrap(true); - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(textArea); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - final JPanel panel = new JPanel(new BorderLayout(10, 0)); - panel.setPreferredSize(new Dimension(600, 400)); - panel.add(scrollPane, BorderLayout.CENTER); - panel.add(new JBLabel("Details:", Messages.getErrorIcon(), SwingConstants.LEFT), BorderLayout.NORTH); - builder.setCenterPanel(panel); - builder.setButtonsAlignment(SwingConstants.CENTER); - builder.addOkAction(); - builder.show(); + private static void doShowError(@NotNull String title, @NotNull PackageManagementService.ErrorDescription description, + @NotNull DialogBuilder builder) { + if (description.getCommand() != null || description.getOutput() != null || description.getSolution() != null) { + final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description); + dialog.show(); + } + else { + builder.setTitle(title); + final JTextArea textArea = new JTextArea(); + textArea.setEditable(false); + textArea.setText(description.getMessage()); + textArea.setWrapStyleWord(false); + textArea.setLineWrap(true); + final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(textArea); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + final JPanel panel = new JPanel(new BorderLayout(10, 0)); + panel.setPreferredSize(new Dimension(600, 400)); + panel.add(scrollPane, BorderLayout.CENTER); + panel.add(new JBLabel("Details:", Messages.getErrorIcon(), SwingConstants.LEFT), BorderLayout.NORTH); + builder.setCenterPanel(panel); + builder.setButtonsAlignment(SwingConstants.CENTER); + builder.addOkAction(); + builder.show(); + } } public void showResult(String packageName, @Nullable PackageManagementService.ErrorDescription errorDescription) { diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form similarity index 98% rename from python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form rename to platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form index e9a0ca36ae8a..17e3e0464741 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.form +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form @@ -1,5 +1,5 @@ - + diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java similarity index 86% rename from python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java rename to platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java index 79dfe2d1c90c..70fd461ca08d 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageNotificationDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java @@ -13,11 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.packaging.ui; +package com.intellij.webcore.packaging; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.ui.components.JBLabel; -import com.intellij.webcore.packaging.PackageManagementService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,7 +25,7 @@ import javax.swing.*; /** * @author vlan */ -public class PyPackageNotificationDialog extends DialogWrapper { +public class PackagingErrorDialog extends DialogWrapper { private JPanel myMainPanel; private JBLabel myErrorMessage; private JTextArea myCommandOutput; @@ -36,7 +35,7 @@ public class PyPackageNotificationDialog extends DialogWrapper { private JPanel mySolutionPanel; private JTextPane mySolution; - public PyPackageNotificationDialog(@NotNull String title, @NotNull PackageManagementService.ErrorDescription errorDescription) { + public PackagingErrorDialog(@NotNull String title, @NotNull PackageManagementService.ErrorDescription errorDescription) { super(false); init(); setResizable(false); diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index 958e5a088c74..a1d484c695b1 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -33,9 +33,9 @@ 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.PackagingErrorDialog; import com.intellij.webcore.packaging.PackageManagementService; import com.jetbrains.python.packaging.ui.PyPackageManagementService; -import com.jetbrains.python.packaging.ui.PyPackageNotificationDialog; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -198,7 +198,7 @@ public class PyPackageManagerUI { @NotNull HyperlinkEvent event) { assert myProject != null; final String title = StringUtil.capitalizeWords(getFailureTitle(), true); - final PyPackageNotificationDialog dialog = new PyPackageNotificationDialog(title, description); + final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description); dialog.show(); } }; From bace8a102487cb4a30a5762033a7ff7301f33b23 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 15:23:18 +0400 Subject: [PATCH 21/24] Fixed call to PyPackageManagerUI.Listener after any packaging task --- .../python/packaging/PyPackageManagerUI.java | 27 +++++++++---------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index a1d484c695b1..f2b08df38d76 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -205,22 +205,19 @@ public class PyPackageManagerUI { notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), firstLine + " Details...", NotificationType.ERROR, listener)); } - else { - 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); - } - } - }); - - } } + 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); + } + } + }); } } From 42c6e7efc398c4e8b662f381b435268f09c9d480 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 16:38:07 +0400 Subject: [PATCH 22/24] Use unified PackagingErrorDialog for reporting errors --- .../packaging/ManagePackagesDialog.java | 2 +- .../packaging/PackagesNotificationPanel.java | 48 ++---------- .../packaging/PackagingErrorDialog.form | 77 +++++++++++++++---- .../packaging/PackagingErrorDialog.java | 22 +++++- .../PyActiveSdkConfigurable.java | 2 +- .../packaging/PyManagePackagesDialog.java | 2 +- .../python/packaging/PyPackageManagerUI.java | 5 +- .../ui/PyInstalledPackagesPanel.java | 2 +- .../python/sdk/CreateVirtualEnvDialog.java | 2 +- 9 files changed, 94 insertions(+), 68 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java index 13a34cfefce2..3f92bb0d19d0 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java @@ -95,7 +95,7 @@ public class ManagePackagesDialog extends DialogWrapper { init(); setTitle("Available Packages"); myPackages = new JBList(); - myNotificationArea = new PackagesNotificationPanel(project); + myNotificationArea = new PackagesNotificationPanel(); myNotificationsAreaPlaceholder.add(myNotificationArea.getComponent(), BorderLayout.CENTER); final AnActionButton reloadButton = new AnActionButton("Reload List of Packages", AllIcons.Actions.Refresh) { diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java index dc978dd570f3..d5d0e43360ad 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagesNotificationPanel.java @@ -1,12 +1,7 @@ package com.intellij.webcore.packaging; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogBuilder; import com.intellij.openapi.ui.MessageType; -import com.intellij.openapi.ui.Messages; import com.intellij.ui.HyperlinkAdapter; -import com.intellij.ui.ScrollPaneFactory; -import com.intellij.ui.components.JBLabel; import com.intellij.util.ui.SwingHelper; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -22,14 +17,12 @@ import java.util.Map; * @author yole */ public class PackagesNotificationPanel { - private final Project myProject; private final JEditorPane myHtmlViewer; private final Map myLinkHandlers = new HashMap(); private String myErrorTitle; private PackageManagementService.ErrorDescription myErrorDescription; - public PackagesNotificationPanel(@NotNull Project project) { - myProject = project; + public PackagesNotificationPanel() { myHtmlViewer = SwingHelper.createHtmlViewer(true, null, null, null); myHtmlViewer.setVisible(false); myHtmlViewer.addHyperlinkListener(new HyperlinkAdapter() { @@ -40,46 +33,15 @@ public class PackagesNotificationPanel { handler.run(); } else if (myErrorTitle != null && myErrorDescription != null) { - showError(myProject, myErrorTitle, myErrorDescription); + showError(myErrorTitle, myErrorDescription); } } }); } - public static void showError(@NotNull Project project, @NotNull String title, - @NotNull PackageManagementService.ErrorDescription description) { - doShowError(title, description, new DialogBuilder(project)); - } - - public static void showError(@NotNull Component owner, @NotNull String title, - @NotNull PackageManagementService.ErrorDescription description) { - doShowError(title, description, new DialogBuilder(owner)); - } - - private static void doShowError(@NotNull String title, @NotNull PackageManagementService.ErrorDescription description, - @NotNull DialogBuilder builder) { - if (description.getCommand() != null || description.getOutput() != null || description.getSolution() != null) { - final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description); - dialog.show(); - } - else { - builder.setTitle(title); - final JTextArea textArea = new JTextArea(); - textArea.setEditable(false); - textArea.setText(description.getMessage()); - textArea.setWrapStyleWord(false); - textArea.setLineWrap(true); - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(textArea); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - final JPanel panel = new JPanel(new BorderLayout(10, 0)); - panel.setPreferredSize(new Dimension(600, 400)); - panel.add(scrollPane, BorderLayout.CENTER); - panel.add(new JBLabel("Details:", Messages.getErrorIcon(), SwingConstants.LEFT), BorderLayout.NORTH); - builder.setCenterPanel(panel); - builder.setButtonsAlignment(SwingConstants.CENTER); - builder.addOkAction(); - builder.show(); - } + public static void showError(@NotNull String title, @NotNull PackageManagementService.ErrorDescription description) { + final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description); + dialog.show(); } public void showResult(String packageName, @Nullable PackageManagementService.ErrorDescription errorDescription) { diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form index 17e3e0464741..ec21749a4b76 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.form @@ -1,6 +1,6 @@ - + @@ -10,13 +10,13 @@ - + - + @@ -51,17 +51,17 @@ - + - + - + @@ -82,37 +82,47 @@ - + - + - + - + - + + + - - + + + + + + + + + + - + @@ -132,13 +142,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java index 70fd461ca08d..e3f43172a308 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/PackagingErrorDialog.java @@ -16,6 +16,7 @@ package com.intellij.webcore.packaging; import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.ui.Messages; import com.intellij.ui.components.JBLabel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,13 +28,18 @@ import javax.swing.*; */ public class PackagingErrorDialog extends DialogWrapper { private JPanel myMainPanel; - private JBLabel myErrorMessage; private JTextArea myCommandOutput; private JPanel myCommandOutputPanel; private JPanel myCommandPanel; private JTextPane myCommand; private JPanel mySolutionPanel; private JTextPane mySolution; + private JPanel myDetailsPanel; + private JTextArea myDetails; + private JPanel myMessagePanel; + private JBLabel myDetailsLabel; + private JTextPane myMessage; + private JBLabel myMessageIcon; public PackagingErrorDialog(@NotNull String title, @NotNull PackageManagementService.ErrorDescription errorDescription) { super(false); @@ -45,11 +51,23 @@ public class PackagingErrorDialog extends DialogWrapper { final String message = errorDescription.getMessage(); final String solution = errorDescription.getSolution(); + final boolean extendedInfo = command != null || output != null || solution != null; + + myDetailsPanel.setVisible(!extendedInfo); + myMessagePanel.setVisible(extendedInfo); myCommandPanel.setVisible(command != null); myCommandOutputPanel.setVisible(output != null); mySolutionPanel.setVisible(solution != null); - myErrorMessage.setText(message); + if (extendedInfo) { + myMessage.setText(message); + myMessageIcon.setIcon(Messages.getErrorIcon()); + } + else { + myDetails.setText(message); + myDetailsLabel.setIcon(Messages.getErrorIcon()); + } + if (command != null) { myCommand.setText(command); } diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index 9fe57b7fcc8d..be1691369946 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -187,7 +187,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { mySdkCombo.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE); mySdkCombo.setRenderer(new PySdkListCellRenderer(false)); - final PackagesNotificationPanel notificationsArea = new PackagesNotificationPanel(myProject); + final PackagesNotificationPanel notificationsArea = new PackagesNotificationPanel(); final JComponent notificationsComponent = notificationsArea.getComponent(); final Dimension preferredSize = mySdkCombo.getPreferredSize(); mySdkCombo.setPreferredSize(preferredSize); diff --git a/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java b/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java index 4a9df08f6eb7..33cf7ccbe9c3 100644 --- a/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java +++ b/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java @@ -50,7 +50,7 @@ public class PyManagePackagesDialog extends DialogWrapper { final JComboBox sdkComboBox = new JComboBox(new CollectionComboBoxModel(sdks, sdk)); sdkComboBox.setRenderer(new PySdkListCellRenderer(false)); - PackagesNotificationPanel notificationPanel = new PackagesNotificationPanel(project); + PackagesNotificationPanel notificationPanel = new PackagesNotificationPanel(); final PyInstalledPackagesPanel packagesPanel = new PyInstalledPackagesPanel(project, notificationPanel); packagesPanel.setBorder(BorderFactory.createEmptyBorder(4, 0, 0, 0)); packagesPanel.updatePackages(new PyPackageManagementService(project, sdk)); diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java index f2b08df38d76..c894f0fe8087 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java @@ -33,8 +33,8 @@ 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.PackagingErrorDialog; import com.intellij.webcore.packaging.PackageManagementService; +import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.packaging.ui.PyPackageManagementService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -198,8 +198,7 @@ public class PyPackageManagerUI { @NotNull HyperlinkEvent event) { assert myProject != null; final String title = StringUtil.capitalizeWords(getFailureTitle(), true); - final PackagingErrorDialog dialog = new PackagingErrorDialog(title, description); - dialog.show(); + PackagesNotificationPanel.showError(title, description); } }; notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(), firstLine + " Details...", diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index c4aaa8562a90..90240955831a 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -74,7 +74,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { PyPackageManager packageManager = PyPackageManager.getInstance(sdk); final PackageManagementService.ErrorDescription description = PyPackageManagementService.toErrorDescription(exceptions, sdk); if (description != null) { - PackagesNotificationPanel.showError(myProject, "Failed to install Python packaging tools", description); + PackagesNotificationPanel.showError("Failed to install Python packaging tools", description); } packageManager.refresh(); updatePackages(new PyPackageManagementService(myProject, sdk)); diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java index fe60b1146144..7e6475926917 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java @@ -426,7 +426,7 @@ public class CreateVirtualEnvDialog extends IdeaDialog { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - PackagesNotificationPanel.showError(getOwner(), "Failed to Create Virtual Environment", + PackagesNotificationPanel.showError("Failed to Create Virtual Environment", PackageManagementService.ErrorDescription.fromMessage(e.toString())); } }, ModalityState.any()); From 9fd56e410ff6168c009236b8acb4907a70163771 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 16:50:49 +0400 Subject: [PATCH 23/24] Use extended error description for exceptions during installation of framework packages --- .../com/jetbrains/python/sdk/CreateVirtualEnvDialog.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java index 7e6475926917..2d59d1fd4132 100644 --- a/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java +++ b/python/src/com/jetbrains/python/sdk/CreateVirtualEnvDialog.java @@ -53,6 +53,7 @@ import com.intellij.webcore.packaging.PackageManagementService; import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.packaging.PyPackageManager; import com.jetbrains.python.packaging.PyPackageService; +import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; import com.jetbrains.python.ui.IdeaDialog; import org.jetbrains.annotations.NotNull; @@ -426,8 +427,11 @@ public class CreateVirtualEnvDialog extends IdeaDialog { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { - PackagesNotificationPanel.showError("Failed to Create Virtual Environment", - PackageManagementService.ErrorDescription.fromMessage(e.toString())); + final PackageManagementService.ErrorDescription description = + PyPackageManagementService.toErrorDescription(Collections.singletonList(e), basicSdk); + if (description != null) { + PackagesNotificationPanel.showError("Failed to Create Virtual Environment", description); + } } }, ModalityState.any()); } From 3970ef798d7d59fb5b9f5c106ccbc38a6be48f5c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Thu, 2 Oct 2014 17:17:01 +0400 Subject: [PATCH 24/24] Propose solutions for SyntaxError and Python.h in installation output --- .../ui/PyPackageManagementService.java | 41 ++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 2e28aa450df6..221468ce39a8 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -19,12 +19,14 @@ 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.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.CatchingConsumer; import com.intellij.webcore.packaging.InstalledPackage; import com.intellij.webcore.packaging.PackageManagementService; import com.intellij.webcore.packaging.RepoPackage; import com.jetbrains.python.packaging.*; +import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.sdk.PySdkUtil; import com.jetbrains.python.sdk.PythonSdkType; import org.apache.xmlrpc.AsyncCallback; @@ -323,23 +325,42 @@ public class PyPackageManagementService extends PackageManagementService { final PyExecutionException ee = (PyExecutionException)e; final String stdoutCause = findErrorCause(ee.getStdout()); final String stderrCause = findErrorCause(ee.getStderr()); - final String message = stdoutCause != null ? stdoutCause : stderrCause != null ? stderrCause : ee.getMessage(); - final String solution; - if ("pip".equals(ee.getCommand())) { - solution = "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 '" + sdk.getHomePath() + "'."; - } - else { - solution = null; - } + final String cause = stdoutCause != null ? stdoutCause : stderrCause; + final String message = cause != null ? cause : ee.getMessage(); final String command = ee.getCommand() + " " + StringUtil.join(ee.getArgs(), " "); - return new ErrorDescription(message, command, ee.getStdout(), solution); + return new ErrorDescription(message, command, ee.getStdout(), findErrorSolution(ee, cause, sdk)); } else { return ErrorDescription.fromMessage(e.getMessage()); } } + @Nullable + private static String findErrorSolution(@NotNull PyExecutionException e, @Nullable String cause, @NotNull Sdk sdk) { + if (cause != null) { + if (StringUtil.containsIgnoreCase(cause, "SyntaxError")) { + final LanguageLevel languageLevel = PythonSdkType.getLanguageLevelForSdk(sdk); + return "Make sure that you use a version of Python supported by this package. Currently you are using Python " + + languageLevel + "."; + } + } + + if (SystemInfo.isLinux && (containsInOutput(e, "pyconfig.h") || containsInOutput(e, "Python.h"))) { + return "Make sure that you have installed Python development packages for your operating system."; + } + + if ("pip".equals(e.getCommand())) { + return "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 '" + sdk.getHomePath() + "'."; + } + + return null; + } + + private static boolean containsInOutput(@NotNull PyExecutionException e, @NotNull String text) { + return StringUtil.containsIgnoreCase(e.getStdout(), text) || StringUtil.containsIgnoreCase(e.getStderr(), text); + } + @Nullable private static String findErrorCause(@NotNull String output) { final Matcher m = PATTERN_ERROR_LINE.matcher(output);