Commit 0373f87b authored by Aleksey Rostovskiy's avatar Aleksey Rostovskiy Committed by intellij-monorepo-bot
Browse files

IDEA-CR-45718: PY-34628 Bundle miniconda with PyCharm

(cherry picked from commit f45acd5cf462c6355827fbd2a950ea452cc3f856)

IDEA-CR-45683: Create Anaconda plugin that facilitates using Сonda management system (PY-34875)

(cherry picked from commit 3003039d9050c98ffd7a004e50aa12821681b1d3)

IDEA-CR-45718: PY-34628 Bundle miniconda with PyCharm

(cherry picked from commit f73f1970)

IDEA-CR-45683: Create Anaconda plugin that facilitates using Сonda management system (PY-34875)

(cherry picked from commit f9c9e2f6)

IDEA-CR-45765: PY-34993 Prepare PyCharm + Anaconda plugin installation process

(cherry picked from commit 3f62a1f407f327434719e1569336dbc6d5847d6a)

IDEA-CR-45858: PY-34993 build PyCharm Anaconda edition together with regular PyCharm

(cherry picked from commit f8b936317f24e3c80479b237163702e937ec5dc3)

IDEA-CR-45765: PY-34993 Prepare PyCharm + Anaconda plugin installation process

(cherry picked from commit 9596ad7d)

temporary workaround

(cherry picked from commit e904a8e3e3d76aee5daf63032276a759e4ce2ed0)

IDEA-CR-45850: cleanup

(cherry picked from commit c2b74413)

IDEA-CR-45858: PY-34993 build PyCharm Anaconda edition together with regular PyCharm

(cherry picked from commit 7f6ee080)

PY-35115 "Missing extension point: Pythonid.pythonSdkTypeComparator" trying to create a new project in 191.6605.9 fix

(cherry picked from commit f0e9ae9c)

IDEA-CR-45718: PY-34628 Bundle miniconda with PyCharm

(cherry picked from commit f45acd5cf462c6355827fbd2a950ea452cc3f856)

IDEA-CR-45683: Create Anaconda plugin that facilitates using Сonda management system (PY-34875)

(cherry picked from commit 3003039d9050c98ffd7a004e50aa12821681b1d3)

IDEA-CR-45718: PY-34628 Bundle miniconda with PyCharm

(cherry picked from commit f73f1970)

IDEA-CR-45683: Create Anaconda plugin that facilitates using Сonda management system (PY-34875)

(cherry picked from commit f9c9e2f6)

IDEA-CR-45765: PY-34993 Prepare PyCharm + Anaconda plugin installation process

(cherry picked from commit 3f62a1f407f327434719e1569336dbc6d5847d6a)

IDEA-CR-45858: PY-34993 build PyCharm Anaconda edition together with regular PyCharm

(cherry picked from commit f8b936317f24e3c80479b237163702e937ec5dc3)

IDEA-CR-45765: PY-34993 Prepare PyCharm + Anaconda plugin installation process

(cherry picked from commit 9596ad7d)

temporary workaround

(cherry picked from commit e904a8e3e3d76aee5daf63032276a759e4ce2ed0)

IDEA-CR-45850: cleanup

(cherry picked from commit c2b74413)

IDEA-CR-45858: PY-34993 build PyCharm Anaconda edition together with regular PyCharm

(cherry picked from commit 7f6ee080)

PY-35115 "Missing extension point: Pythonid.pythonSdkTypeComparator" trying to create a new project in 191.6605.9 fix

(cherry picked from commit f0e9ae9c)

