Support reading virtualenv variables on Windows

This enables on Windows having the correct env variables for Python processes started by PyCharm (PY-15085) and starts the terminal in a virtualenv aware mode (PY-10498)
This commit is contained in:
Dmitry Trofimov
2016-09-06 15:58:07 +02:00
parent aa6ae06c13
commit 5123deb520
3 changed files with 67 additions and 27 deletions

View File

@@ -177,46 +177,52 @@ public class EnvironmentUtil {
LOG.info("loading shell env: " + StringUtil.join(command, " "));
ProcessBuilder builder = new ProcessBuilder(command).redirectErrorStream(true);
builder.environment().put(DISABLE_OMZ_AUTO_UPDATE, "true");
Process process = builder.start();
StreamGobbler gobbler = new StreamGobbler(process.getInputStream());
int rv = waitAndTerminateAfter(process, SHELL_ENV_READING_TIMEOUT);
gobbler.stop();
String lines = FileUtil.loadFile(envFile);
if (rv != 0 || lines.isEmpty()) {
throw new Exception("rv:" + rv + " text:" + lines.length() + " out:" + StringUtil.trimEnd(gobbler.getText(), '\n'));
}
return parseEnv(lines);
return runProcessAndReadEnvs(command, envFile, "\0");
}
finally {
FileUtil.delete(envFile);
}
}
@NotNull
protected static Map<String, String> runProcessAndReadEnvs(@NotNull List<String> command, @NotNull File envFile, String lineSeparator) throws Exception {
ProcessBuilder builder = new ProcessBuilder(command).redirectErrorStream(true);
builder.environment().put(DISABLE_OMZ_AUTO_UPDATE, "true");
Process process = builder.start();
StreamGobbler gobbler = new StreamGobbler(process.getInputStream());
int rv = waitAndTerminateAfter(process, SHELL_ENV_READING_TIMEOUT);
gobbler.stop();
String lines = FileUtil.loadFile(envFile);
if (rv != 0 || lines.isEmpty()) {
throw new Exception("rv:" + rv + " text:" + lines.length() + " out:" + StringUtil.trimEnd(gobbler.getText(), '\n'));
}
return parseEnv(lines, lineSeparator);
}
protected List<String> getShellProcessCommand() throws Exception {
String shell = getShell();
if (shell == null || !new File(shell).canExecute()) {
throw new Exception("shell:" + shell);
}
return new ArrayList<String>(Arrays.asList(shell, "-l", "-i"));
}
@NotNull
@Nullable
protected String getShell() throws Exception {
String shell = System.getenv("SHELL");
if (shell == null || !new File(shell).canExecute()) {
throw new Exception("shell:" + shell);
}
return shell;
return System.getenv("SHELL");
}
}
private static Map<String, String> parseEnv(String text) throws Exception {
@NotNull
private static Map<String, String> parseEnv(String text, String lineSeparator) throws Exception {
Set<String> toIgnore = new HashSet<String>(Arrays.asList("_", "PWD", "SHLVL", DISABLE_OMZ_AUTO_UPDATE));
Map<String, String> env = System.getenv();
Map<String, String> newEnv = new HashMap<String, String>();
String[] lines = text.split("\0");
String[] lines = text.split(lineSeparator);
for (String line : lines) {
int pos = line.indexOf('=');
if (pos <= 0) {
@@ -318,7 +324,7 @@ public class EnvironmentUtil {
@TestOnly
static Map<String, String> testParser(@NotNull String lines) {
try {
return parseEnv(lines);
return parseEnv(lines, "\0");
}
catch (Exception e) {
throw new RuntimeException(e);

View File

@@ -36,7 +36,7 @@ class PyVirtualEnvTerminalCustomizer : LocalTerminalCustomizer() {
envs: MutableMap<String, String>): Array<out String> {
val sdk: Sdk? = findSdk(project)
if (sdk != null && PythonSdkType.isVirtualEnv(sdk) && SystemInfo.isUnix) {
if (sdk != null && PythonSdkType.isVirtualEnv(sdk)) {
// in case of virtualenv sdk on unix we activate virtualenv
val path = sdk.homePath

View File

@@ -15,6 +15,8 @@
*/
package com.jetbrains.python.run
import com.intellij.openapi.util.SystemInfo
import com.intellij.openapi.util.io.FileUtil
import com.intellij.util.EnvironmentUtil
import java.io.File
@@ -26,17 +28,49 @@ import java.io.File
class PyVirtualEnvReader(virtualEnvSdkPath: String) : EnvironmentUtil.ShellEnvReader() {
val activate = findActivateScript(virtualEnvSdkPath, shell)
override fun getShellProcessCommand(): MutableList<String>? {
override fun readShellEnv(): MutableMap<String, String> {
if (SystemInfo.isUnix) {
return super.readShellEnv()
}
else {
return readVirtualEnvOnWindows();
}
}
return if (activate != null) mutableListOf(shell, "--rcfile", activate, "-i")
private fun readVirtualEnvOnWindows(): MutableMap<String, String> {
val activateFile = FileUtil.createTempFile("pycharm-virualenv-activate.", ".bat", false)
val envFile = FileUtil.createTempFile("pycharm-virualenv-envs.", ".tmp", false)
try {
FileUtil.copy(File(activate), activateFile);
FileUtil.appendToFile(activateFile, "\n\nset")
val command = listOf<String>(activateFile.path, ">", envFile.absolutePath)
return runProcessAndReadEnvs(command, envFile, "\r\n")
}
finally {
FileUtil.delete(activateFile)
FileUtil.delete(envFile)
}
}
override fun getShellProcessCommand(): MutableList<String>? {
val shellPath = shell
if (shellPath == null || !File(shellPath).canExecute()) {
throw Exception("shell:" + shellPath)
}
return if (activate != null) mutableListOf(shellPath, "--rcfile", activate, "-i")
else super.getShellProcessCommand()
}
}
fun findActivateScript(path: String?, shellPath: String): String? {
val shellName = File(shellPath).name
val activate = if (shellName == "fish" || shellName == "csh") File(File(path).parentFile, "activate." + shellName)
fun findActivateScript(path: String?, shellPath: String?): String? {
val shellName = if (shellPath != null) File(shellPath).name else null
val activate = if (SystemInfo.isWindows) File(File(path).parentFile, "activate.bat")
else if (shellName == "fish" || shellName == "csh") File(File(path).parentFile, "activate." + shellName)
else File(File(path).parentFile, "activate")
return if (activate.exists()) activate.absolutePath else null