Support reading virtualenv variables on Windows
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 6 Sep 2016 13:58:07 +0000 (15:58 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 6 Sep 2016 13:58:07 +0000 (15:58 +0200)
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)

platform/util/src/com/intellij/util/EnvironmentUtil.java
python/python-terminal/src/com/jetbrains/python/sdk/PyVirtualEnvTerminalCustomizer.kt
python/src/com/jetbrains/python/run/PyVirtualEnvReader.kt

index 7f897b2a714d1ea60517e3fb05ea3312df4287c5..1d6428dadfd6a49fbfa808f6606063fc5d48a18f 100644 (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);
index 82cb5cfbaf1443a002c6c33a4b148029a87be124..806ded0c2563cc9136ea3bb453e02a4398e6039b 100644 (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
 
index 826278397ca517bf6557da1f3f043203fcbec0f1..ab18199e8e7db22dd094f4c52a65207a5d03e7cb 100644 (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 readShellEnv(): MutableMap<String, String> {
+    if (SystemInfo.isUnix) {
+      return super.readShellEnv()
+    }
+    else {
+      return readVirtualEnvOnWindows();
+    }
+  }
+
+  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(shell, "--rcfile", activate, "-i")
+    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