GitOrigin-RevId: 709cab1522966eb844912d148306ddec8b0fbe5a
parent c174f139
Showing with 775 additions and 17 deletions
+775 -17
......@@ -2,11 +2,13 @@
package com.intellij.openapi.updateSettings.impl
import com.intellij.ide.IdeBundle
import com.intellij.ide.plugins.PluginManager
import com.intellij.ide.util.DelegatingProgressIndicator
import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.application.ex.ApplicationInfoEx
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.ProgressManager
......@@ -31,6 +33,8 @@ object UpdateInstaller {
private const val PATCH_FILE_NAME = "patch-file.zip"
private const val UPDATER_ENTRY = "com/intellij/updater/Runner.class"
private val LOG = Logger.getInstance(UpdateChecker::class.java)
private val patchesUrl: URL
get() = URL(System.getProperty("idea.patches.url") ?: ApplicationInfoEx.getInstanceEx().updateUrls.patchesUrl)
......@@ -41,13 +45,15 @@ object UpdateInstaller {
val files = mutableListOf<File>()
val product = ApplicationInfo.getInstance().build.productCode
val edition = getEditionSuffix(product)
val jdk = getJdkSuffix()
val share = 1.0 / (chain.size - 1)
for (i in 1 until chain.size) {
val from = chain[i - 1].withoutProductCode().asString()
val to = chain[i].withoutProductCode().asString()
val patchName = "${product}-${from}-${to}-patch${jdk}-${PatchInfo.OS_SUFFIX}.jar"
val patchName = "$product-$from-$to-patch$jdk$edition-${PatchInfo.OS_SUFFIX}.jar"
System.out.println(" patchName: $patchName")
val patchFile = File(getTempDir(), patchName)
val url = URL(patchesUrl, patchName).toString()
val partIndicator = object : DelegatingProgressIndicator(indicator) {
......@@ -82,9 +88,11 @@ object UpdateInstaller {
}
indicator.checkCanceled()
}
catch (e: ProcessCanceledException) { throw e }
catch (e: ProcessCanceledException) {
throw e
}
catch (e: Exception) {
Logger.getInstance(UpdateChecker::class.java).info(e)
LOG.info(e)
}
}
......@@ -98,7 +106,7 @@ object UpdateInstaller {
installed = true
}
catch (e: Exception) {
Logger.getInstance(UpdateChecker::class.java).info(e)
LOG.info(e)
}
}
}
......@@ -180,7 +188,7 @@ object UpdateInstaller {
private fun findLib(libName: String): File {
val libFile = File(PathManager.getLibPath(), libName)
return if (libFile.exists()) libFile else throw IOException("Missing: ${libFile}")
return if (libFile.exists()) libFile else throw IOException("Missing: $libFile")
}
private fun getTempDir() = File(PathManager.getTempPath(), "patch-update")
......@@ -193,7 +201,20 @@ object UpdateInstaller {
val version = try {
releaseFile.readLines().first { it.startsWith("JAVA_VERSION=") }.let { JavaVersion.parse(it) }.feature
}
catch (e: Exception) { 0 }
catch (e: Exception) {
0
}
return if (version == 11) "-jbr11" else ""
}
private fun getEditionSuffix(productCode: String): String {
if (productCode in listOf("PC", "PY")
&& File(PathManager.getHomePath(), "minicondaInstaller").exists()
&& PluginId.findId("org.jetbrains.plugins.anaconda")
?.let { PluginManager.getPlugin(it)?.isBundled } == true ) {
LOG.info("Anaconda edition patch should be taken")
return "-anaconda"
}
return ""
}
}
\ No newline at end of file
......@@ -38,4 +38,15 @@ class PyCharmBuildOptions {
static File getTemporaryFolderForUnzip(BuildContext context) {
return new File(context.paths.temp, "unzips")
}
/**
* Build PyCharm with Anaconda plugin distributions together with regular PyCharm
*/
static final String buildWithAnacondaEdition = SystemProperties.getBooleanProperty("intellij.build.pycharm.anaconda.edition", false)
/**
* Miniconda installer folder name
* The same folder should be specified at com.jetbrains.python.PythonMinicondaLocator#getMinicondaInstallerFolder()
* */
static final String minicondaInstallerFolderName = "minicondaInstaller"
}
......@@ -201,4 +201,12 @@ abstract class PyCharmPropertiesBase extends ProductProperties {
folderWithUnzipContent.deleteDir()
}
static void downloadMiniconda(BuildContext context, String targetDirectory, String osName) {
final String installer = "Miniconda3-latest-$osName-x86_64.${if (osName == "Windows") "exe" else "sh"}"
context.ant.mkdir(dir: "$targetDirectory/$PyCharmBuildOptions.minicondaInstallerFolderName")
context.ant.get(src: "https://repo.continuum.io/miniconda/$installer",
dest: "$targetDirectory/$PyCharmBuildOptions.minicondaInstallerFolderName")
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.python.community.impl" />
<orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
<orderEntry type="module" module-name="intellij.platform.ide" />
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
</component>
</module>
\ No newline at end of file
<idea-plugin>
<extensions defaultExtensionNs="Pythonid">
<pythonSdkTypeComparator implementation="com.jetbrains.python.sdk.PyPreferringCondaSdkTypeComparator"/>
</extensions>
<actions>
<action id="InstallMiniconda" class="com.jetbrains.python.conda.InstallCondaAction"
text="Install Miniconda 3...">
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions>
</idea-plugin>
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude" allow-bundled-update="true">
<name>Anaconda Support</name>
<id>org.jetbrains.plugins.anaconda</id>
<vendor>JetBrains</vendor>
<description>Facilitates using Сonda management system in PyCharm</description>
<depends>com.intellij.modules.python</depends>
<change-notes>
<![CDATA[
<ul>
<li>Establishes Conda as the default virtual environment for the newly created project in PyCharm.</li>
</ul>
]]>
</change-notes>
<xi:include href="/META-INF/conda.xml" xpointer="xpointer(/idea-plugin/*)"/>
</idea-plugin>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 138 138.7" style="enable-background:new 0 0 138 138.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#43B02A;}
</style>
<g>
<path class="st0" d="M27.1,104.4V104c0-3.9,0.4-7.6,1-11.3v-0.4l-0.4-0.2c-3.5-1.4-6.8-3.1-10.2-4.9L17.2,87L17,87.4
c-2.1,4.3-3.9,9-5.5,13.7l-0.2,0.4l0.4,0.2c4.9,1.4,9.8,2.3,14.9,2.7L27.1,104.4z"/>
<path class="st0" d="M37.3,37.6C37.3,37.4,37.3,37.4,37.3,37.6c-2.9,0-5.7,0.2-8.6,0.4c0.4,2.9,0.8,5.7,1.6,8.6
C32.2,43.3,34.6,40.1,37.3,37.6z"/>
<path class="st0" d="M27.1,107.9v-0.4h-0.4c-4.1-0.4-8.4-1.2-12.7-2.1l-1.2-0.2l0.6,1c3.7,5.7,8.4,10.7,13.9,15.2l0.8,0.8l0-1.4
C27.5,116.3,27.1,112,27.1,107.9z"/>
<path class="st0" d="M47.6,6.9c-5.1,1.8-10,4.1-14.5,7c3.3,0.6,6.6,1.4,10,2.3C44.5,13.2,45.9,10,47.6,6.9z"/>
<path class="st0" d="M68.9,3.4c-2.5,0-5.1,0.2-7.4,0.4c3.5,2.3,6.8,5.1,10,8l2.5,2.3l-2.5,2.5c-2.1,2-4.3,4.3-6.3,6.6v0.2
c0,0-0.4,0.4-1,1.2c1.6-0.2,3.1-0.2,4.7-0.2c24.8,0,44.9,20.1,44.9,44.9s-20.1,44.9-44.9,44.9c-8.6,0-16.6-2.3-23.5-6.6
c-3.3,0.4-6.8,0.6-10.2,0.6c-1.6,0-3.1,0-4.7-0.2c0.2,5.1,0.6,10.4,1.4,15.8c10.6,7.2,23.3,11.3,36.9,11.3
c36.5,0,66.1-29.5,66.1-66.1C135,32.9,105.5,3.4,68.9,3.4z"/>
<path class="st0" d="M60.2,19c1.4-1.6,2.7-3.1,4.1-4.5c-3.1-2.5-6.4-5.1-9.8-7.2c-2.1,3.5-3.9,7.2-5.5,10.9
c2.9,1.2,5.9,2.3,8.8,3.7C59,20.4,60,19.2,60.2,19z"/>
<path class="st0" d="M15.4,56.7l0.2,0.4l0.4-0.2c3.1-2.1,6.3-4.3,9.6-6.1l0.4-0.2v-0.4c-1-3.7-1.8-7.6-2.1-11.5v-0.4h-0.4
c-4.9,1-9.8,2.1-14.3,3.9l-0.4,0.2L9,42.9C10.3,47.6,12.7,52.3,15.4,56.7z"/>
<path class="st0" d="M14.6,63l-0.4,0.4c-3.5,2.9-6.8,6.1-10,9.6l-0.4,0.4l0.4,0.4c3.5,2.9,7,5.7,10.9,8.2l0.4,0.2l0.2-0.4
c1.8-3.1,3.7-6.1,5.9-9l0.2-0.4l-0.2-0.2c-2.3-2.7-4.7-5.5-6.8-8.6L14.6,63z"/>
<path class="st0" d="M40.6,104.6h1.2l-1-0.8c-3.5-2.9-6.6-6.3-9-10.2v-0.2L31,93.1v0.6c-0.4,3.3-0.8,6.8-0.8,10.4v0.4h0.4
c1.6,0,3.1,0.2,4.7,0.2C37.1,104.8,38.9,104.8,40.6,104.6z"/>
<path class="st0" d="M38.5,31.7c0.8-3.3,1.6-6.4,2.7-9.6c-4.1-1.2-8.4-2.1-12.7-2.7c-0.4,4.3-0.4,8.6-0.2,12.9
C31.6,31.9,34.9,31.7,38.5,31.7z"/>
<path class="st0" d="M44.7,31.5c2.9-1.8,6.1-3.3,9.2-4.5c-2.3-1.2-4.7-2.1-7-2.9C46.1,26.5,45.3,29,44.7,31.5z"/>
<path class="st0" d="M14,85.1l-0.4-0.2c-3.3-2.1-6.6-4.5-9.8-7l-1-0.8l0.2,1.2c0.8,6.3,2.5,12.5,5.3,18.4l0.4,1l0.4-1
c1.4-3.7,2.9-7.4,4.7-10.9L14,85.1z"/>
<path class="st0" d="M22.4,22.5c-3.9,3.9-7.4,8.4-10.2,13.1c3.3-1,6.8-1.8,10.4-2.5C22.4,29.6,22.4,26.1,22.4,22.5z"/>
<path class="st0" d="M24.4,68.9v-1.2c0.2-4.1,0.8-8,2-11.7l0.4-1.2l-1,0.6c-2.5,1.6-5.1,3.1-7.6,4.9l-0.4,0.2l0.6,0.4
c1.8,2.3,3.5,4.9,5.5,7L24.4,68.9z"/>
<path class="st0" d="M25,75.7l-0.2-1.2l-0.6,1c-1.8,2.5-3.5,5.3-5.1,8l-0.2,0.4l0.4,0.2c2.7,1.6,5.7,2.9,8.6,4.3l1,0.4l-0.4-1
C26.7,83.9,25.6,79.8,25,75.7z"/>
<path class="st0" d="M12.1,59.9l0.4-0.2l-0.2-0.4c-2.1-3.3-3.9-6.8-5.7-10.4l-0.4-1l-0.4,1.2c-2,6.1-2.9,12.1-3.1,18.6v1.2l0.8-0.8
C6,65,9,62.2,12.1,59.9z"/>
</g>
</svg>
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.conda
import com.intellij.notification.Notification
import com.intellij.notification.NotificationType
import com.intellij.notification.Notifications
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.DumbAware
import com.intellij.openapi.project.Project
import com.intellij.util.ExceptionUtil
import com.jetbrains.python.PyBundle
import com.jetbrains.python.actions.InstallCondaActionImpl
import icons.PythonIcons.Python.Anaconda
/**
* @author Aleksey.Rostovskiy
*/
class InstallCondaAction : AnAction(PyBundle.message("action.SetupMiniconda.actionNameWithDots"),
null,
Anaconda),
DumbAware {
private val LOG = Logger.getInstance(InstallCondaAction::class.java)
override fun actionPerformed(event: AnActionEvent) {
val project = event.project
val title = PyBundle.message("action.SetupMiniconda.actionName")
val dialog = InstallCondaActionDialog(project)
dialog.show()
if (!dialog.isOK) return
val path = dialog.getPathInstallation().also {
LOG.info("Path is specified to $it")
}
object : Task.Backgroundable(project, title) {
override fun run(indicator: ProgressIndicator) {
try {
val handler = InstallCondaActionImpl.installationHandler(path) { line ->
indicator.text2 = line
}
handler.runProcessWithProgressIndicator(indicator).also { LOG.info(it.stdout) }
when (handler.exitCode) {
0 -> reportSuccess(project, path)
137 -> reportFailure(project, PyBundle.message("action.SetupMiniconda.installCanceled"))
else -> reportFailure(project)
}
}
catch (e: Exception) {
LOG.warn(e)
reportFailure(project, e)
}
}
}.queue()
}
private fun reportSuccess(project: Project?, path: String) {
Notifications.Bus.notify(
Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID,
PyBundle.message("action.SetupMiniconda.installSuccess"),
"Successfully installed to $path",
NotificationType.INFORMATION),
project)
}
private fun reportFailure(project: Project?, e: Exception) {
return reportFailure(project, ExceptionUtil.getNonEmptyMessage(e, "Internal error"))
}
private fun reportFailure(project: Project?, message: String = "Internal error") {
Notifications.Bus.notify(
Notification(Notifications.SYSTEM_MESSAGES_GROUP_ID,
PyBundle.message("action.SetupMiniconda.installFailed"),
message,
NotificationType.ERROR),
project)
}
}
\ No newline at end of file
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.conda
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
import com.intellij.openapi.fileChooser.ex.FileChooserDialogImpl
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.ui.layout.*
import com.jetbrains.python.PyBundle
import com.jetbrains.python.actions.InstallCondaActionImpl
import javax.swing.JTextField
/**
* @author Aleksey.Rostovskiy
*/
class InstallCondaActionDialog(private val project: Project?) : DialogWrapper(project) {
private val installationPath = JTextField(InstallCondaActionImpl.defaultDirectoryFile.absolutePath)
private val fileChooser = TextFieldWithBrowseButton(installationPath) {
val virtualFile = LocalFileSystem.getInstance().findFileByPath(getPathInstallation())
val path = FileChooserDialogImpl(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project)
.choose(project, virtualFile)
.firstOrNull()
?.path
installationPath.text = path ?: InstallCondaActionImpl.defaultDirectoryFile.absolutePath
}
init {
title = PyBundle.message("action.SetupMiniconda.actionName")
init()
}
override fun doOKAction() {
val errorMessage = InstallCondaActionImpl.checkPath(getPathInstallation())
if (errorMessage != null) {
Messages.showErrorDialog(project,
errorMessage,
PyBundle.message("action.SetupMiniconda.installFailed"))
}
else {
super.doOKAction()
}
}
override fun createCenterPanel() =
panel {
row {
label(PyBundle.message("action.SetupMiniconda.specifyPath"))
}
row {
fileChooser()
}
}
fun getPathInstallation(): String = installationPath.text
}
\ No newline at end of file
package com.jetbrains.python.sdk
class PyPreferringCondaSdkTypeComparator: PySdkTypeComparator {
override fun compare(type1: PySdkTypeComparator.PySdkType, type2: PySdkTypeComparator.PySdkType): Int {
return when {
type1 == PySdkTypeComparator.PySdkType.CondaEnv -> -1
type2 == PySdkTypeComparator.PySdkType.CondaEnv -> 1
else -> 0
}
}
}
\ No newline at end of file
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.ide.customize.AbstractCustomizeWizardStep;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.PathChooserDialog;
import com.intellij.openapi.fileChooser.impl.FileChooserFactoryImpl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.local.CoreLocalFileSystem;
import com.intellij.openapi.vfs.local.CoreLocalVirtualFile;
import com.intellij.ui.components.labels.LinkLabel;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.ui.GridBag;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.actions.InstallCondaActionImpl;
import kotlin.Unit;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.concurrent.ExecutorService;
import static javax.swing.SwingUtilities.invokeLater;
/**
* @author Aleksey.Rostovskiy
*/
public class PyCharmCustomizeCondaSetupStep extends AbstractCustomizeWizardStep {
private final ExecutorService executorService = AppExecutorUtil.createBoundedApplicationPoolExecutor(
"PyCharmCustomizeCondaSetupStep Pool", 1);
private static final Logger LOG = Logger.getInstance(PyCharmCustomizeCondaSetupStep.class);
private final ProgressIndicator myProgressIndicator = new ProgressIndicatorBase();
private final JButton myInstallButton;
private final TextFieldWithBrowseButton mySetupCondaFileChooser;
private final JProgressBar myProgressBar;
private final JPanel myProgressPanel;
private final LinkLabel myCancelLink;
private VirtualFile myLastSelection;
public PyCharmCustomizeCondaSetupStep() {
setLayout(new BorderLayout());
myProgressBar = new JProgressBar(0, 100);
myProgressBar.setStringPainted(true);
myProgressBar.setIndeterminate(true);
myCancelLink = new LinkLabel("Cancel", null);
myCancelLink.setVisible(false);
myCancelLink.setListener((aSource, aLinkData) -> {
myProgressIndicator.cancel();
showErrorDialog(PyBundle.message("action.SetupMiniconda.installCanceled"), false);
unlockElementsAfterInstall();
}, null);
final JPanel linkWrapper = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
linkWrapper.add(myCancelLink);
myProgressPanel = new JPanel(new VerticalFlowLayout(true, false));
myProgressPanel.add(myProgressBar);
myProgressPanel.add(linkWrapper);
myProgressPanel.setEnabled(true);
myProgressPanel.setVisible(false);
final File installationPath = InstallCondaActionImpl.getDefaultDirectoryFile();
myLastSelection = new CoreLocalFileSystem().findFileByIoFile(installationPath);
myInstallButton = new JButton(PyBundle.message("action.SetupMiniconda.actionName"));
myInstallButton.addActionListener(e -> installButtonActionListener());
mySetupCondaFileChooser = new TextFieldWithBrowseButton();
mySetupCondaFileChooser.setText(installationPath.getAbsolutePath());
mySetupCondaFileChooser.addActionListener(e -> setupFileChooserActionListener());
JPanel controls = new JPanel(new GridBagLayout());
controls.setOpaque(false);
GridBag gbc = new GridBag()
.setDefaultAnchor(GridBagConstraints.WEST)
.setDefaultFill(GridBagConstraints.BOTH)
.setDefaultWeightX(1);
gbc.insets.top = UIUtil.PANEL_REGULAR_INSETS.top;
gbc.insets.left = UIUtil.PANEL_REGULAR_INSETS.left;
controls.add(myInstallButton, gbc.anchor(GridBagConstraints.NORTHWEST));
gbc.nextLine();
JLabel specifyPath = new JLabel(wrapInHtml(PyBundle.message("action.SetupMiniconda.specifyPath") + "<br>"));
controls.add(specifyPath, gbc.nextLine());
controls.add(mySetupCondaFileChooser, gbc.nextLine());
JLabel description = new JLabel(wrapInHtml(PyBundle.message("action.SetupMiniconda.description") + "<br>"));
controls.add(description, gbc.nextLine());
controls.add(myProgressPanel, gbc.nextLine());
JPanel content = new JPanel(createSmallBorderLayout());
content.add(controls, BorderLayout.NORTH);
add(content, BorderLayout.CENTER);
}
// TODO button `Start using PyCharm` can be blocked this way, but it's better not
//@Override
//public boolean beforeOkAction() {
// return !myProgressPanel.isVisible();
//}
private static void showErrorDialog(@NotNull String message, boolean log) {
if (log) LOG.error(message);
invokeLater(() -> Messages.showErrorDialog(message, PyBundle.message("action.SetupMiniconda.installFailed")));
}
private void lockElementsOnInstall() {
myInstallButton.setEnabled(false);
mySetupCondaFileChooser.setEnabled(false);
myProgressBar.setString("Installing...");
myProgressPanel.setVisible(true);
myCancelLink.setVisible(true);
}
private void unlockElementsAfterInstall() {
mySetupCondaFileChooser.setEnabled(true);
myInstallButton.setEnabled(true);
myProgressPanel.setVisible(false);
myCancelLink.setVisible(false);
}
private void installButtonActionListener() {
String path = InstallCondaActionImpl.beatifyPath(mySetupCondaFileChooser.getText());
String errorMessage = InstallCondaActionImpl.checkPath(path);
if (errorMessage != null) {
showErrorDialog(errorMessage, false);
return;
}
lockElementsOnInstall();
final ProcessOutput[] processOutput = new ProcessOutput[1];
executorService.submit(() -> {
try {
CapturingProcessHandler handler = InstallCondaActionImpl.installationHandler(path, (line) -> {
myProgressBar.setString(line);
return Unit.INSTANCE;
});
processOutput[0] = handler.runProcessWithProgressIndicator(myProgressIndicator);
LOG.info(processOutput[0].getStdout());
int exitCode = processOutput[0].getExitCode();
if (exitCode == 0) {
myInstallButton.setText("Installed");
}
else {
showErrorDialog(processOutput[0].getStderr(), true);
unlockElementsAfterInstall();
}
}
catch (Exception e) {
showErrorDialog(processOutput[0].getStderr(), true);
unlockElementsAfterInstall();
}
finally {
myProgressPanel.setVisible(false);
}
});
}
private void setupFileChooserActionListener() {
FileChooserDescriptor chooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
chooserDescriptor.setHideIgnored(false);
chooserDescriptor.withFileFilter(file -> file.isDirectory());
Ref<VirtualFile> fileRef = Ref.create();
PathChooserDialog chooser = FileChooserFactoryImpl.createNativePathChooserIfEnabled(chooserDescriptor, null, this);
if (chooser == null) {
File lastSelectedFile = myLastSelection == null ? null : VfsUtilCore.virtualToIoFile(myLastSelection);
JFileChooser fc = new JFileChooser(lastSelectedFile == null ? null : lastSelectedFile.getParentFile());
fc.setSelectedFile(lastSelectedFile);
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fc.setFileHidingEnabled(SystemInfo.isWindows || SystemInfo.isMac);
int returnVal = fc.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
if (file != null) {
fileRef.set(new CoreLocalVirtualFile(new CoreLocalFileSystem(), file));
mySetupCondaFileChooser.setText(file.getAbsolutePath());
}
}
}
else {
chooser.choose(myLastSelection, files -> fileRef.set(files.get(0)));
}
if (!fileRef.isNull()) {
File file = VfsUtilCore.virtualToIoFile(fileRef.get());
myLastSelection = fileRef.get();
mySetupCondaFileChooser.setText(file.getAbsolutePath());
}
}
@Override
public String getTitle() {
return "Featured tools";
}
@Override
protected String getHTMLHeader() {
return wrapInHtml("<h2>Miniconda</h2>");
}
@Override
protected String getHTMLFooter() {
return "Miniconda can be installed later via Tools | " + PyBundle.message("action.SetupMiniconda.actionNameWithDots");
}
private static String wrapInHtml(@NotNull String text) {
return "<html><body>" + text + "</body></html>";
}
}
......@@ -31,5 +31,9 @@ public class PyCharmCustomizeIDEWizardStepsProvider implements CustomizeIDEWizar
}
steps.add(new CustomizeFeaturedPluginsStepPanel(groups));
if (PythonMinicondaLocator.isInstallerExists()) {
steps.add(new PyCharmCustomizeCondaSetupStep());
}
}
}
......@@ -24,6 +24,7 @@
<orderEntry type="module" module-name="intellij.properties" />
<orderEntry type="module" module-name="intellij.markdown" />
<orderEntry type="module" module-name="intellij.configurationScript" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.python.conda" />
<orderEntry type="module" module-name="intellij.statsCollector" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.sh" />
</component>
......
......@@ -7,6 +7,8 @@ import com.intellij.openapi.ui.ValidationInfo
import com.intellij.ui.SimpleListCellRenderer
import com.intellij.util.ui.FormBuilder
import com.jetbrains.python.sdk.PySdkSettings
import com.jetbrains.python.sdk.PySdkTypeComparator
import com.jetbrains.python.sdk.PySdkTypeComparator.Companion.sortBySdkTypes
import com.jetbrains.python.sdk.add.PyAddNewCondaEnvPanel
import com.jetbrains.python.sdk.add.PyAddNewEnvPanel
import com.jetbrains.python.sdk.add.PyAddNewVirtualEnvPanel
......@@ -32,10 +34,7 @@ class PyAddNewEnvironmentPanel(existingSdks: List<Sdk>, newProjectPath: String?,
}
// TODO: Introduce a method in PyAddSdkProvider or in a Python SDK Provider
private val panels = listOf(PyAddNewVirtualEnvPanel(null, null, existingSdks, newProjectPath),
PyAddPipEnvPanel(null, null, existingSdks, newProjectPath),
PyAddNewCondaEnvPanel(null, null, existingSdks, newProjectPath))
private val panels = createPanels(existingSdks, newProjectPath)
var selectedPanel: PyAddNewEnvPanel = panels.find { it.envName == preferredType ?: PySdkSettings.instance.preferredEnvironmentType } ?: panels[0]
private val listeners = mutableListOf<Runnable>()
......@@ -88,4 +87,14 @@ class PyAddNewEnvironmentPanel(existingSdks: List<Sdk>, newProjectPath: String?,
}
listeners += listener
}
private fun createPanels(existingSdks: List<Sdk>, newProjectPath: String?): List<PyAddNewEnvPanel> {
return mutableListOf(
PySdkTypeComparator.PySdkType.VirtualEnv to PyAddNewVirtualEnvPanel(null, null, existingSdks, newProjectPath),
PySdkTypeComparator.PySdkType.PipEnv to PyAddPipEnvPanel(null, null, existingSdks, newProjectPath),
PySdkTypeComparator.PySdkType.CondaEnv to PyAddNewCondaEnvPanel(null, null, existingSdks, newProjectPath)
)
.sortBySdkTypes { it.first }
.map { it.second }
}
}
......@@ -705,6 +705,7 @@
<extensionPoint qualifiedName="Pythonid.debugSessionFactory" interface="com.jetbrains.python.debugger.PyDebugSessionFactory"/>
<extensionPoint qualifiedName="Pythonid.customPackageIdentifier" interface="com.jetbrains.python.psi.PyCustomPackageIdentifier"/>
<extensionPoint qualifiedName="Pythonid.pythonSdkComparator" interface="com.jetbrains.python.sdk.PySdkComparator" />
<extensionPoint qualifiedName="Pythonid.pythonSdkTypeComparator" interface="com.jetbrains.python.sdk.PySdkTypeComparator"/>
<extensionPoint qualifiedName="Pythonid.pyPregeneratedSkeletonsProvider"
interface="com.jetbrains.python.sdk.skeletons.PyPregeneratedSkeletonsProvider"/>
<extensionPoint qualifiedName="Pythonid.pyAddSdkProvider" interface="com.jetbrains.python.sdk.add.PyAddSdkProvider"/>
......
......@@ -1100,3 +1100,18 @@ smartKeys.insert.type.placeholder.in.docstring.stub=Insert type placeholders in
show.expression.type.no.expression.found=No expression found
runcfg.labels.module=Module:
remote.interpreter.wsl.default.interpreter.path=/usr/bin/python
action.SetupMiniconda.actionName=Install Miniconda 3
action.SetupMiniconda.actionNameWithDots=Install Miniconda 3...
action.SetupMiniconda.specifyPath=Installation path for Miniconda 3:
action.SetupMiniconda.description=\
Conda is an open source package management system and environment management system.<br>\
It helps isolate environments for your projects, manage both the Python interpreter \
and the packages within these environments.
action.SetupMiniconda.installSuccess=Miniconda Installation Succeed
action.SetupMiniconda.installFailed=Miniconda Installation Failed
action.SetupMiniconda.installCanceled=Miniconda Installation has been canceled
action.SetupMiniconda.installerMissing=Miniconda installer doesn't exist, please check your installation
action.SetupMiniconda.installDirectoryIsNotEmpty=Installation folder {0} is not empty - please specify another location
action.SetupMiniconda.installDirectoryMissing=Installation folder can not be empty - please specify location
action.SetupMiniconda.canNotWriteToInstallationDirectory=Can not install to {0} folder (no permissions) - please specify another location
\ No newline at end of file
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python
import com.intellij.openapi.application.PathManager
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.SystemInfo
import java.io.File
/**
* @author Aleksey.Rostovskiy
*/
object PythonMinicondaLocator {
private val LOG = Logger.getInstance(PythonMinicondaLocator::class.java)
/**
* @return miniconda3 installer path in installation folder
*/
fun getMinicondaInstallerPath() = getMinicondaInstallerFile()?.absolutePath
/**
* @return exists miniconda3 installer in distribution or not
*/
@JvmStatic
fun isInstallerExists() = getMinicondaInstallerFile() != null
private fun getMinicondaInstallerFolder() = File(PathManager.getHomePath(), "minicondaInstaller")
private fun getMinicondaInstallerFile(): File? {
val osName = when {
SystemInfo.isWindows -> "Windows"
SystemInfo.isLinux -> "Linux"
SystemInfo.isMac -> "MacOSX"
else -> {
LOG.error("${SystemInfo.OS_NAME} isn't expected as an operation system")
throw IllegalArgumentException("Wrong OS: ${SystemInfo.OS_NAME}")
}
}
val installer = File(getMinicondaInstallerFolder(),
"Miniconda3-latest-$osName-x86_64.${if (SystemInfo.isWindows) "exe" else "sh"}")
return if (installer.exists()) installer else null
}
}
\ No newline at end of file
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.actions
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessOutputTypes
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.text.StringUtil
import com.intellij.util.SystemProperties
import com.jetbrains.python.PyBundle
import com.jetbrains.python.PythonMinicondaLocator
import java.io.File
/**
* @author Aleksey.Rostovskiy
*/
object InstallCondaActionImpl {
private val LOG = Logger.getInstance(InstallCondaActionImpl::class.java)
/**
* @return `miniconda3` folder at home directory
*/
@JvmStatic
val defaultDirectoryFile: File by lazy {
File(SystemProperties.getUserHome(), "miniconda3")
}
/**
* [checkPath] should be called before running this process
* @param path installation path
* @param indicationFunction function to do when lines from stdout/stderr will be coming
* @return CapturingProcessHandler for installation
*/
@JvmStatic
fun installationHandler(path: String,
indicationFunction: (String) -> Unit): CapturingProcessHandler {
val handler = CapturingProcessHandler(getCommandLine(path))
handler.addProcessListener(object : ProcessAdapter() {
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
if (outputType === ProcessOutputTypes.STDOUT || outputType === ProcessOutputTypes.STDERR) {
for (line in StringUtil.splitByLines(event.text)) {
indicationFunction(line)
}
}
}
})
return handler
}
/**
* Supported couple cases for Unix-based OS:
* * "folder" -> "$HOME/folder"
* * "~/folder" -> $HOME/folder"
* @return transformed folder
*/
@JvmStatic
fun beatifyPath(path: String): String {
if (!SystemInfo.isWindows && !path.startsWith("/") && path.isNotBlank()) {
val home = SystemProperties.getUserHome()
return if (path.startsWith("~")) {
home + path.substring(1)
}
else {
File(home, path).absolutePath
}
}
return path
}
private fun File.checkCondaWrite(): Boolean {
return if (exists()) canWrite() else parentFile.checkCondaWrite()
}
/**
* @return GeneralCommandLine with arguments for current OS
* @throws IllegalArgumentException if OS is unknown
* */
private fun getCommandLine(path: String): GeneralCommandLine {
val installerPath = PythonMinicondaLocator.getMinicondaInstallerPath()!!
return when {
SystemInfo.isWindows -> GeneralCommandLine(installerPath,
"/InstallationType=JustMe",
"/AddToPath=0",
"/RegisterPython=0",
"/S",
"/D=$path")
SystemInfo.isLinux || SystemInfo.isMac -> GeneralCommandLine("bash",
installerPath,
"-b",
"-p", path)
else -> {
LOG.error("${SystemInfo.OS_NAME} isn't expected as a operation system")
throw IllegalArgumentException("OS ${SystemInfo.OS_NAME} isn't supported for Miniconda installation")
}
}
}
/**
* @param path checks on
* * being blank
* * already exists
* * writable permissions
* * already existed
* @return String with error message or null if none
*/
@JvmStatic
fun checkPath(path: String): String? {
if (path.isBlank())
return PyBundle.message("action.SetupMiniconda.installDirectoryMissing")
val pathFile = File(path)
if (pathFile.exists())
return PyBundle.message("action.SetupMiniconda.installDirectoryIsNotEmpty", path)
if (!pathFile.checkCondaWrite())
return PyBundle.message("action.SetupMiniconda.canNotWriteToInstallationDirectory", path)
if (!PythonMinicondaLocator.isInstallerExists())
return PyBundle.message("action.SetupMiniconda.installerMissing")
return null
}
}
\ No newline at end of file
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.sdk
import com.intellij.openapi.extensions.ExtensionPointName
import org.jetbrains.annotations.ApiStatus
import java.util.*
import kotlin.Comparator
@ApiStatus.Experimental
interface PySdkTypeComparator : Comparator<PySdkTypeComparator.PySdkType> {
override fun compare(type1: PySdkType, type2: PySdkType): Int
enum class PySdkType {
VirtualEnv, CondaEnv, PipEnv, SystemWide, Other
}
companion object {
private val EP_NAME = ExtensionPointName.create<PySdkTypeComparator>("Pythonid.pythonSdkTypeComparator")
private val COMPARATOR by lazy {
EP_NAME.extensions()
.map { it as Comparator<PySdkType> }
.reduce(Comparator { _, _ -> 0 }) { a, b -> a.thenComparing(b) }
}
fun <T> MutableList<T>.sortBySdkTypes(key: (T) -> PySdkType): MutableList<T> = also {
this.sortWith(Comparator { o1, o2 -> COMPARATOR.compare(key(o1), key(o2)) })
}
}
}
\ No newline at end of file
......@@ -34,11 +34,9 @@ import com.intellij.util.ExceptionUtil
import com.intellij.util.PlatformUtils
import com.intellij.util.ui.JBUI
import com.jetbrains.python.packaging.PyExecutionException
import com.jetbrains.python.sdk.PreferredSdkComparator
import com.jetbrains.python.sdk.PythonSdkType
import com.jetbrains.python.sdk.*
import com.jetbrains.python.sdk.PySdkTypeComparator.Companion.sortBySdkTypes
import com.jetbrains.python.sdk.add.PyAddSdkDialogFlowAction.*
import com.jetbrains.python.sdk.detectVirtualEnvs
import com.jetbrains.python.sdk.isAssociatedWithModule
import icons.PythonIcons
import java.awt.CardLayout
import java.awt.event.ActionEvent
......@@ -74,9 +72,7 @@ class PyAddSdkDialog private constructor(private val project: Project?,
val sdks = existingSdks
.filter { it.sdkType is PythonSdkType && !PythonSdkType.isInvalid(it) }
.sortedWith(PreferredSdkComparator())
val panels = arrayListOf<PyAddSdkView>(createVirtualEnvPanel(project, module, sdks),
createAnacondaPanel(project, module),
PyAddSystemWideInterpreterPanel(module, existingSdks))
val panels = createPanels(sdks).toMutableList()
val extendedPanels = PyAddSdkProvider.EP_NAME.extensions
.mapNotNull {
it.safeCreateView(project = project, module = module, existingSdks = existingSdks)
......@@ -87,6 +83,16 @@ class PyAddSdkDialog private constructor(private val project: Project?,
return mainPanel
}
private fun createPanels(sdks: List<Sdk>): List<PyAddSdkView> {
return mutableListOf(
PySdkTypeComparator.PySdkType.VirtualEnv to createVirtualEnvPanel(project, module, sdks),
PySdkTypeComparator.PySdkType.CondaEnv to createAnacondaPanel(project, module),
PySdkTypeComparator.PySdkType.SystemWide to PyAddSystemWideInterpreterPanel(module, existingSdks)
)
.sortBySdkTypes { it.first }
.map { it.second }
}
private fun <T> T.registerIfDisposable(): T = apply { (this as? Disposable)?.let { Disposer.register(disposable, it) } }
private var navigationPanelCardLayout: CardLayout? = null
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment