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 cdecb3fce64e..f88b4aca8392 100644 --- a/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java +++ b/platform/lang-impl/src/com/intellij/webcore/packaging/ManagePackagesDialog.java @@ -522,6 +522,7 @@ public class ManagePackagesDialog extends DialogWrapper { @Override public void consume(Exception exception) { + myDescriptionTextArea.setText("No information available"); LOG.info("Error retrieving package details", exception); } }); diff --git a/python/helpers/conda_packaging_tool.py b/python/helpers/conda_packaging_tool.py new file mode 100644 index 000000000000..b4680b915e97 --- /dev/null +++ b/python/helpers/conda_packaging_tool.py @@ -0,0 +1,50 @@ +import sys +import traceback + +ERROR_WRONG_USAGE = 1 +ERROR_EXCEPTION = 4 + +def usage(): + sys.stderr.write('Usage: conda_packaging_tool.py \n') + sys.stderr.flush() + exit(ERROR_WRONG_USAGE) + +def do_list_available_packages(): + from conda.cli.main_search import common + index = common.get_index_trap() + for pkg in index.values(): + sys.stdout.write("\t".join([pkg["name"], pkg["version"], ":".join(pkg["depends"])])+chr(10)) + sys.stdout.flush() + + +def do_list_channels(): + import conda.config as config + for channel in config.get_channel_urls(): + sys.stdout.write(channel+chr(10)) + sys.stdout.flush() + +def main(): + retcode = 0 + try: + if len(sys.argv) < 2: + usage() + cmd = sys.argv[1] + if cmd == 'listall': + if len(sys.argv) != 2: + usage() + return + do_list_available_packages() + elif cmd == 'channels': + if len(sys.argv) != 2: + usage() + return + do_list_channels() + else: + usage() + except Exception: + traceback.print_exc() + exit(ERROR_EXCEPTION) + exit(retcode) + +if __name__ == '__main__': + main() diff --git a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java index cd01a4037866..7099a4731a09 100644 --- a/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java +++ b/python/ide/src/com/jetbrains/python/configuration/PyActiveSdkConfigurable.java @@ -37,8 +37,8 @@ import com.intellij.ui.CollectionComboBoxModel; import com.intellij.util.NullableConsumer; import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.packaging.PyPackageManagers; import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel; -import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.sdk.*; @@ -94,7 +94,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { @Override public void actionPerformed(ActionEvent e) { final Sdk selectedSdk = (Sdk)mySdkCombo.getSelectedItem(); - myPackagesPanel.updatePackages(selectedSdk != null ? new PyPackageManagementService(myProject, selectedSdk) : null); + myPackagesPanel.updatePackages(selectedSdk != null ? PyPackageManagers.getInstance().getManagementService(myProject, selectedSdk) : null); myPackagesPanel.updateNotifications(selectedSdk); } }); @@ -148,7 +148,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { } updateSdkList(false); mySdkCombo.getModel().setSelectedItem(myProjectSdksModel.findSdk(sdk.getName())); - myPackagesPanel.updatePackages(new PyPackageManagementService(myProject, sdk)); + myPackagesPanel.updatePackages(PyPackageManagers.getInstance().getManagementService(myProject, sdk)); myPackagesPanel.updateNotifications(sdk); } }; @@ -336,7 +336,7 @@ public class PyActiveSdkConfigurable implements UnnamedConfigurable { final Sdk sdk = getSdk(); mySdkCombo.getModel().setSelectedItem(sdk == null ? null : myProjectSdksModel.findSdk(sdk.getName())); - myPackagesPanel.updatePackages(sdk != null ? new PyPackageManagementService(myProject, sdk) : null); + myPackagesPanel.updatePackages(sdk != null ? PyPackageManagers.getInstance().getManagementService(myProject, sdk) : null); myPackagesPanel.updateNotifications(sdk); } diff --git a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java index 3b3cde32f9e6..abb8d384a891 100644 --- a/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java +++ b/python/openapi/src/com/jetbrains/python/packaging/PyPackageManagers.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2015 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. @@ -16,7 +16,9 @@ package com.jetbrains.python.packaging; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.webcore.packaging.PackageManagementService; import org.jetbrains.annotations.NotNull; /** @@ -31,4 +33,6 @@ public abstract class PyPackageManagers { @NotNull public abstract PyPackageManager forSdk(Sdk sdk); + + public abstract PackageManagementService getManagementService(Project project, Sdk sdk); } diff --git a/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java b/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java index fecafdf570c4..6b04c6872153 100644 --- a/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java +++ b/python/pluginSrc/com/jetbrains/python/packaging/PyManagePackagesDialog.java @@ -22,7 +22,6 @@ import com.intellij.openapi.ui.LabeledComponent; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.webcore.packaging.PackagesNotificationPanel; import com.jetbrains.python.packaging.ui.PyInstalledPackagesPanel; -import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.sdk.PreferredSdkComparator; import com.jetbrains.python.sdk.PySdkListCellRenderer; import com.jetbrains.python.sdk.PythonSdkType; @@ -53,7 +52,7 @@ public class PyManagePackagesDialog extends DialogWrapper { 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)); + packagesPanel.updatePackages(PyPackageManagers.getInstance().getManagementService(project, sdk)); packagesPanel.updateNotifications(sdk); myMainPanel = new JPanel(new BorderLayout()); @@ -67,7 +66,7 @@ public class PyManagePackagesDialog extends DialogWrapper { @Override public void actionPerformed(ActionEvent e) { Sdk sdk = (Sdk) sdkComboBox.getSelectedItem(); - packagesPanel.updatePackages(new PyPackageManagementService(project, sdk)); + packagesPanel.updatePackages(PyPackageManagers.getInstance().getManagementService(project, sdk)); packagesPanel.updateNotifications(sdk); } }); diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index 410b3ff9b4cc..52b9200c0947 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -93,6 +93,8 @@ + @@ -523,7 +525,7 @@ - + diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java new file mode 100644 index 000000000000..9883e5cb21c3 --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageManagerImpl.java @@ -0,0 +1,203 @@ +/* + * Copyright 2000-2015 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.collect.Lists; +import com.intellij.execution.ExecutionException; +import com.intellij.execution.RunCanceledByUserException; +import com.intellij.execution.configurations.GeneralCommandLine; +import com.intellij.execution.process.CapturingProcessHandler; +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PyCondaPackageManagerImpl extends PyPackageManagerImpl { + public static final String PYTHON = "python"; + + PyCondaPackageManagerImpl(@NotNull Sdk sdk) { + super(sdk); + } + + @Override + public void installManagement() throws ExecutionException { + } + + + @Override + public boolean hasManagement(boolean cachedOnly) throws ExecutionException { + return getCondaExecutable(mySdk) != null; + } + + @Override + protected void installManagement(@NotNull String name) throws ExecutionException { + } + + @Override + public void install(@NotNull List requirements, @NotNull List extraArgs) throws ExecutionException { + final ArrayList arguments = new ArrayList(); + for (PyRequirement requirement : requirements) { + arguments.add(requirement.toString()); + } + arguments.add("-y"); + if (extraArgs.contains("-U")) { + getCondaOutput("update", arguments); + } + else { + arguments.addAll(extraArgs); + getCondaOutput("install", arguments); + } + } + + private ProcessOutput getCondaOutput(@NotNull final String command, List arguments) throws ExecutionException { + final String condaExecutable = getCondaExecutable(mySdk); + + final String path = getCondaDirectory(); + if (path == null) throw new PyExecutionException("Empty conda name for " + mySdk, command, arguments); + + final ArrayList parameters = Lists.newArrayList(condaExecutable, command, "-p", path); + parameters.addAll(arguments); + + final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); + final Process process = commandLine.createProcess(); + final CapturingProcessHandler handler = new CapturingProcessHandler(process); + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + final ProcessOutput result; + if (indicator != null) { + result = handler.runProcessWithProgressIndicator(indicator); + } + else { + result = handler.runProcess(); + } + if (result.isCancelled()) { + throw new RunCanceledByUserException(); + } + 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, "Conda", parameters, result); + } + return result; + } + + @Nullable + private String getCondaDirectory() { + final VirtualFile homeDirectory = mySdk.getHomeDirectory(); + if (homeDirectory == null) return null; + return homeDirectory.getParent().getParent().getPath(); + } + + @Override + public void install(@NotNull String requirementString) throws ExecutionException { + getCondaOutput("install", Lists.newArrayList(requirementString, "-y")); + } + + @Override + public void uninstall(@NotNull List packages) throws ExecutionException { + final ArrayList arguments = new ArrayList(); + for (PyPackage aPackage : packages) { + arguments.add(aPackage.getName()); + } + arguments.add("-y"); + + getCondaOutput("remove", arguments); + } + + @Nullable + @Override + public List getPackages(boolean cachedOnly) throws ExecutionException { + final ProcessOutput output = getCondaOutput("list", Lists.newArrayList("-e")); + return parseCondaToolOutput(output.getStdout()); + } + + @NotNull + protected static List parseCondaToolOutput(@NotNull String s) throws ExecutionException { + final String[] lines = StringUtil.splitByLines(s); + final List packages = new ArrayList(); + for (String line : lines) { + if (line.startsWith("#")) continue; + final List fields = StringUtil.split(line, "="); + if (fields.size() < 3) { + throw new PyExecutionException("Invalid conda output format", "conda", Collections.emptyList()); + } + final String name = fields.get(0); + final String version = fields.get(1); + final String location = fields.get(2); + final List requirements = new ArrayList(); + if (fields.size() >= 4) { + final String requiresLine = fields.get(3); + final String requiresSpec = StringUtil.join(StringUtil.split(requiresLine, ":"), "\n"); + requirements.addAll(PyRequirement.parse(requiresSpec)); + } + if (!"Python".equals(name)) { + packages.add(new PyPackage(name, version, location, requirements)); + } + } + return packages; + } + + @Nullable + public static String getCondaExecutable(Sdk sdk) { + final String condaName = SystemInfo.isWindows ? "conda.exe" : "conda"; + final VirtualFile homeDirectory = sdk.getHomeDirectory(); + if (homeDirectory == null) return null; + final VirtualFile condaExecutable = homeDirectory.getParent().findChild(condaName); + return condaExecutable != null ? condaExecutable.getPath() : null; + } + + @NotNull + @Override + public String createVirtualEnv(@NotNull String destinationDir, boolean useGlobalSite) throws ExecutionException { + final String condaExecutable = getCondaExecutable(mySdk); + final String versionString = mySdk.getVersionString(); + final LanguageLevel level = versionString != null ? LanguageLevel.fromPythonVersion(versionString) : LanguageLevel.PYTHON35; + + final ArrayList parameters = Lists.newArrayList(condaExecutable, "create", "-p", destinationDir, + "python=" + level.toString(), "-y"); + + final GeneralCommandLine commandLine = new GeneralCommandLine(parameters); + final Process process = commandLine.createProcess(); + final CapturingProcessHandler handler = new CapturingProcessHandler(process); + final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); + final ProcessOutput result = handler.runProcessWithProgressIndicator(indicator); + if (result.isCancelled()) { + throw new RunCanceledByUserException(); + } + 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, "Conda", parameters, result); + } + final String binary = PythonSdkType.getPythonExecutable(destinationDir); + final String binaryFallback = destinationDir + File.separator + "bin" + File.separator + "python"; + return (binary != null) ? binary : binaryFallback; + } + +} diff --git a/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java b/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java new file mode 100644 index 000000000000..0fa8822ef725 --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/PyCondaPackageService.java @@ -0,0 +1,180 @@ +/* + * Copyright 2000-2015 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.execution.process.ProcessOutput; +import com.intellij.openapi.components.*; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.SystemProperties; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.text.VersionComparatorUtil; +import com.intellij.util.xmlb.XmlSerializerUtil; +import com.jetbrains.python.PythonHelpersLocator; +import com.jetbrains.python.sdk.PySdkUtil; +import com.jetbrains.python.sdk.PythonSdkType; +import com.jetbrains.python.sdk.flavors.VirtualEnvSdkFlavor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; + +@State(name = "PyCondaPackageService", + storages = { + @Storage(file = StoragePathMacros.APP_CONFIG + "/conda_packages.xml") + } +) +public class PyCondaPackageService implements PersistentStateComponent { + public Map CONDA_PACKAGES = ContainerUtil.newConcurrentMap(); + public Map> PACKAGES_TO_RELEASES = new HashMap>(); + public Set CONDA_CHANNELS = ContainerUtil.newConcurrentSet(); + + public long LAST_TIME_CHECKED = 0; + + @Override + public PyCondaPackageService getState() { + return this; + } + + @Override + public void loadState(PyCondaPackageService state) { + XmlSerializerUtil.copyBean(state, this); + } + + public static PyCondaPackageService getInstance() { + return ServiceManager.getService(PyCondaPackageService.class); + } + + public Map getCondaPackages() { + return CONDA_PACKAGES; + } + + public Map loadAndGetPackages() { + if (CONDA_PACKAGES.isEmpty()) { + updatePackagesCache(); + } + return CONDA_PACKAGES; + } + + public Set loadAndGetChannels() { + if (CONDA_CHANNELS.isEmpty()) { + updateChannels(); + } + return CONDA_CHANNELS; + } + + @Nullable + public static String getCondaPython() { + final String condaName = SystemInfo.isWindows ? "python.exe" : "python"; + final VirtualFile userHome = LocalFileSystem.getInstance().findFileByPath(SystemProperties.getUserHome().replace('\\', '/')); + if (userHome != null) { + for (String root : VirtualEnvSdkFlavor.CONDA_DEFAULT_ROOTS) { + VirtualFile condaFolder = userHome.findChild(root); + String executableFile = findExecutable(condaName, condaFolder); + if (executableFile != null) return executableFile; + if (SystemInfo.isWindows) { + condaFolder = LocalFileSystem.getInstance().findFileByPath("C:\\" + root); + executableFile = findExecutable(condaName, condaFolder); + if (executableFile != null) return executableFile; + } + else { + final String systemWidePath = "/opt/anaconda"; + condaFolder = LocalFileSystem.getInstance().findFileByPath(systemWidePath); + executableFile = findExecutable(condaName, condaFolder); + if (executableFile != null) return executableFile; + } + } + } + + return null; + } + + @Nullable + private static String findExecutable(String condaName, VirtualFile condaFolder) { + if (condaFolder != null) { + final VirtualFile bin = condaFolder.findChild("bin"); + if (bin != null) { + final VirtualFile[] children = bin.getChildren(); + if (children.length == 0) return null; + final String executableFile = PythonSdkType.getExecutablePath(children[0].getPath(), condaName); + if (executableFile != null) { + return executableFile; + } + } + } + return null; + } + + public void updatePackagesCache() { + final String condaPython = getCondaPython(); + if (condaPython == null) return; + final String path = PythonHelpersLocator.getHelperPath("conda_packaging_tool.py"); + final String runDirectory = new File(condaPython).getParent(); + final ProcessOutput output = PySdkUtil.getProcessOutput(runDirectory, new String[]{condaPython, path, "listall"}); + if (output.getExitCode() != 0) return; + final List lines = output.getStdoutLines(); + for (String line : lines) { + final List split = StringUtil.split(line, "\t"); + if (split.size() < 2) continue; + final String aPackage = CONDA_PACKAGES.get(split.get(0)); + if (aPackage != null) { + if (VersionComparatorUtil.compare(aPackage, split.get(1)) < 0) + CONDA_PACKAGES.put(split.get(0), split.get(1)); + } + else { + CONDA_PACKAGES.put(split.get(0), split.get(1)); + } + + if (PACKAGES_TO_RELEASES.containsKey(split.get(0))) { + final List versions = PACKAGES_TO_RELEASES.get(split.get(0)); + if (!versions.contains(split.get(1))) { + versions.add(split.get(1)); + } + } + else { + final ArrayList versions = new ArrayList(); + versions.add(split.get(1)); + PACKAGES_TO_RELEASES.put(split.get(0), versions); + } + } + LAST_TIME_CHECKED = System.currentTimeMillis(); + } + + @NotNull + public List getPackageVersions(@NotNull final String packageName) { + if (PACKAGES_TO_RELEASES.containsKey(packageName)) { + return PACKAGES_TO_RELEASES.get(packageName); + } + return Collections.emptyList(); + } + + public void updateChannels() { + final String condaPython = getCondaPython(); + if (condaPython == null) return; + final String path = PythonHelpersLocator.getHelperPath("conda_packaging_tool.py"); + final String runDirectory = new File(condaPython).getParent(); + final ProcessOutput output = PySdkUtil.getProcessOutput(runDirectory, new String[]{condaPython, path, "channels"}); + if (output.getExitCode() != 0) return; + final List lines = output.getStdoutLines(); + for (String line : lines) { + CONDA_CHANNELS.add(line); + } + LAST_TIME_CHECKED = System.currentTimeMillis(); + } +} diff --git a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java index 4a04b3eb827d..6151b6037229 100644 --- a/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java +++ b/python/src/com/jetbrains/python/packaging/PyPackageManagersImpl.java @@ -15,7 +15,10 @@ */ package com.jetbrains.python.packaging; +import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; +import com.jetbrains.python.packaging.ui.PyCondaManagementService; +import com.jetbrains.python.packaging.ui.PyPackageManagementService; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; @@ -29,7 +32,6 @@ public class PyPackageManagersImpl extends PyPackageManagers { private final Map myInstances = new HashMap(); @NotNull - @Override public synchronized PyPackageManager forSdk(Sdk sdk) { final String name = sdk.getName(); PyPackageManagerImpl manager = myInstances.get(name); @@ -37,6 +39,9 @@ public class PyPackageManagersImpl extends PyPackageManagers { if (PythonSdkType.isRemote(sdk)) { manager = new PyRemotePackageManagerImpl(sdk); } + else if (PyCondaPackageManagerImpl.getCondaExecutable(sdk) != null) { + manager = new PyCondaPackageManagerImpl(sdk); + } else { manager = new PyPackageManagerImpl(sdk); } @@ -44,4 +49,11 @@ public class PyPackageManagersImpl extends PyPackageManagers { } return manager; } + + public PyPackageManagementService getManagementService(Project project, Sdk sdk) { + if (PyCondaPackageManagerImpl.getCondaExecutable(sdk) != null) { + return new PyCondaManagementService(project, sdk); + } + return new PyPackageManagementService(project, sdk); + } } diff --git a/python/src/com/jetbrains/python/packaging/PyPIPackagesUpdater.java b/python/src/com/jetbrains/python/packaging/PyPackagesUpdater.java similarity index 77% rename from python/src/com/jetbrains/python/packaging/PyPIPackagesUpdater.java rename to python/src/com/jetbrains/python/packaging/PyPackagesUpdater.java index f18d49a6e311..1c721ba6aa6a 100644 --- a/python/src/com/jetbrains/python/packaging/PyPIPackagesUpdater.java +++ b/python/src/com/jetbrains/python/packaging/PyPackagesUpdater.java @@ -18,7 +18,6 @@ package com.jetbrains.python.packaging; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.project.Project; @@ -34,19 +33,9 @@ import java.io.IOException; * PyPI cache updater * User : catherine */ -public class PyPIPackagesUpdater implements StartupActivity { +public class PyPackagesUpdater implements StartupActivity { private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.packaging.PyPIPackagesUpdater"); - public static PyPIPackagesUpdater getInstance() { - final StartupActivity[] extensions = Extensions.getExtensions(StartupActivity.POST_STARTUP_ACTIVITY); - for (StartupActivity extension : extensions) { - if (extension instanceof PyPIPackagesUpdater) { - return (PyPIPackagesUpdater) extension; - } - } - throw new UnsupportedOperationException("could not find self"); - } - @Override public void runActivity(@NotNull final Project project) { final Application application = ApplicationManager.getApplication(); @@ -68,19 +57,35 @@ public class PyPIPackagesUpdater implements StartupActivity { } }); } + if (checkCondaUpdateNeeded(project)) { + application.executeOnPooledThread(new Runnable() { + @Override + public void run() { + PyCondaPackageService.getInstance().updatePackagesCache(); + } + }); + } } + private static boolean checkCondaUpdateNeeded(Project project) { + if (!hasPython(project)) return false; + final long timeDelta = System.currentTimeMillis() - PyCondaPackageService.getInstance().LAST_TIME_CHECKED; + if (Math.abs(timeDelta) < DateFormatUtil.DAY) return false; + return true; + } - public static boolean checkNeeded(Project project, PyPackageService service) { - boolean hasPython = false; + private static boolean hasPython(Project project) { for (Module module : ModuleManager.getInstance(project).getModules()) { final Sdk sdk = PythonSdkType.findPythonSdk(module); if (sdk != null && sdk.getSdkType() instanceof PythonSdkType) { - hasPython = true; - break; + return true; } } - if (!hasPython) return false; + return false; + } + + public static boolean checkNeeded(Project project, PyPackageService service) { + if (!hasPython(project)) return false; final long timeDelta = System.currentTimeMillis() - service.LAST_TIME_CHECKED; if (Math.abs(timeDelta) < DateFormatUtil.DAY) return false; return true; diff --git a/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java new file mode 100644 index 000000000000..53ac6835f92a --- /dev/null +++ b/python/src/com/jetbrains/python/packaging/ui/PyCondaManagementService.java @@ -0,0 +1,78 @@ +/* + * Copyright 2000-2015 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.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.util.CatchingConsumer; +import com.intellij.webcore.packaging.InstalledPackage; +import com.intellij.webcore.packaging.PackageVersionComparator; +import com.intellij.webcore.packaging.RepoPackage; +import com.jetbrains.python.packaging.PyCondaPackageService; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PyCondaManagementService extends PyPackageManagementService { + + public PyCondaManagementService(@NotNull final Project project, @NotNull final Sdk sdk) { + super(project, sdk); + } + + @Override + @NotNull + public List getAllPackagesCached() { + return versionMapToPackageList(PyCondaPackageService.getInstance().getCondaPackages()); + } + + @Override + @NotNull + public List getAllPackages() { + return versionMapToPackageList(PyCondaPackageService.getInstance().loadAndGetPackages()); + } + + @Override + @NotNull + public List reloadAllPackages() { + return getAllPackages(); + } + + @Override + public List getAllRepositories() { + List result = new ArrayList(); + result.addAll(PyCondaPackageService.getInstance().loadAndGetChannels()); + return result; + } + + @Override + public boolean canInstallToUser() { + return false; + } + + @Override + public void fetchPackageVersions(String packageName, CatchingConsumer, Exception> consumer) { + final List versions = PyCondaPackageService.getInstance().getPackageVersions(packageName); + Collections.sort(versions, Collections.reverseOrder(new PackageVersionComparator())); + consumer.consume(versions); + } + + @Override + public void uninstallPackages(List installedPackages, Listener listener) { + super.uninstallPackages(installedPackages, listener); + } +} diff --git a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java index b0dea29c4230..08173f209fde 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyInstalledPackagesPanel.java @@ -76,7 +76,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { PackagesNotificationPanel.showError("Failed to install Python packaging tools", description); } packageManager.refresh(); - updatePackages(new PyPackageManagementService(myProject, sdk)); + updatePackages(PyPackageManagers.getInstance().getManagementService(myProject, sdk)); updateNotifications(sdk); } }); @@ -130,7 +130,7 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { if (sdk != null) { fix.run(sdk); myNotificationArea.removeLinkHandler(key); - updatePackages(new PyPackageManagementService(myProject, sdk)); + updatePackages(PyPackageManagers.getInstance().getManagementService(myProject, sdk)); updateNotifications(sdk); } } @@ -164,7 +164,8 @@ public class PyInstalledPackagesPanel extends InstalledPackagesPanel { final String name = pkg.getName(); if (PyPackageManager.PIP.equals(name) || PyPackageManager.SETUPTOOLS.equals(name) || - PyPackageManager.DISTRIBUTE.equals(name)) { + PyPackageManager.DISTRIBUTE.equals(name) || + PyCondaPackageManagerImpl.PYTHON.equals(name)) { return false; } return true; diff --git a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java index 856c8567c146..ce47af624677 100644 --- a/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java +++ b/python/src/com/jetbrains/python/packaging/ui/PyPackageManagementService.java @@ -49,11 +49,12 @@ public class PyPackageManagementService extends PackageManagementService { private final Project myProject; private final Sdk mySdk; - public PyPackageManagementService(Project project, Sdk sdk) { + public PyPackageManagementService(@NotNull final Project project, @NotNull final Sdk sdk) { myProject = project; mySdk = sdk; } + @NotNull public Sdk getSdk() { return mySdk; } @@ -90,7 +91,7 @@ public class PyPackageManagementService extends PackageManagementService { return packages; } - private static List versionMapToPackageList(Map packageToVersionMap) { + protected static List versionMapToPackageList(Map packageToVersionMap) { final boolean customRepoConfigured = !PyPackageService.getInstance().additionalRepositories.isEmpty(); String url = customRepoConfigured ? PyPIPackageUtil.PYPI_URL : ""; List packages = new ArrayList(); diff --git a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java index c5244daf5196..c489033bf383 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/VirtualEnvSdkFlavor.java @@ -42,7 +42,7 @@ public class VirtualEnvSdkFlavor extends CPythonSdkFlavor { private VirtualEnvSdkFlavor() { } private final static String[] NAMES = new String[]{"jython", "pypy", "python.exe", "jython.bat", "pypy.exe"}; - private final static String[] CONDA_DEFAULT_ROOTS = new String[]{"anaconda", "anaconda3", "miniconda", "miniconda3", + public final static String[] CONDA_DEFAULT_ROOTS = new String[]{"anaconda", "anaconda3", "miniconda", "miniconda3", "Anaconda", "Anaconda3", "Miniconda", "Miniconda3"}; public static VirtualEnvSdkFlavor INSTANCE = new VirtualEnvSdkFlavor();