mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Ability to install python from PyCharm on macOS (PY-51686)
GitOrigin-RevId: 53ba94d3de3844fb5b58822576e4a54fe060d53b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
637aea0ae3
commit
aa5acb75c9
@@ -432,8 +432,10 @@ python.sdk.can.t.obtain.python.version=Can't obtain python version
|
||||
python.sdk.empty.version.string=Python interpreter returned the empty output as a version string
|
||||
python.sdk.non.zero.exit.code=Python interpreter process exited with a non-zero exit code {0}
|
||||
python.sdk.executable.not.found.header=Python executable is not found. Choose one of the following options:
|
||||
python.sdk.executable.not.found.option.specify.path=Click {0} to specify a path to python.exe in your file system
|
||||
python.sdk.executable.not.found.option.specify.path=Click {0} to specify a path to {1} in your file system
|
||||
python.sdk.executable.not.found.option.download.and.install=Click {0} to download and install Python from python.org ({1})
|
||||
python.sdk.executable.not.found.option.install.with.cldt=Click {0} to install Python with the Command-Line Developer Tools: {1}
|
||||
python.sdk.executable.not.found.option.install.or.brew=Install Python manually from python.org or via Homebrew
|
||||
python.sdk.admin.permissions.needed.consider.creating.venv=<html><strong>Note:</strong> You'll need admin permissions to install packages for this interpreter. Consider creating a per-project virtual environment instead.</html>
|
||||
python.sdk.try.to.install.python.manually=Try to install Python from https://www.python.org manually.
|
||||
python.sdk.consider.installing.python.manually=Consider installing Python from https://www.python.org manually.
|
||||
@@ -449,6 +451,13 @@ python.sdk.action.add.new.interpreter.text=Add New Interpreter
|
||||
python.sdk.action.add.local.interpreter.text=Add Local Interpreter...
|
||||
python.sdk.action.add.interpreter.based.on.target.text=On {0}...
|
||||
|
||||
python.cldt.checking=Checking for Command-Line Developer Tools installation
|
||||
python.cldt.required=Selected python requires Command-Line Developer Tools: {0}
|
||||
python.cldt.installing.suggestion=Install Python with the Command-Line Developer Tools
|
||||
python.cldt.installing.title=Waiting for Python Installation and Interpreter Setup
|
||||
python.cldt.installing.indicator=macOS is installing Command-Line Developer Tools
|
||||
python.cldt.installing.skip=Skip this step to configure an interpreter later
|
||||
|
||||
# SDK / skeletons
|
||||
sdk.some.skeletons.failed=Some skeletons failed to generate
|
||||
sdk.gen.cleaning.up=Cleaning up skeletons\u2026
|
||||
|
||||
@@ -7,8 +7,11 @@ import com.google.common.io.Files
|
||||
import com.intellij.execution.ExecutionException
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.process.CapturingProcessHandler
|
||||
import com.intellij.execution.process.OSProcessUtil
|
||||
import com.intellij.execution.process.ProcessOutput
|
||||
import com.intellij.execution.util.ExecUtil.execAndGetOutput
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
@@ -38,9 +41,11 @@ import com.jetbrains.python.sdk.PySdkToInstallCollector.Companion.InstallationRe
|
||||
import com.jetbrains.python.sdk.PySdkToInstallCollector.Companion.LookupResult
|
||||
import com.jetbrains.python.sdk.PySdkToInstallCollector.Companion.logSdkDownload
|
||||
import com.jetbrains.python.sdk.PySdkToInstallCollector.Companion.logSdkInstallation
|
||||
import com.jetbrains.python.sdk.flavors.MacPythonSdkFlavor
|
||||
import org.jetbrains.annotations.CalledInAny
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
private val LOGGER = Logger.getInstance(PySdkToInstall::class.java)
|
||||
@@ -48,6 +53,7 @@ private val LOGGER = Logger.getInstance(PySdkToInstall::class.java)
|
||||
@CalledInAny
|
||||
internal fun getSdksToInstall(): List<PySdkToInstall> {
|
||||
return if (SystemInfo.isWindows) listOf(getPy39ToInstallOnWindows(), getPy310ToInstallOnWindows())
|
||||
else if (SystemInfo.isMac) listOf(PySdkToInstallViaXCodeSelect())
|
||||
else emptyList()
|
||||
}
|
||||
|
||||
@@ -127,7 +133,7 @@ private class PySdkToInstallOnWindows(name: String,
|
||||
return HtmlBuilder()
|
||||
.append(PyBundle.message("python.sdk.executable.not.found.header"))
|
||||
.append(tag("ul").children(
|
||||
tag("li").children(raw(PyBundle.message("python.sdk.executable.not.found.option.specify.path", text("...").bold()))),
|
||||
tag("li").children(raw(PyBundle.message("python.sdk.executable.not.found.option.specify.path", text("...").bold(), "python.exe"))),
|
||||
tag("li").children(raw(PyBundle.message("python.sdk.executable.not.found.option.download.and.install",
|
||||
text(defaultButtonName).bold(), fileSize)))
|
||||
)).toString()
|
||||
@@ -331,3 +337,87 @@ private class PySdkToInstallOnWindows(name: String,
|
||||
private class PyInstallationException(val commandLine: GeneralCommandLine, val output: ProcessOutput) : Exception()
|
||||
private class PyInstallationExecutionException(val commandLine: GeneralCommandLine, override val cause: ExecutionException) : Exception()
|
||||
}
|
||||
|
||||
private class PySdkToInstallViaXCodeSelect : PySdkToInstall("Python", "") {
|
||||
|
||||
override fun renderInList(renderer: PySdkListCellRenderer) {
|
||||
renderer.append(name)
|
||||
renderer.append(" ")
|
||||
renderer.append(PyBundle.message("python.cldt.installing.suggestion"), SimpleTextAttributes.GRAYED_SMALL_ATTRIBUTES)
|
||||
}
|
||||
|
||||
@NlsContexts.DialogMessage
|
||||
override fun getInstallationWarning(defaultButtonName: String): String {
|
||||
val commandChunk = text(MacPythonSdkFlavor.getXCodeSelectInstallCommand().commandLineString)
|
||||
|
||||
return HtmlBuilder()
|
||||
.append(PyBundle.message("python.sdk.executable.not.found.header"))
|
||||
.append(tag("ul").children(
|
||||
tag("li").children(raw(PyBundle.message("python.sdk.executable.not.found.option.specify.path", text("...").bold(), "python"))),
|
||||
tag("li").children(raw(
|
||||
PyBundle.message("python.sdk.executable.not.found.option.install.with.cldt", text(defaultButtonName).bold(), commandChunk.code())
|
||||
)),
|
||||
tag("li").children(text(PyBundle.message("python.sdk.executable.not.found.option.install.or.brew")))
|
||||
)).toString()
|
||||
}
|
||||
|
||||
override fun install(module: Module?, systemWideSdksDetector: () -> List<PyDetectedSdk>): PyDetectedSdk? {
|
||||
val project = module?.project
|
||||
return ProgressManager.getInstance().run(
|
||||
object : Task.WithResult<PyDetectedSdk?, Exception>(project, PyBundle.message("python.cldt.installing.title"), true) {
|
||||
override fun compute(indicator: ProgressIndicator): PyDetectedSdk? {
|
||||
@Suppress("DialogTitleCapitalization")
|
||||
indicator.text = PyBundle.message("python.cldt.installing.indicator")
|
||||
indicator.text2 = PyBundle.message("python.cldt.installing.skip")
|
||||
|
||||
runXCodeSelectInstall()
|
||||
|
||||
while (!MacPythonSdkFlavor.areCommandLineDeveloperToolsAvailable() && isInstallCommandLineDeveloperToolsAppRunning()) {
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(5))
|
||||
}
|
||||
|
||||
LOGGER.debug("Resetting system-wide sdks detectors")
|
||||
resetSystemWideSdksDetectors()
|
||||
|
||||
return systemWideSdksDetector()
|
||||
.also { sdks ->
|
||||
LOGGER.debug { sdks.joinToString(prefix = "Detected system-wide sdks: ") { it.homePath ?: it.name } }
|
||||
}
|
||||
.singleOrNull()
|
||||
}
|
||||
}
|
||||
.also {
|
||||
it.cancelText = IdeBundle.message("button.skip")
|
||||
it.cancelTooltipText = IdeBundle.message("button.skip")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun runXCodeSelectInstall() {
|
||||
val commandLine = MacPythonSdkFlavor.getXCodeSelectInstallCommand()
|
||||
try {
|
||||
execAndGetOutput(commandLine)
|
||||
.also {
|
||||
if (LOGGER.isDebugEnabled) {
|
||||
LOGGER.debug("Result of '${commandLine.commandLineString}':\n$it")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: ExecutionException) {
|
||||
LOGGER.warn("Exception during '${commandLine.commandLineString}'", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInstallCommandLineDeveloperToolsAppRunning(): Boolean {
|
||||
val appName = "Install Command Line Developer Tools.app"
|
||||
|
||||
return OSProcessUtil
|
||||
.getProcessList()
|
||||
.any { it.commandLine.contains(appName) }
|
||||
.also {
|
||||
if (LOGGER.isDebugEnabled) {
|
||||
LOGGER.debug("'$appName' is${if (it) "" else " not"} running")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.openapi.ui.ValidationInfo
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.UserDataHolder
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import com.jetbrains.python.PySdkBundle
|
||||
@@ -32,6 +33,7 @@ import com.jetbrains.python.newProject.steps.PyAddNewEnvironmentPanel
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.add.PyAddSdkDialogFlowAction.OK
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectVirtualEnvConfiguration
|
||||
import com.jetbrains.python.sdk.flavors.MacPythonSdkFlavor
|
||||
import icons.PythonIcons
|
||||
import java.awt.Component
|
||||
import java.io.File
|
||||
@@ -104,6 +106,9 @@ abstract class PyAddSdkPanel : JPanel(), PyAddSdkView {
|
||||
val message = sdk.getInstallationWarning(defaultButtonName)
|
||||
ValidationInfo(message).asWarning().withOKEnabled()
|
||||
}
|
||||
is PyDetectedSdk -> {
|
||||
if (SystemInfo.isMac) MacPythonSdkFlavor.checkDetectedPython(sdk) else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
// Copyright 2000-2021 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.flavors;
|
||||
|
||||
import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.process.ProcessOutput;
|
||||
import com.intellij.execution.util.ExecUtil;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.ui.ValidationInfo;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.openapi.util.text.HtmlBuilder;
|
||||
import com.intellij.openapi.util.text.HtmlChunk;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.sdk.PyDetectedSdk;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -14,8 +25,14 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.openapi.util.text.HtmlChunk.raw;
|
||||
import static com.intellij.openapi.util.text.HtmlChunk.text;
|
||||
|
||||
|
||||
public final class MacPythonSdkFlavor extends CPythonSdkFlavor {
|
||||
|
||||
private static final Logger LOGGER = Logger.getInstance(MacPythonSdkFlavor.class);
|
||||
|
||||
private MacPythonSdkFlavor() {
|
||||
}
|
||||
|
||||
@@ -32,7 +49,9 @@ public final class MacPythonSdkFlavor extends CPythonSdkFlavor {
|
||||
collectPythonInstallations("/System/Library/Frameworks/Python.framework/Versions", candidates);
|
||||
collectPythonInstallations("/usr/local/Cellar/python", candidates);
|
||||
UnixPythonSdkFlavor.collectUnixPythons("/usr/local/bin", candidates);
|
||||
UnixPythonSdkFlavor.collectUnixPythons("/usr/bin", candidates);
|
||||
if (areCommandLineDeveloperToolsAvailable()) {
|
||||
UnixPythonSdkFlavor.collectUnixPythons("/usr/bin", candidates);
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
@@ -56,4 +75,57 @@ public final class MacPythonSdkFlavor extends CPythonSdkFlavor {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static @NotNull GeneralCommandLine getXCodeSelectInstallCommand() {
|
||||
return new GeneralCommandLine("xcode-select", "--install");
|
||||
}
|
||||
|
||||
private static @NotNull GeneralCommandLine getXCodeSelectPathCommand() {
|
||||
return new GeneralCommandLine("xcode-select", "-p");
|
||||
}
|
||||
|
||||
public static boolean areCommandLineDeveloperToolsAvailable() {
|
||||
final GeneralCommandLine commandLine = getXCodeSelectPathCommand();
|
||||
|
||||
try {
|
||||
final ProcessOutput output = ExecUtil.execAndGetOutput(commandLine);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Result of '" + commandLine.getCommandLineString() + "':\n" + output);
|
||||
}
|
||||
|
||||
return output.getExitCode() != 2;
|
||||
}
|
||||
catch (ExecutionException e) {
|
||||
LOGGER.warn("Exception during '" + commandLine.getCommandLineString() + "'", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable ValidationInfo checkDetectedPython(@NotNull PyDetectedSdk sdk) {
|
||||
if (!"/usr/bin/python3".equals(sdk.getHomePath())) return null;
|
||||
|
||||
//noinspection DialogTitleCapitalization
|
||||
final String progressTitle = PyBundle.message("python.cldt.checking");
|
||||
|
||||
if (ProgressManager
|
||||
.getInstance()
|
||||
.runProcessWithProgressSynchronously(
|
||||
MacPythonSdkFlavor::areCommandLineDeveloperToolsAvailable,
|
||||
progressTitle,
|
||||
true,
|
||||
null
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final HtmlChunk commandChunk = text(getXCodeSelectInstallCommand().getCommandLineString());
|
||||
|
||||
final String message = new HtmlBuilder().append(
|
||||
raw(PyBundle.message("python.cldt.required", commandChunk.code()))
|
||||
).toString();
|
||||
|
||||
return new ValidationInfo(message).asWarning().withOKEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user