summaryrefslogtreecommitdiff
path: root/python/src/com/jetbrains/python/packaging
diff options
context:
space:
mode:
authorTor Norbye <tnorbye@google.com>2014-09-18 20:40:22 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-09-18 20:40:22 +0000
commit07d35c37ce79a64bdd905b394d40fc9bbb18fa60 (patch)
treee8787c45e494dfcc558faf0f75956f8785c39b94 /python/src/com/jetbrains/python/packaging
parente222a9e1e66670a56e926a6b0f3e10231eeeb1fb (diff)
parentb5fb31ef6a38f19404859755dbd2e345215b97bf (diff)
downloadidea-07d35c37ce79a64bdd905b394d40fc9bbb18fa60.tar.gz
Merge "Merge remote-tracking branch 'aosp/upstream-master' into merge"
Diffstat (limited to 'python/src/com/jetbrains/python/packaging')
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java665
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java367
-rw-r--r--python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java24
-rw-r--r--python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java177
-rw-r--r--python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java119
-rw-r--r--python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java15
6 files changed, 696 insertions, 671 deletions
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
index 65d95eb62713..9b5f669168f7 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerImpl.java
@@ -15,31 +15,17 @@
*/
package com.jetbrains.python.packaging;
-import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.execution.util.ExecUtil;
-import com.intellij.icons.AllIcons;
-import com.intellij.notification.Notification;
-import com.intellij.notification.NotificationListener;
-import com.intellij.notification.NotificationType;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
-import com.intellij.openapi.progress.ProgressIndicator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.progress.Task;
-import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
-import com.intellij.openapi.projectRoots.SdkAdditionalData;
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
import com.intellij.openapi.roots.OrderRootType;
-import com.intellij.openapi.ui.Messages;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
@@ -49,46 +35,36 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
-import com.intellij.remote.RemoteFile;
-import com.intellij.remote.RemoteSdkAdditionalData;
-import com.intellij.remote.RemoteSdkCredentials;
-import com.intellij.remote.VagrantNotStartedException;
import com.intellij.util.ArrayUtil;
-import com.intellij.util.Function;
-import com.intellij.util.PathMappingSettings;
-import com.intellij.util.SystemProperties;
import com.intellij.util.containers.HashSet;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.net.HttpConfigurable;
-import com.intellij.webcore.packaging.PackagesNotificationPanel;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyListLiteralExpression;
import com.jetbrains.python.psi.PyStringLiteralExpression;
-import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
-import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
import com.jetbrains.python.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import javax.swing.event.HyperlinkEvent;
-import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.*;
-import java.util.List;
/**
* @author vlan
*/
-@SuppressWarnings({"UnusedDeclaration", "FieldAccessedSynchronizedAndUnsynchronized"})
public class PyPackageManagerImpl extends PyPackageManager {
- private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class);
+ // Bundled versions of package management tools
+ public static final String SETUPTOOLS_VERSION = "1.1.5";
+ public static final String PIP_VERSION = "1.4.1";
+
+ public static final String SETUPTOOLS = PACKAGE_SETUPTOOLS + "-" + SETUPTOOLS_VERSION;
+ public static final String PIP = PACKAGE_PIP + "-" + PIP_VERSION;
public static final int OK = 0;
- public static final int ERROR_WRONG_USAGE = 1;
public static final int ERROR_NO_PIP = 2;
public static final int ERROR_NO_SETUPTOOLS = 3;
public static final int ERROR_INVALID_SDK = -1;
@@ -97,260 +73,24 @@ public class PyPackageManagerImpl extends PyPackageManager {
public static final int ERROR_INVALID_OUTPUT = -4;
public static final int ERROR_ACCESS_DENIED = -5;
public static final int ERROR_EXECUTION = -6;
- public static final int ERROR_INTERRUPTED = -7;
- public static final int ERROR_VAGRANT_NOT_LAUNCHED = 101;
- public static final int ERROR_REMOTE_ACCESS = 102;
-
- public static final String PACKAGE_PIP = "pip";
- public static final String PACKAGE_DISTRIBUTE = "distribute";
- public static final String PACKAGE_SETUPTOOLS = "setuptools";
-
- public static final Key<Boolean> RUNNING_PACKAGING_TASKS = Key.create("PyPackageRequirementsInspection.RunningPackagingTasks");
+ private static final Logger LOG = Logger.getInstance(PyPackageManagerImpl.class);
private static final String PACKAGING_TOOL = "packaging_tool.py";
private static final String VIRTUALENV = "virtualenv.py";
private static final int TIMEOUT = 10 * 60 * 1000;
private static final String BUILD_DIR_OPTION = "--build-dir";
- public static final String USE_USER_SITE = "--user";
public static final String INSTALL = "install";
public static final String UNINSTALL = "uninstall";
public static final String UNTAR = "untar";
- // Bundled versions of package management tools
- public static final String SETUPTOOLS_VERSION = "1.1.5";
- public static final String PIP_VERSION = "1.4.1";
-
- public static final String SETUPTOOLS = PACKAGE_SETUPTOOLS + "-" + SETUPTOOLS_VERSION;
- public static final String PIP = PACKAGE_PIP + "-" + PIP_VERSION;
- private static final String LAUNCH_VAGRANT = "launchVagrant";
-
private List<PyPackage> myPackagesCache = null;
private Map<String, Set<PyPackage>> myDependenciesCache = null;
private PyExternalProcessException myExceptionCache = null;
- private Sdk mySdk;
-
- public static class UI {
- @Nullable private Listener myListener;
- @NotNull private Project myProject;
- @NotNull private Sdk mySdk;
-
- public interface Listener {
- void started();
-
- void finished(List<PyExternalProcessException> exceptions);
- }
-
- public UI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) {
- myProject = project;
- mySdk = sdk;
- myListener = listener;
- }
-
- public void installManagement(@NotNull final String name) {
- final String progressTitle;
- final String successTitle;
- progressTitle = "Installing package " + name;
- successTitle = "Packages installed successfully";
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
- indicator.setText(String.format("Installing package '%s'...", name));
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- try {
- manager.installManagement(name);
- }
- catch (PyExternalProcessException e) {
- exceptions.add(e);
- }
- return exceptions;
- }
- }, progressTitle, successTitle, "Installed package " + name,
- "Install package failed"
- );
- }
-
- public void install(@NotNull final List<PyRequirement> requirements, @NotNull final List<String> extraArgs) {
- final String progressTitle;
- final String successTitle;
- progressTitle = "Installing packages";
- successTitle = "Packages installed successfully";
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final int size = requirements.size();
- final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- for (int i = 0; i < size; i++) {
- final PyRequirement requirement = requirements.get(i);
- if (myListener != null) {
- indicator.setText(String.format("Installing package '%s'...", requirement));
- indicator.setFraction((double)i / size);
- }
- try {
- manager.install(list(requirement), extraArgs);
- }
- catch (PyExternalProcessException e) {
- exceptions.add(e);
- }
- }
- manager.refresh();
- return exceptions;
- }
- }, progressTitle, successTitle, "Installed packages: " + PyPackageUtil.requirementsToString(requirements),
- "Install packages failed"
- );
- }
-
- public void uninstall(@NotNull final List<PyPackage> packages) {
- final String packagesString = StringUtil.join(packages, new Function<PyPackage, String>() {
- @Override
- public String fun(PyPackage pkg) {
- return "'" + pkg.getName() + "'";
- }
- }, ", ");
- if (checkDependents(packages)) return;
-
- run(new MultiExternalRunnable() {
- @Override
- public List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator) {
- final PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManagers.getInstance().forSdk(mySdk);
- try {
- manager.uninstall(packages);
- return list();
- }
- catch (PyExternalProcessException e) {
- return list(e);
- }
- finally {
- manager.refresh();
- }
- }
- }, "Uninstalling packages", "Packages uninstalled successfully", "Uninstalled packages: " + packagesString,
- "Uninstall packages failed"
- );
- }
-
- private boolean checkDependents(@NotNull final List<PyPackage> packages) {
- try {
- final Map<String, Set<PyPackage>> dependentPackages = collectDependents(packages, mySdk);
- final int[] warning = {0};
- if (!dependentPackages.isEmpty()) {
- ApplicationManager.getApplication().invokeAndWait(new Runnable() {
- @Override
- public void run() {
- if (dependentPackages.size() == 1) {
- String message = "You are attempting to uninstall ";
- List<String> dep = new ArrayList<String>();
- int size = 1;
- for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
- final Set<PyPackage> value = entry.getValue();
- size = value.size();
- dep.add(entry.getKey() + " package which is required for " + StringUtil.join(value, ", "));
- }
- message += StringUtil.join(dep, "\n");
- message += size == 1 ? " package" : " packages";
- message += "\n\nDo you want to proceed?";
- warning[0] = Messages.showYesNoDialog(message, "Warning",
- AllIcons.General.BalloonWarning);
- }
- else {
- String message = "You are attempting to uninstall packages which are required for another packages.\n\n";
- List<String> dep = new ArrayList<String>();
- for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
- dep.add(entry.getKey() + " -> " + StringUtil.join(entry.getValue(), ", "));
- }
- message += StringUtil.join(dep, "\n");
- message += "\n\nDo you want to proceed?";
- warning[0] = Messages.showYesNoDialog(message, "Warning",
- AllIcons.General.BalloonWarning);
- }
- }
- }, ModalityState.current());
- }
- if (warning[0] != Messages.YES) return true;
- }
- catch (PyExternalProcessException e) {
- LOG.info("Error loading packages dependents: " + e.getMessage(), e);
- }
- return false;
- }
-
- private interface MultiExternalRunnable {
- List<PyExternalProcessException> run(@NotNull ProgressIndicator indicator);
- }
-
- private void run(@NotNull final MultiExternalRunnable runnable, @NotNull final String progressTitle,
- @NotNull final String successTitle, @NotNull final String successDescription, @NotNull final String failureTitle) {
- ProgressManager.getInstance().run(new Task.Backgroundable(myProject, progressTitle, false) {
- @Override
- public void run(@NotNull ProgressIndicator indicator) {
- indicator.setText(progressTitle + "...");
- final Ref<Notification> notificationRef = new Ref<Notification>(null);
- final String PACKAGING_GROUP_ID = "Packaging";
- final Application application = ApplicationManager.getApplication();
- if (myListener != null) {
- application.invokeLater(new Runnable() {
- @Override
- public void run() {
- myListener.started();
- }
- });
- }
-
- final List<PyExternalProcessException> exceptions = runnable.run(indicator);
- if (exceptions.isEmpty()) {
- notificationRef.set(new Notification(PACKAGING_GROUP_ID, successTitle, successDescription, NotificationType.INFORMATION));
- }
- else {
- final String progressLower = progressTitle.toLowerCase();
- final String firstLine = String.format("Error%s occurred when %s.", exceptions.size() > 1 ? "s" : "", progressLower);
-
- final String description = createDescription(exceptions, firstLine);
- notificationRef.set(new Notification(PACKAGING_GROUP_ID, failureTitle,
- firstLine + " <a href=\"xxx\">Details...</a>",
- NotificationType.ERROR,
- new NotificationListener() {
- @Override
- public void hyperlinkUpdate(@NotNull Notification notification,
- @NotNull HyperlinkEvent event) {
- assert myProject != null;
- PackagesNotificationPanel.showError(myProject, failureTitle, description);
- }
- }
- ));
- }
- application.invokeLater(new Runnable() {
- @Override
- public void run() {
- if (myListener != null) {
- myListener.finished(exceptions);
- }
- final Notification notification = notificationRef.get();
- if (notification != null) {
- notification.notify(myProject);
- }
- }
- });
- }
- });
- }
-
- public static String createDescription(List<PyExternalProcessException> exceptions, String firstLine) {
- final StringBuilder b = new StringBuilder();
- b.append(firstLine);
- b.append("\n\n");
- for (PyExternalProcessException exception : exceptions) {
- b.append(exception.toString());
- b.append("\n");
- }
- return b.toString();
- }
- }
+ protected Sdk mySdk;
@Override
public void refresh() {
@@ -373,7 +113,23 @@ public class PyPackageManagerImpl extends PyPackageManager {
});
}
- private void installManagement(String name) throws PyExternalProcessException {
+ @Override
+ public void installManagement() throws PyExternalProcessException {
+ if (!hasPackage(PACKAGE_SETUPTOOLS, false) && !hasPackage(PACKAGE_DISTRIBUTE, false)) {
+ installManagement(SETUPTOOLS);
+ }
+ if (!hasPackage(PACKAGE_PIP, false)) {
+ installManagement(PIP);
+ }
+ }
+
+ @Override
+ public boolean hasManagement(boolean cachedOnly) {
+ return (hasPackage(PACKAGE_SETUPTOOLS, cachedOnly) || hasPackage(PACKAGE_DISTRIBUTE, cachedOnly)) &&
+ hasPackage(PACKAGE_PIP, cachedOnly);
+ }
+
+ protected void installManagement(@NotNull String name) throws PyExternalProcessException {
final String helperPath = getHelperPath(name);
ArrayList<String> args = Lists.newArrayList(UNTAR, helperPath);
@@ -390,7 +146,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
final String fileName = dirName + name + File.separatorChar + "setup.py";
try {
- output = getProcessOutput(fileName, Collections.<String>singletonList(INSTALL), true, dirName + name);
+ output = getProcessOutput(fileName, Collections.singletonList(INSTALL), true, dirName + name);
final int retcode = output.getExitCode();
if (output.isTimeout()) {
throw new PyExternalProcessException(ERROR_TIMEOUT, fileName, Lists.newArrayList(INSTALL), "Timed out");
@@ -406,17 +162,28 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
finally {
clearCaches();
- FileUtil.delete(new File(dirName)); //TODO: remove temp directory for remote interpreter
+ FileUtil.delete(new File(dirName));
+ }
+ }
+
+ private boolean hasPackage(@NotNull String name, boolean cachedOnly) {
+ try {
+ return findPackage(name, cachedOnly) != null;
+ }
+ catch (PyExternalProcessException ignored) {
+ return false;
}
}
PyPackageManagerImpl(@NotNull Sdk sdk) {
mySdk = sdk;
+ subscribeToLocalChanges(sdk);
+ }
+
+ protected void subscribeToLocalChanges(Sdk sdk) {
final Application app = ApplicationManager.getApplication();
final MessageBusConnection connection = app.getMessageBus().connect();
- if (!PySdkUtil.isRemote(sdk)) {
- connection.subscribe(VirtualFileManager.VFS_CHANGES, new MySdkRootWatcher());
- }
+ connection.subscribe(VirtualFileManager.VFS_CHANGES, new MySdkRootWatcher());
}
public Sdk getSdk() {
@@ -424,27 +191,13 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
@Override
- public void install(String requirementString) throws PyExternalProcessException {
- boolean hasSetuptools = false;
- boolean hasPip = false;
- try {
- hasSetuptools = findInstalledPackage(SETUPTOOLS) != null;
- }
- catch (PyExternalProcessException ignored) {
- }
- try {
- hasPip = findInstalledPackage(PIP) != null;
- }
- catch (PyExternalProcessException ignored) {
- }
-
- if (!hasSetuptools) installManagement(SETUPTOOLS);
- if (!hasPip) installManagement(PIP);
+ public void install(@NotNull String requirementString) throws PyExternalProcessException {
+ installManagement();
install(Collections.singletonList(PyRequirement.fromString(requirementString)), Collections.<String>emptyList());
}
- public void install(@NotNull List<PyRequirement> requirements, @NotNull List<String> extraArgs)
- throws PyExternalProcessException {
+ @Override
+ public void install(@NotNull List<PyRequirement> requirements, @NotNull List<String> extraArgs) throws PyExternalProcessException {
final List<String> args = new ArrayList<String>();
args.add(INSTALL);
final File buildDir;
@@ -455,7 +208,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
throw new PyExternalProcessException(ERROR_ACCESS_DENIED, PACKAGING_TOOL, args, "Cannot create temporary build directory");
}
if (!extraArgs.contains(BUILD_DIR_OPTION)) {
- args.addAll(list(BUILD_DIR_OPTION, buildDir.getAbsolutePath()));
+ args.addAll(Arrays.asList(BUILD_DIR_OPTION, buildDir.getAbsolutePath()));
}
boolean useUserSite = extraArgs.contains(USE_USER_SITE);
@@ -499,69 +252,22 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
- private static Map<String, Set<PyPackage>> collectDependents(@NotNull final List<PyPackage> packages, Sdk sdk)
- throws PyExternalProcessException {
- Map<String, Set<PyPackage>> dependentPackages = new HashMap<String, Set<PyPackage>>();
- for (PyPackage pkg : packages) {
- final Set<PyPackage> dependents =
- ((PyPackageManagerImpl)PyPackageManager.getInstance(sdk)).getDependents(pkg.getName());
- if (dependents != null && !dependents.isEmpty()) {
- for (PyPackage dependent : dependents) {
- if (!packages.contains(dependent)) {
- dependentPackages.put(pkg.getName(), dependents);
- }
- }
- }
- }
- return dependentPackages;
- }
-
- public static String getUserSite() {
- if (SystemInfo.isWindows) {
- final String appdata = System.getenv("APPDATA");
- return appdata + File.separator + "Python";
- }
- else {
- final String userHome = SystemProperties.getUserHome();
- return userHome + File.separator + ".local";
- }
- }
-
-
- public boolean cacheIsNotNull() {
- return myPackagesCache != null;
- }
-
- /**
- * Returns the list of packages for the SDK without initiating a remote connection. Returns null
- * for a remote interpreter if the list of packages was not loaded.
- *
- * @return the list of packages or null
- */
@Nullable
- public synchronized List<PyPackage> getPackagesFast() throws PyExternalProcessException {
- if (myPackagesCache != null) {
- return myPackagesCache;
- }
- if (PySdkUtil.isRemote(mySdk)) {
- return null;
- }
- return getPackages();
- }
-
- @NotNull
- public synchronized List<PyPackage> getPackages() throws PyExternalProcessException {
+ public synchronized List<PyPackage> getPackages(boolean cachedOnly) throws PyExternalProcessException {
if (myPackagesCache == null) {
if (myExceptionCache != null) {
throw myExceptionCache;
}
-
+ if (cachedOnly) {
+ return null;
+ }
loadPackages();
}
return myPackagesCache;
}
- public synchronized Set<PyPackage> getDependents(String pkg) throws PyExternalProcessException {
+ @Nullable
+ public synchronized Set<PyPackage> getDependents(@NotNull PyPackage pkg) throws PyExternalProcessException {
if (myDependenciesCache == null) {
if (myExceptionCache != null) {
throw myExceptionCache;
@@ -569,12 +275,12 @@ public class PyPackageManagerImpl extends PyPackageManager {
loadPackages();
}
- return myDependenciesCache.get(pkg);
+ return myDependenciesCache.get(pkg.getName());
}
public synchronized void loadPackages() throws PyExternalProcessException {
try {
- final String output = runPythonHelper(PACKAGING_TOOL, list("list"));
+ final String output = runPythonHelper(PACKAGING_TOOL, Arrays.asList("list"));
myPackagesCache = parsePackagingToolOutput(output);
Collections.sort(myPackagesCache, new Comparator<PyPackage>() {
@Override
@@ -592,7 +298,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
}
- private void calculateDependents() {
+ private synchronized void calculateDependents() {
myDependenciesCache = new HashMap<String, Set<PyPackage>>();
for (PyPackage p : myPackagesCache) {
final List<PyRequirement> requirements = p.getRequirements();
@@ -608,47 +314,18 @@ public class PyPackageManagerImpl extends PyPackageManager {
@Override
@Nullable
- public PyPackage findInstalledPackage(String name) throws PyExternalProcessException {
- return findPackageByName(name, getPackages());
- }
-
- @Override
- public boolean findPackage(@NotNull final String name) {
- try {
- final String output = runPythonHelper(PACKAGING_TOOL, list("search", name));
- return StringUtil.containsIgnoreCase(output, name + " ");
- }
- catch (PyExternalProcessException e) {
- LOG.error(e.getMessage());
- return false;
- }
- }
-
- @Nullable
- public PyPackage findPackageFast(String name) throws PyExternalProcessException {
- final List<PyPackage> packages = getPackagesFast();
- return packages != null ? findPackageByName(name, packages) : null;
- }
-
- @Nullable
- private static PyPackage findPackageByName(String name, List<PyPackage> packages) {
- for (PyPackage pkg : packages) {
- if (name.equalsIgnoreCase(pkg.getName())) {
- return pkg;
+ public PyPackage findPackage(@NotNull String name, boolean cachedOnly) throws PyExternalProcessException {
+ final List<PyPackage> packages = getPackages(cachedOnly);
+ if (packages != null) {
+ for (PyPackage pkg : packages) {
+ if (name.equalsIgnoreCase(pkg.getName())) {
+ return pkg;
+ }
}
}
return null;
}
- public boolean hasPip() {
- try {
- return findPackageFast(PACKAGE_PIP) != null;
- }
- catch (PyExternalProcessException e) {
- return false;
- }
- }
-
@NotNull
public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws PyExternalProcessException {
final List<String> args = new ArrayList<String>();
@@ -674,30 +351,21 @@ public class PyPackageManagerImpl extends PyPackageManager {
final String path = (binary != null) ? binary : binaryFallback;
if (usePyVenv) {
- // TODO: Still no 'packaging' and 'pysetup3' for Python 3.3rc1, see PEP 405
+ // Still no 'packaging' and 'pysetup3' for Python 3.3rc1, see PEP 405
final VirtualFile binaryFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
if (binaryFile != null) {
final ProjectJdkImpl tmpSdk = new ProjectJdkImpl("", PythonSdkType.getInstance());
tmpSdk.setHomePath(path);
- final PyPackageManagerImpl manager = new PyPackageManagerImpl(tmpSdk);
- manager.installManagement(SETUPTOOLS);
- manager.installManagement(PIP);
+ final PyPackageManager manager = PyPackageManager.getInstance(tmpSdk);
+ manager.installManagement();
}
}
return path;
}
- public static void deleteVirtualEnv(@NotNull String sdkHome) throws PyExternalProcessException {
- final File root = PythonSdkType.getVirtualEnvRoot(sdkHome);
- if (root != null) {
- FileUtil.delete(root);
- }
- }
-
@Nullable
- public static List<PyRequirement> getRequirements(@NotNull Module module) {
- // TODO: Cache requirements, clear cache on requirements.txt or setup.py updates
- List<PyRequirement> requirements = getRequirementsFromTxt(module);
+ public List<PyRequirement> getRequirements(@NotNull Module module) {
+ List<PyRequirement> requirements = PySdkUtil.getRequirementsFromTxt(module);
if (requirements != null) {
return requirements;
}
@@ -721,25 +389,12 @@ public class PyPackageManagerImpl extends PyPackageManager {
return null;
}
- @Nullable
- public static List<PyRequirement> getRequirementsFromTxt(Module module) {
- final VirtualFile requirementsTxt = PyPackageUtil.findRequirementsTxt(module);
- if (requirementsTxt != null) {
- return PyRequirement.parse(requirementsTxt);
- }
- return null;
- }
-
- private void clearCaches() {
+ protected synchronized void clearCaches() {
myPackagesCache = null;
myDependenciesCache = null;
myExceptionCache = null;
}
- private static <T> List<T> list(T... xs) {
- return Arrays.asList(xs);
- }
-
@Nullable
private static String getProxyString() {
final HttpConfigurable settings = HttpConfigurable.getInstance();
@@ -792,164 +447,61 @@ public class PyPackageManagerImpl extends PyPackageManager {
}
@Nullable
- private String getHelperPath(String helper) {
- String helperPath;
- final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
- if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
- PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
-
- try {
- final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
- if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
- helperPath = new RemoteFile(remoteSdkCredentials.getHelpersPath(),
- helper).getPath();
- }
- else {
- helperPath = null;
- }
- }
- catch (Exception e) {
- helperPath = null;
- LOG.error(e);
- }
- }
- else {
- helperPath = PythonHelpersLocator.getHelperPath(helper);
- }
- return helperPath;
+ protected String getHelperPath(String helper) {
+ return PythonHelpersLocator.getHelperPath(helper);
}
- private ProcessOutput getProcessOutput(@NotNull String helperPath,
+ protected ProcessOutput getProcessOutput(@NotNull String helperPath,
@NotNull List<String> args,
boolean askForSudo,
- @Nullable String workingDir)
- throws PyExternalProcessException {
- final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ @Nullable String workingDir) throws PyExternalProcessException {
final String homePath = mySdk.getHomePath();
if (homePath == null) {
throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK");
}
- if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter
- RemoteSdkCredentials remoteSdkCredentials;
+ if (workingDir == null) {
+ workingDir = new File(homePath).getParent();
+ }
+ final List<String> cmdline = new ArrayList<String>();
+ cmdline.add(homePath);
+ cmdline.add(helperPath);
+ cmdline.addAll(args);
+ LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
+
+ final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
+ if (!canCreate && !SystemInfo.isWindows && askForSudo) { //is system site interpreter --> we need sudo privileges
try {
- remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
- }
- catch (InterruptedException e) {
- LOG.error(e);
- remoteSdkCredentials = null;
- }
- catch (final ExecutionException e) {
- if (e.getCause() instanceof VagrantNotStartedException) {
- throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
- LAUNCH_VAGRANT +
- "\">Launch vagrant</a>")
- .withHandler(LAUNCH_VAGRANT, new Runnable() {
- @Override
- public void run() {
- final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
- if (manager != null) {
-
- try {
- manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
- clearCaches();
- }
- catch (ExecutionException e1) {
- throw new RuntimeException(e1);
- }
- }
- }
- });
- }
- else {
- throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
- }
- }
- final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
- if (manager != null && remoteSdkCredentials != null) {
- final List<String> cmdline = new ArrayList<String>();
- cmdline.add(homePath);
- cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath());
- cmdline.addAll(Collections2.transform(args, new com.google.common.base.Function<String, String>() {
- @Override
- public String apply(@Nullable String input) {
- return quoteIfNeeded(input);
+ final ProcessOutput result = ExecUtil.sudoAndGetOutput(cmdline,
+ "Please enter your password to make changes in system packages: ",
+ workingDir);
+ String message = result.getStderr();
+ if (result.getExitCode() != 0) {
+ final String stdout = result.getStdout();
+ if (StringUtil.isEmptyOrSpaces(message)) {
+ message = stdout;
}
- }));
- try {
- if (askForSudo) {
- askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
+ if (StringUtil.isEmptyOrSpaces(message)) {
+ message = "Failed to perform action. Permission denied.";
}
- 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;
+ throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
}
- catch (ExecutionException e) {
- throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e);
+ if (SystemInfo.isMac && !StringUtil.isEmptyOrSpaces(message)) {
+ throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
}
+ return result;
}
- else {
- throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args,
- PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
+ catch (ExecutionException e) {
+ throw new PyExternalProcessException(ERROR_EXECUTION, helperPath, args, e.getMessage());
+ }
+ catch (IOException e) {
+ throw new PyExternalProcessException(ERROR_ACCESS_DENIED, helperPath, args, e.getMessage());
}
}
else {
- if (workingDir == null) {
- workingDir = new File(homePath).getParent();
- }
- final List<String> cmdline = new ArrayList<String>();
- cmdline.add(homePath);
- cmdline.add(helperPath);
- cmdline.addAll(args);
- LOG.info("Running packaging tool: " + StringUtil.join(cmdline, " "));
-
- final boolean canCreate = FileUtil.ensureCanCreateFile(new File(homePath));
- if (!canCreate && !SystemInfo.isWindows && askForSudo) { //is system site interpreter --> we need sudo privileges
- try {
- final ProcessOutput result = ExecUtil.sudoAndGetOutput(cmdline,
- "Please enter your password to make changes in system packages: ",
- workingDir);
- 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.";
- }
- throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
- }
- if (SystemInfo.isMac && !StringUtil.isEmptyOrSpaces(message)) {
- throw new PyExternalProcessException(result.getExitCode(), helperPath, args, message);
- }
- return result;
- }
- catch (ExecutionException e) {
- throw new PyExternalProcessException(ERROR_EXECUTION, helperPath, args, e.getMessage());
- }
- catch (IOException e) {
- throw new PyExternalProcessException(ERROR_ACCESS_DENIED, helperPath, args, e.getMessage());
- }
- }
- else {
- return PySdkUtil.getProcessOutput(workingDir, ArrayUtil.toStringArray(cmdline), TIMEOUT);
- }
+ return PySdkUtil.getProcessOutput(workingDir, ArrayUtil.toStringArray(cmdline), TIMEOUT);
}
}
- private static String quoteIfNeeded(String arg) {
- return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode
- }
-
@NotNull
private static List<PyPackage> parsePackagingToolOutput(@NotNull String s) throws PyExternalProcessException {
final String[] lines = StringUtil.splitByLines(s);
@@ -976,17 +528,6 @@ public class PyPackageManagerImpl extends PyPackageManager {
return packages;
}
-
- @Override
- public void showInstallationError(Project project, String title, String description) {
- PackagesNotificationPanel.showError(project, title, description);
- }
-
- @Override
- public void showInstallationError(Component owner, String title, String description) {
- PackagesNotificationPanel.showError(owner, title, description);
- }
-
private class MySdkRootWatcher extends BulkFileListener.Adapter {
@Override
public void after(@NotNull List<? extends VFileEvent> events) {
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
new file mode 100644
index 000000000000..28dfa6c834a4
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagerUI.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.packaging;
+
+import com.intellij.icons.AllIcons;
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.Function;
+import com.intellij.webcore.packaging.PackagesNotificationPanel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.event.HyperlinkEvent;
+import java.util.*;
+
+/**
+* @author vlan
+*/
+public class PyPackageManagerUI {
+ private static final Logger LOG = Logger.getInstance(PyPackageManagerUI.class);
+ @Nullable private Listener myListener;
+ @NotNull private Project myProject;
+ @NotNull private Sdk mySdk;
+
+ public interface Listener {
+ void started();
+
+ void finished(List<PyExternalProcessException> exceptions);
+ }
+
+ public PyPackageManagerUI(@NotNull Project project, @NotNull Sdk sdk, @Nullable Listener listener) {
+ myProject = project;
+ mySdk = sdk;
+ myListener = listener;
+ }
+
+ public void installManagement() {
+ ProgressManager.getInstance().run(new InstallManagementTask(myProject, mySdk, myListener));
+ }
+
+ public void install(@NotNull final List<PyRequirement> requirements, @NotNull final List<String> extraArgs) {
+ ProgressManager.getInstance().run(new InstallTask(myProject, mySdk, requirements, extraArgs, myListener));
+ }
+
+ public void uninstall(@NotNull final List<PyPackage> packages) {
+ if (checkDependents(packages)) {
+ return;
+ }
+ ProgressManager.getInstance().run(new UninstallTask(myProject, mySdk, myListener, packages));
+ }
+
+ private boolean checkDependents(@NotNull final List<PyPackage> packages) {
+ try {
+ final Map<String, Set<PyPackage>> dependentPackages = collectDependents(packages, mySdk);
+ final int[] warning = {0};
+ if (!dependentPackages.isEmpty()) {
+ ApplicationManager.getApplication().invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ if (dependentPackages.size() == 1) {
+ String message = "You are attempting to uninstall ";
+ List<String> dep = new ArrayList<String>();
+ int size = 1;
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ final Set<PyPackage> value = entry.getValue();
+ size = value.size();
+ dep.add(entry.getKey() + " package which is required for " + StringUtil.join(value, ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += size == 1 ? " package" : " packages";
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ else {
+ String message = "You are attempting to uninstall packages which are required for another packages.\n\n";
+ List<String> dep = new ArrayList<String>();
+ for (Map.Entry<String, Set<PyPackage>> entry : dependentPackages.entrySet()) {
+ dep.add(entry.getKey() + " -> " + StringUtil.join(entry.getValue(), ", "));
+ }
+ message += StringUtil.join(dep, "\n");
+ message += "\n\nDo you want to proceed?";
+ warning[0] = Messages.showYesNoDialog(message, "Warning",
+ AllIcons.General.BalloonWarning);
+ }
+ }
+ }, ModalityState.current());
+ }
+ if (warning[0] != Messages.YES) return true;
+ }
+ catch (PyExternalProcessException e) {
+ LOG.info("Error loading packages dependents: " + e.getMessage(), e);
+ }
+ return false;
+ }
+
+ private static Map<String, Set<PyPackage>> collectDependents(@NotNull final List<PyPackage> packages, Sdk sdk)
+ throws PyExternalProcessException {
+ Map<String, Set<PyPackage>> dependentPackages = new HashMap<String, Set<PyPackage>>();
+ for (PyPackage pkg : packages) {
+ final Set<PyPackage> dependents = PyPackageManager.getInstance(sdk).getDependents(pkg);
+ if (dependents != null && !dependents.isEmpty()) {
+ for (PyPackage dependent : dependents) {
+ if (!packages.contains(dependent)) {
+ dependentPackages.put(pkg.getName(), dependents);
+ }
+ }
+ }
+ }
+ return dependentPackages;
+ }
+
+ private abstract static class PackagingTask extends Task.Backgroundable {
+ private static final String PACKAGING_GROUP_ID = "Packaging";
+
+ @Nullable protected final Listener myListener;
+
+ public PackagingTask(@Nullable Project project, @NotNull String title, @Nullable Listener listener) {
+ super(project, title);
+ myListener = listener;
+ }
+
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+ taskStarted(indicator);
+ taskFinished(runTask(indicator));
+ }
+
+ @NotNull
+ protected abstract List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator);
+
+ @NotNull
+ protected abstract String getSuccessTitle();
+
+ @NotNull
+ protected abstract String getSuccessDescription();
+
+ @NotNull
+ protected abstract String getFailureTitle();
+
+ protected void taskStarted(@NotNull ProgressIndicator indicator) {
+ indicator.setText(getTitle() + "...");
+ if (myListener != null) {
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ myListener.started();
+ }
+ });
+ }
+ }
+
+ protected void taskFinished(@NotNull final List<PyExternalProcessException> exceptions) {
+ final Ref<Notification> notificationRef = new Ref<Notification>(null);
+ if (exceptions.isEmpty()) {
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getSuccessTitle(), getSuccessDescription(),
+ NotificationType.INFORMATION));
+ }
+ else {
+ final String firstLine = getTitle() + ": error occurred.";
+ final String description = createDescription(exceptions, firstLine);
+ notificationRef.set(new Notification(PACKAGING_GROUP_ID, getFailureTitle(),
+ firstLine + " <a href=\"xxx\">Details...</a>",
+ NotificationType.ERROR,
+ new NotificationListener() {
+ @Override
+ public void hyperlinkUpdate(@NotNull Notification notification,
+ @NotNull HyperlinkEvent event) {
+ assert myProject != null;
+ PackagesNotificationPanel.showError(myProject, getFailureTitle(), description);
+ }
+ }
+ ));
+ }
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ if (myListener != null) {
+ myListener.finished(exceptions);
+ }
+ final Notification notification = notificationRef.get();
+ if (notification != null) {
+ notification.notify(myProject);
+ }
+ }
+ });
+ }
+ }
+
+ private static class InstallTask extends PackagingTask {
+ @NotNull protected final Sdk mySdk;
+ @NotNull private final List<PyRequirement> myRequirements;
+ @NotNull private final List<String> myExtraArgs;
+
+ public InstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @NotNull List<PyRequirement> requirements,
+ @NotNull List<String> extraArgs,
+ @Nullable Listener listener) {
+ super(project, "Installing packages", listener);
+ mySdk = sdk;
+ myRequirements = requirements;
+ myExtraArgs = extraArgs;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final int size = myRequirements.size();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ for (int i = 0; i < size; i++) {
+ final PyRequirement requirement = myRequirements.get(i);
+ indicator.setText(String.format("Installing package '%s'...", requirement));
+ indicator.setFraction((double)i / size);
+ try {
+ manager.install(Arrays.asList(requirement), myExtraArgs);
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages installed successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed packages: " + PyPackageUtil.requirementsToString(myRequirements);
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Install packages failed";
+ }
+ }
+
+ private static class InstallManagementTask extends InstallTask {
+
+ public InstallManagementTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener) {
+ super(project, sdk, Collections.<PyRequirement>emptyList(), Collections.<String>emptyList(), listener);
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final List<PyExternalProcessException> exceptions = new ArrayList<PyExternalProcessException>();
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ indicator.setText("Installing packaging tools...");
+ indicator.setIndeterminate(true);
+ try {
+ manager.installManagement();
+ }
+ catch (PyExternalProcessException e) {
+ exceptions.add(e);
+ }
+ manager.refresh();
+ return exceptions;
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ return "Installed Python packaging tools";
+ }
+ }
+
+ private static class UninstallTask extends PackagingTask {
+ @NotNull private final Sdk mySdk;
+ @NotNull private final List<PyPackage> myPackages;
+
+ public UninstallTask(@Nullable Project project,
+ @NotNull Sdk sdk,
+ @Nullable Listener listener,
+ @NotNull List<PyPackage> packages) {
+ super(project, "Uninstalling packages", listener);
+ mySdk = sdk;
+ myPackages = packages;
+ }
+
+ @NotNull
+ @Override
+ protected List<PyExternalProcessException> runTask(@NotNull ProgressIndicator indicator) {
+ final PyPackageManager manager = PyPackageManagers.getInstance().forSdk(mySdk);
+ try {
+ manager.uninstall(myPackages);
+ return Arrays.asList();
+ }
+ catch (PyExternalProcessException e) {
+ return Arrays.asList(e);
+ }
+ finally {
+ manager.refresh();
+ }
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessTitle() {
+ return "Packages uninstalled successfully";
+ }
+
+ @NotNull
+ @Override
+ protected String getSuccessDescription() {
+ final String packagesString = StringUtil.join(myPackages, new Function<PyPackage, String>() {
+ @Override
+ public String fun(PyPackage pkg) {
+ return "'" + pkg.getName() + "'";
+ }
+ }, ", ");
+ return "Uninstalled packages: " + packagesString;
+ }
+
+ @NotNull
+ @Override
+ protected String getFailureTitle() {
+ return "Uninstall packages failed";
+ }
+ }
+
+ public static String createDescription(List<PyExternalProcessException> exceptions, String firstLine) {
+ final StringBuilder b = new StringBuilder();
+ b.append(firstLine);
+ b.append("\n\n");
+ for (PyExternalProcessException exception : exceptions) {
+ b.append(exception.toString());
+ b.append("\n");
+ }
+ return b.toString();
+ }
+}
diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
index 054e9e01d23f..e68cede3f799 100644
--- a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
+++ b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java
@@ -15,13 +15,11 @@
*/
package com.jetbrains.python.packaging;
-import com.intellij.openapi.module.Module;
import com.intellij.openapi.projectRoots.Sdk;
+import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
/**
@@ -36,22 +34,14 @@ public class PyPackageManagersImpl extends PyPackageManagers {
final String name = sdk.getName();
PyPackageManagerImpl manager = myInstances.get(name);
if (manager == null) {
- manager = new PyPackageManagerImpl(sdk);
+ if (PythonSdkType.isRemote(sdk)) {
+ manager = new PyRemotePackageManagerImpl(sdk);
+ }
+ else {
+ manager = new PyPackageManagerImpl(sdk);
+ }
myInstances.put(name, manager);
}
return manager;
}
-
- @Nullable
- @Override
- public List<PyRequirement> getRequirements(Module module) {
- return PyPackageManagerImpl.getRequirements(module);
- }
-
-
- @Nullable
- @Override
- public List<PyRequirement> getRequirementsFromTxt(Module module) {
- return PyPackageManagerImpl.getRequirementsFromTxt(module);
- }
}
diff --git a/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java
new file mode 100644
index 000000000000..6c6de77d9454
--- /dev/null
+++ b/python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java
@@ -0,0 +1,177 @@
+/*
+ * 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.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.process.ProcessOutput;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.SdkAdditionalData;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.remote.RemoteFile;
+import com.intellij.remote.RemoteSdkAdditionalData;
+import com.intellij.remote.RemoteSdkCredentials;
+import com.intellij.remote.VagrantNotStartedException;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.PathMappingSettings;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+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) {
+ super(sdk);
+ }
+
+ @Nullable
+ @Override
+ protected String getHelperPath(String helper) {
+ final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
+ final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+ try {
+ final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
+ if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
+ return new RemoteFile(remoteSdkCredentials.getHelpersPath(), helper).getPath();
+ }
+ else {
+ return null;
+ }
+ }
+ catch (Exception e) {
+ LOG.error(e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected ProcessOutput getProcessOutput(@NotNull String helperPath,
+ @NotNull List<String> args,
+ boolean askForSudo,
+ @Nullable String workingDir) throws PyExternalProcessException {
+ final String homePath = mySdk.getHomePath();
+ if (homePath == null) {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Cannot find interpreter for SDK");
+ }
+ final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
+ if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter
+ RemoteSdkCredentials remoteSdkCredentials;
+ try {
+ remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
+ }
+ catch (InterruptedException e) {
+ LOG.error(e);
+ remoteSdkCredentials = null;
+ }
+ catch (final ExecutionException e) {
+ if (e.getCause() instanceof VagrantNotStartedException) {
+ throw new PyExternalProcessException(ERROR_VAGRANT_NOT_LAUNCHED, helperPath, args, "Vagrant instance is down. <a href=\"" +
+ LAUNCH_VAGRANT +
+ "\">Launch vagrant</a>")
+ .withHandler(LAUNCH_VAGRANT, new Runnable() {
+ @Override
+ public void run() {
+ final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+ if (manager != null) {
+
+ try {
+ manager.runVagrant(((VagrantNotStartedException)e.getCause()).getVagrantFolder());
+ clearCaches();
+ }
+ catch (ExecutionException e1) {
+ throw new RuntimeException(e1);
+ }
+ }
+ }
+ });
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_REMOTE_ACCESS, helperPath, args, e.getMessage());
+ }
+ }
+ final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+ if (manager != null && remoteSdkCredentials != null) {
+ final List<String> cmdline = new ArrayList<String>();
+ cmdline.add(homePath);
+ cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath());
+ cmdline.addAll(Collections2.transform(args, new Function<String, String>() {
+ @Override
+ public String apply(@Nullable String input) {
+ 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;
+ }
+ catch (ExecutionException e) {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Error running SDK: " + e.getMessage(), e);
+ }
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args,
+ PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED);
+ }
+ }
+ else {
+ throw new PyExternalProcessException(ERROR_INVALID_SDK, helperPath, args, "Invalid remote SDK");
+ }
+ }
+
+ @Override
+ protected void subscribeToLocalChanges(Sdk sdk) {
+ // Local VFS changes aren't needed
+ }
+
+ @Override
+ protected void installManagement(@NotNull String name) throws PyExternalProcessException {
+ super.installManagement(name);
+ // TODO: remove temp directory for remote interpreter
+ }
+
+ private static String quoteIfNeeded(String arg) {
+ return arg.replace("<", "\\<").replace(">", "\\>"); //TODO: move this logic to ParametersListUtil.encode
+ }
+}
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
index 348d2817ec75..094149336d5d 100644
--- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
+++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java
@@ -25,10 +25,8 @@ import com.intellij.util.Consumer;
import com.intellij.webcore.packaging.InstalledPackage;
import com.intellij.webcore.packaging.InstalledPackagesPanel;
import com.intellij.webcore.packaging.PackagesNotificationPanel;
-import com.jetbrains.python.packaging.PyExternalProcessException;
-import com.jetbrains.python.packaging.PyPackage;
-import com.jetbrains.python.packaging.PyPackageManager;
-import com.jetbrains.python.packaging.PyPackageManagerImpl;
+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;
@@ -42,33 +40,13 @@ import java.util.Set;
* @author yole
*/
public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
- public static final String INSTALL_SETUPTOOLS = "installSetuptools";
- public static final String INSTALL_PIP = "installPip";
+ public static final String INSTALL_MANAGEMENT = "installManagement";
public static final String CREATE_VENV = "createVEnv";
- private boolean myHasSetuptools;
- private boolean myHasPip = true;
+ private boolean myHasManagement = false;
public PyInstalledPackagesPanel(Project project, PackagesNotificationPanel area) {
super(project, area);
- myNotificationArea.addLinkHandler(INSTALL_SETUPTOOLS, new Runnable() {
- @Override
- public void run() {
- final Sdk sdk = getSelectedSdk();
- if (sdk != null) {
- installManagementTool(sdk, PyPackageManagerImpl.SETUPTOOLS);
- }
- }
- });
- myNotificationArea.addLinkHandler(INSTALL_PIP, new Runnable() {
- @Override
- public void run() {
- final Sdk sdk = getSelectedSdk();
- if (sdk != null) {
- installManagementTool(sdk, PyPackageManagerImpl.PIP);
- }
- }
- });
}
private Sdk getSelectedSdk() {
@@ -85,19 +63,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
application.executeOnPooledThread(new Runnable() {
@Override
public void run() {
- PyExternalProcessException exc = null;
- try {
- PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(selectedSdk);
- myHasSetuptools = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_SETUPTOOLS) != null;
- if (!myHasSetuptools) {
- myHasSetuptools = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_DISTRIBUTE) != null;
- }
- myHasPip = packageManager.findInstalledPackage(PyPackageManagerImpl.PACKAGE_PIP) != null;
- }
- catch (PyExternalProcessException e) {
- exc = e;
- }
- final PyExternalProcessException externalProcessException = exc;
+ PyPackageManager packageManager = PyPackageManager.getInstance(selectedSdk);
+ myHasManagement = packageManager.hasManagement(false);
application.invokeLater(new Runnable() {
@Override
public void run() {
@@ -112,42 +79,24 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
myNotificationArea.hide();
if (!invalid) {
String text = null;
- if (externalProcessException != null) {
- final int retCode = externalProcessException.getRetcode();
- if (retCode == PyPackageManagerImpl.ERROR_NO_PIP) {
- myHasPip = false;
- }
- else if (retCode == PyPackageManagerImpl.ERROR_NO_SETUPTOOLS) {
- myHasSetuptools = false;
- }
- else {
- text = externalProcessException.getMessage();
- }
- final boolean hasPackagingTools = myHasPip && myHasSetuptools;
- allowCreateVirtualEnv &= !hasPackagingTools;
-
- if (externalProcessException.hasHandler()) {
- final String key = externalProcessException.getHandler().first;
- myNotificationArea.addLinkHandler(key,
- new Runnable() {
- @Override
- public void run() {
- externalProcessException.getHandler().second.run();
- myNotificationArea.removeLinkHandler(key);
- updateNotifications(selectedSdk);
+ 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 (text == null) {
- if (!myHasSetuptools) {
- text = "Python package management tools not found. <a href=\"" + INSTALL_SETUPTOOLS + "\">Install 'setuptools'</a>";
- }
- else if (!myHasPip) {
- text = "Python packaging tool 'pip' not found. <a href=\"" + INSTALL_PIP + "\">Install 'pip'</a>";
- }
+ if (!myHasManagement) {
+ text = "Python packaging tools not found. <a href=\"" + INSTALL_MANAGEMENT + "\">Install packaging tools</a>";
}
if (text != null) {
if (allowCreateVirtualEnv) {
@@ -157,7 +106,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
}
}
- myInstallButton.setEnabled(!invalid && externalProcessException == null && myHasPip);
+ myInstallButton.setEnabled(!invalid && myHasManagement);
}
}
}, ModalityState.any());
@@ -170,8 +119,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
return Sets.newHashSet("pip", "distutils", "setuptools");
}
- private void installManagementTool(@NotNull final Sdk sdk, final String name) {
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, sdk, new PyPackageManagerImpl.UI.Listener() {
+ private void installManagementTools(@NotNull final Sdk sdk) {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, sdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
myPackagesTable.setPaintBusy(true);
@@ -180,11 +129,11 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
@Override
public void finished(List<PyExternalProcessException> exceptions) {
myPackagesTable.setPaintBusy(false);
- PyPackageManagerImpl packageManager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
+ PyPackageManager packageManager = PyPackageManager.getInstance(sdk);
if (!exceptions.isEmpty()) {
- final String firstLine = "Install package failed. ";
- final String description = PyPackageManagerImpl.UI.createDescription(exceptions, firstLine);
- packageManager.showInstallationError(myProject, "Failed to install " + name, description);
+ 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));
@@ -194,22 +143,22 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
updateNotifications(sdk);
}
});
- ui.installManagement(name);
+ ui.installManagement();
}
@Override
protected boolean canUninstallPackage(InstalledPackage pkg) {
- if (!myHasPip) return false;
+ if (!myHasManagement) return false;
if (PythonSdkType.isVirtualEnv(getSelectedSdk()) && pkg instanceof PyPackage) {
final String location = ((PyPackage)pkg).getLocation();
- if (location != null && location.startsWith(PyPackageManagerImpl.getUserSite())) {
+ if (location != null && location.startsWith(PySdkUtil.getUserSite())) {
return false;
}
}
final String name = pkg.getName();
- if (PyPackageManagerImpl.PACKAGE_PIP.equals(name) ||
- PyPackageManagerImpl.PACKAGE_SETUPTOOLS.equals(name) ||
- PyPackageManagerImpl.PACKAGE_DISTRIBUTE.equals(name)) {
+ if (PyPackageManager.PACKAGE_PIP.equals(name) ||
+ PyPackageManager.PACKAGE_SETUPTOOLS.equals(name) ||
+ PyPackageManager.PACKAGE_DISTRIBUTE.equals(name)) {
return false;
}
return true;
@@ -217,6 +166,6 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel {
@Override
protected boolean canUpgradePackage(InstalledPackage pyPackage) {
- return myHasPip;
+ return myHasManagement;
}
}
diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
index ffd291a995b1..95330b361dfb 100644
--- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
+++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java
@@ -23,6 +23,7 @@ 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.sdk.PySdkUtil;
import com.jetbrains.python.sdk.PythonSdkType;
import org.apache.xmlrpc.AsyncCallback;
import org.jetbrains.annotations.NonNls;
@@ -112,7 +113,7 @@ public class PyPackageManagementService extends PackageManagementService {
public String getInstallToUserText() {
String userSiteText = "Install to user's site packages directory";
if (!PythonSdkType.isRemote(mySdk))
- userSiteText += " (" + PyPackageManagerImpl.getUserSite() + ")";
+ userSiteText += " (" + PySdkUtil.getUserSite() + ")";
return userSiteText;
}
@@ -130,12 +131,12 @@ public class PyPackageManagementService extends PackageManagementService {
public Collection<InstalledPackage> getInstalledPackages() throws IOException {
List<PyPackage> packages;
try {
- packages = ((PyPackageManagerImpl)PyPackageManager.getInstance(mySdk)).getPackages();
+ packages = PyPackageManager.getInstance(mySdk).getPackages(false);
}
catch (PyExternalProcessException e) {
throw new IOException(e);
}
- return new ArrayList<InstalledPackage>(packages);
+ return packages != null ? new ArrayList<InstalledPackage>(packages) : new ArrayList<InstalledPackage>();
}
@Override
@@ -145,7 +146,7 @@ public class PyPackageManagementService extends PackageManagementService {
final String repository = PyPIPackageUtil.PYPI_URL.equals(repoPackage.getRepoUrl()) ? null : repoPackage.getRepoUrl();
final List<String> extraArgs = new ArrayList<String>();
if (installToUser) {
- extraArgs.add(PyPackageManagerImpl.USE_USER_SITE);
+ extraArgs.add(PyPackageManager.USE_USER_SITE);
}
if (extraOptions != null) {
// TODO: Respect arguments quotation
@@ -166,7 +167,7 @@ public class PyPackageManagementService extends PackageManagementService {
req = new PyRequirement(packageName);
}
- final PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, mySdk, new PyPackageManagerImpl.UI.Listener() {
+ final PyPackageManagerUI ui = new PyPackageManagerUI(myProject, mySdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
listener.operationStarted(packageName);
@@ -183,7 +184,7 @@ public class PyPackageManagementService extends PackageManagementService {
private String toErrorDescription(List<PyExternalProcessException> exceptions) {
String errorDescription = null;
if (exceptions != null && exceptions.size() > 0) {
- errorDescription = PyPackageManagerImpl.UI.createDescription(exceptions, "");
+ errorDescription = PyPackageManagerUI.createDescription(exceptions, "");
}
return errorDescription;
}
@@ -191,7 +192,7 @@ public class PyPackageManagementService extends PackageManagementService {
@Override
public void uninstallPackages(List<InstalledPackage> installedPackages, final Listener listener) {
final String packageName = installedPackages.size() == 1 ? installedPackages.get(0).getName() : null;
- PyPackageManagerImpl.UI ui = new PyPackageManagerImpl.UI(myProject, mySdk, new PyPackageManagerImpl.UI.Listener() {
+ PyPackageManagerUI ui = new PyPackageManagerUI(myProject, mySdk, new PyPackageManagerUI.Listener() {
@Override
public void started() {
listener.operationStarted(packageName);