Merge remote-tracking branch 'origin/master' into pycharm/docker
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 29 Sep 2015 22:35:29 +0000 (00:35 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Tue, 29 Sep 2015 22:35:29 +0000 (00:35 +0200)
26 files changed:
platform/platform-impl/src/com/intellij/remote/CredentialsType.java
platform/platform-impl/src/com/intellij/remote/DockerCredentialsHolder.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/remote/DockerMachineCommandException.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/remote/DockerMachineException.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/remote/DockerSupport.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/remote/RemoteConnectionCredentialsWrapper.java
platform/platform-impl/src/com/intellij/remote/RemoteProcess.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/remote/RemoteProcessHandlerBase.java
platform/platform-impl/src/com/intellij/remote/RemoteSdkAdditionalData.java
platform/platform-impl/src/com/intellij/remote/RemoteSdkConnectionAcceptor.java
platform/platform-impl/src/com/intellij/remote/RemoteSdkCredentialsHolder.java
platform/platform-impl/src/com/intellij/remote/RemoteSdkException.java
platform/platform-impl/src/com/intellij/remote/RemoteSshProcess.java
python/helpers/generator3.py
python/ipnb/ipnb.iml
python/ipnb/lib/commons-io-1.4.jar [deleted file]
python/ipnb/lib/commons-io-2.3.jar [new file with mode: 0644]
python/src/META-INF/python-core.xml
python/src/com/jetbrains/python/debugger/PyDebugProcess.java
python/src/com/jetbrains/python/packaging/PyRemotePackageManagerImpl.java
python/src/com/jetbrains/python/remote/PyRemoteProcessConnectionData.java [new file with mode: 0644]
python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
python/src/com/jetbrains/python/run/PyRemoteProcessStarter.java
python/src/com/jetbrains/python/run/PyRemoteProcessStarterManager.java [new file with mode: 0644]
python/src/com/jetbrains/python/run/PyRemoteProcessStarterManagerUtil.java [new file with mode: 0644]
python/src/com/jetbrains/python/sdk/PythonSdkType.java

index 92d071c6c3fecf03947a3fde86f3875d9529b4f6..3c8aa66c9ccc17b1c015701e909a8902580b135a 100644 (file)
@@ -19,5 +19,5 @@ package com.intellij.remote;
  * @author traff
  */
 public enum CredentialsType {
-  VAGRANT, WEB_DEPLOYMENT, SSH_HOST
+  VAGRANT, WEB_DEPLOYMENT, SSH_HOST, DOCKER
 }
diff --git a/platform/platform-impl/src/com/intellij/remote/DockerCredentialsHolder.java b/platform/platform-impl/src/com/intellij/remote/DockerCredentialsHolder.java
new file mode 100644 (file)
index 0000000..2ea73e5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.intellij.remote;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jdom.Element;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class DockerCredentialsHolder {
+  public static final String DOCKER_MACHINE_NAME = "DOCKER_MACHINE_NAME";
+  public static final String DOCKER_IMAGE_NAME = "DOCKER_IMAGE_NAME";
+  public static final String DOCKER_CONTAINER_NAME = "DOCKER_CONTAINER_NAME";
+  public static final String DOCKER_REMOTE_PROJECT_PATH = "DOCKER_REMOTE_PROJECT_PATH";
+
+  private String myMachineName;
+
+  private String myImageName;
+
+  private String myContainerName;
+
+  private String myRemoteProjectPath;
+
+  public DockerCredentialsHolder() {
+  }
+
+  public DockerCredentialsHolder(String machineName,
+                                 String imageName,
+                                 String containerName,
+                                 String remoteProjectPath) {
+    myMachineName = machineName;
+    myImageName = imageName;
+    myContainerName = containerName;
+    myRemoteProjectPath = remoteProjectPath;
+  }
+
+  public String getMachineName() {
+    return myMachineName;
+  }
+
+  public String getImageName() {
+    return myImageName;
+  }
+
+  public String getContainerName() {
+    return myContainerName;
+  }
+
+  public String getRemoteProjectPath() {
+    return myRemoteProjectPath;
+  }
+
+  public void save(@NotNull Element element) {
+    if (StringUtil.isNotEmpty(myMachineName)) {
+      element.setAttribute(DOCKER_MACHINE_NAME, myMachineName);
+    }
+    element.setAttribute(DOCKER_IMAGE_NAME, myImageName);
+    if (StringUtil.isNotEmpty(myContainerName)) {
+      element.setAttribute(DOCKER_CONTAINER_NAME, myContainerName);
+    }
+    element.setAttribute(DOCKER_REMOTE_PROJECT_PATH, myRemoteProjectPath);
+  }
+
+  public void load(@NotNull Element element) {
+    myMachineName = element.getAttributeValue(DOCKER_MACHINE_NAME);
+    myImageName = element.getAttributeValue(DOCKER_IMAGE_NAME);
+    myContainerName = element.getAttributeValue(DOCKER_CONTAINER_NAME);
+    myRemoteProjectPath = element.getAttributeValue(DOCKER_REMOTE_PROJECT_PATH);
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/remote/DockerMachineCommandException.java b/platform/platform-impl/src/com/intellij/remote/DockerMachineCommandException.java
new file mode 100644 (file)
index 0000000..d6e7303
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.intellij.remote;
+
+import com.intellij.openapi.util.text.StringUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class DockerMachineCommandException extends RuntimeException {
+  public DockerMachineCommandException(int exitCode, @Nullable String stderr) {
+    super(buildErrorMessage(exitCode, stderr));
+  }
+
+  public DockerMachineCommandException(String message) {
+    super(message);
+  }
+
+  @NotNull
+  private static String buildErrorMessage(int exitCode, String stderr) {
+    StringBuilder sb = new StringBuilder()
+      .append("Docker Machine exited with error code ")
+      .append(exitCode);
+    if (StringUtil.isNotEmpty(stderr)) {
+      sb.append(": ").append(stderr);
+    }
+    return sb.toString();
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/remote/DockerMachineException.java b/platform/platform-impl/src/com/intellij/remote/DockerMachineException.java
new file mode 100644 (file)
index 0000000..296fecd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.intellij.remote;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class DockerMachineException extends RuntimeException {
+  public DockerMachineException(String message) {
+    super(message);
+  }
+
+  public DockerMachineException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public DockerMachineException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/platform/platform-impl/src/com/intellij/remote/DockerSupport.java b/platform/platform-impl/src/com/intellij/remote/DockerSupport.java
new file mode 100644 (file)
index 0000000..bf29448
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * 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.intellij.remote;
+
+import com.intellij.openapi.components.ServiceManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public abstract class DockerSupport {
+  @Nullable
+  public static DockerSupport getInstance() {
+    return ServiceManager.getService(DockerSupport.class);
+  }
+
+  public abstract boolean hasDockerMachine();
+
+  /**
+   * @deprecated should be moved to application settings
+   */
+  @Deprecated
+  @Nullable
+  public abstract String getDockerMachineExecutable();
+
+  /**
+   * @deprecated should be moved to application settings
+   */
+  @Deprecated
+  public abstract void setDockerMachineExecutable(String executable);
+
+  @NotNull
+  public abstract List<String> getVMs() throws DockerMachineException, DockerMachineCommandException;
+
+  @NotNull
+  public abstract String getStatus(@NotNull String machineName);
+
+  @NotNull
+  public abstract ConnectionInfo getConnectionInfo(@NotNull String machineName) throws DockerMachineException, DockerMachineCommandException;
+
+  @NotNull
+  public abstract ConnectionInfo getConnectionInfo();
+
+  public static class ConnectionInfo {
+    @NotNull private final String myApiUrl;
+    @Nullable private final String myCertificatesPath;
+
+    public ConnectionInfo(@NotNull String apiUrl, @Nullable String certificatesPath) {
+      myApiUrl = apiUrl;
+      myCertificatesPath = certificatesPath;
+    }
+
+    @NotNull
+    public String getApiUrl() {
+      return myApiUrl;
+    }
+
+    @Nullable
+    public String getCertificatesPath() {
+      return myCertificatesPath;
+    }
+  }
+}
index e110044eda78629ec3015ffeafc0cb96b4cae702..83fbfaa7397c08c354b3777b8b6f36c716412177 100644 (file)
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
 public class RemoteConnectionCredentialsWrapper {
   public static final String VAGRANT_PREFIX = "vagrant://";
   public static final String SFTP_DEPLOYMENT_PREFIX = "sftp://";
+  public static final String DOCKER_PREFIX = "docker://";
 
   /**
    * Connection types
@@ -36,6 +37,7 @@ public class RemoteConnectionCredentialsWrapper {
   public final Key<VagrantBasedCredentialsHolder> VAGRANT_BASED_CREDENTIALS = Key.create("VAGRANT_BASED_CREDENTIALS");
   public final Key<WebDeploymentCredentialsHolder> WEB_DEPLOYMENT_BASED_CREDENTIALS = Key.create("WEB_DEPLOYMENT_BASED_CREDENTIALS");
   public final Key<RemoteCredentialsHolder> PLAIN_SSH_CREDENTIALS = Key.create("PLAIN_SSH_CREDENTIALS");
+  public final Key<DockerCredentialsHolder> DOCKER_CREDENTIALS = Key.create("DOCKER_CREDENTIALS");
 
   private UserDataHolderBase myCredentialsTypeHolder = new UserDataHolderBase();
 
@@ -68,6 +70,10 @@ public class RemoteConnectionCredentialsWrapper {
     return myCredentialsTypeHolder.getUserData(WEB_DEPLOYMENT_BASED_CREDENTIALS);
   }
 
+  private DockerCredentialsHolder getDockerCredentials() {
+    return myCredentialsTypeHolder.getUserData(DOCKER_CREDENTIALS);
+  }
+
   private boolean isVagrantConnection() {
     return getVagrantCredentials() != null;
   }
@@ -80,6 +86,9 @@ public class RemoteConnectionCredentialsWrapper {
     return getWebDeploymentCredentials() != null;
   }
 
+  private boolean isDockerConnection() {
+    return getDockerCredentials() != null;
+  }
 
   public Object getConnectionKey() {
     if (isVagrantConnection()) {
@@ -112,6 +121,11 @@ public class RemoteConnectionCredentialsWrapper {
       public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
         cred.save(rootElement);
       }
+
+      @Override
+      public void docker(@NotNull DockerCredentialsHolder cred) {
+        cred.save(rootElement);
+      }
     });
   }
 
@@ -136,6 +150,11 @@ public class RemoteConnectionCredentialsWrapper {
       public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
         copy.setWebDeploymentCredentials(getWebDeploymentCredentials());
       }
+
+      @Override
+      public void docker(@NotNull DockerCredentialsHolder credentials) {
+        copy.setDockerDeploymentCredentials(getDockerCredentials());
+      }
     });
   }
 
@@ -159,6 +178,13 @@ public class RemoteConnectionCredentialsWrapper {
       public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
         result.set(constructSftpCredentialsFullPath(cred.getSshCredentials()));
       }
+
+      @Override
+      public void docker(@NotNull DockerCredentialsHolder cred) {
+        // TODO [Docker] review
+        String name = StringUtil.isNotEmpty(cred.getContainerName()) ? cred.getContainerName() : cred.getImageName();
+        result.set(DOCKER_PREFIX + name + "/");
+      }
     });
 
     return result.get();
@@ -185,6 +211,9 @@ public class RemoteConnectionCredentialsWrapper {
     else if (isWebDeploymentConnection()) {
       acceptor.deployment(getWebDeploymentCredentials());
     }
+    else if (isDockerConnection()) {
+      acceptor.docker(getDockerCredentials());
+    }
     else {
       throw unknownConnectionType();
     }
@@ -229,8 +258,21 @@ public class RemoteConnectionCredentialsWrapper {
       public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
         result.set("(" + constructSftpCredentialsFullPath(cred.getSshCredentials()) + interpreterPath + ")");
       }
+
+      @Override
+      public void docker(@NotNull DockerCredentialsHolder credentials) {
+        String containerName = StringUtil.isNotEmpty(credentials.getContainerName())
+                               ? credentials.getContainerName() + " " : "";
+        result.set("Docker " + containerName + "(" + credentials.getImageName() + ")");
+      }
     });
 
     return result.get();
   }
+
+
+  public void setDockerDeploymentCredentials(DockerCredentialsHolder credentials) {
+    myCredentialsTypeHolder = new UserDataHolderBase();
+    myCredentialsTypeHolder.putUserData(DOCKER_CREDENTIALS, credentials);
+  }
 }
diff --git a/platform/platform-impl/src/com/intellij/remote/RemoteProcess.java b/platform/platform-impl/src/com/intellij/remote/RemoteProcess.java
new file mode 100644 (file)
index 0000000..134867f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * 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.intellij.remote;
+
+import com.intellij.execution.process.SelfKiller;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public abstract class RemoteProcess extends Process implements SelfKiller {
+  public abstract boolean killProcessTree();
+
+  public abstract boolean isDisconnected();
+}
index face26cc3b06215e8ba61978c08ce1e6fa947278..1312819c3319438db81c79d39840754f3c22485c 100644 (file)
@@ -14,9 +14,19 @@ public interface RemoteProcessHandlerBase {
   @NotNull
   PathMapper getMappingSettings();
 
+  /**
+   * @deprecated use {@link #getRemoteSocket(int)}
+   */
+  @Deprecated
   Pair<String, Integer> obtainRemoteSocket() throws RemoteSdkException;
 
+  /**
+   * @deprecated use {@link #getRemoteSocket(int)}
+   */
+  @Deprecated
   void addRemoteForwarding(int remotePort, int localPort);
 
+  Pair<String, Integer> getRemoteSocket(int localPort) throws RemoteSdkException;
+
   List<PathMappingSettings.PathMapping> getFileMappings();
 }
index 2da812614b88ecbe5f7f76993dc5d2c2238e7016..839e736ab43210b8782c476947709104688d456f 100644 (file)
@@ -40,6 +40,8 @@ public interface RemoteSdkAdditionalData<T extends RemoteSdkCredentials>
 
   void setDeploymentConnectionType(@NotNull WebDeploymentCredentialsHolder credentials);
 
+  void setDockerConnectionType(@NotNull DockerCredentialsHolder credentials);
+
   CredentialsType getRemoteConnectionType();
 
   void switchOnConnectionType(@NotNull RemoteSdkConnectionAcceptor acceptor);
index d0d0b7a64253795a2632a7d5c07f1a95cb1c3bbe..46adb4fb199e4ae74b22967b46a0f8f1ca6fe408 100644 (file)
@@ -24,4 +24,5 @@ public interface RemoteSdkConnectionAcceptor {
   void ssh(@NotNull RemoteCredentialsHolder cred);
   void vagrant(@NotNull VagrantBasedCredentialsHolder cred);
   void deployment(@NotNull WebDeploymentCredentialsHolder cred);
+  void docker(@NotNull DockerCredentialsHolder credentials);
 }
index ae84919ac9ced74defe4cbbf62a7fd60fa3f7938..50e92faec72a066dc273641828df358d86390cd2 100644 (file)
@@ -157,7 +157,8 @@ public class RemoteSdkCredentialsHolder extends RemoteCredentialsHolder implemen
   public static boolean isRemoteSdk(@Nullable String path) {
     if (path != null) {
       return path.startsWith(SSH_PREFIX) || path.startsWith(RemoteConnectionCredentialsWrapper.VAGRANT_PREFIX) ||
-             path.startsWith(RemoteConnectionCredentialsWrapper.SFTP_DEPLOYMENT_PREFIX);
+             path.startsWith(RemoteConnectionCredentialsWrapper.SFTP_DEPLOYMENT_PREFIX) ||
+             path.startsWith(RemoteConnectionCredentialsWrapper.DOCKER_PREFIX);
     }
     else {
       return false;
index 17b1e46458c48678543e08abe300ffd026090bbc..4c7bcb411fff402b7b18a6fa25cf1274cc38b360 100644 (file)
@@ -58,6 +58,12 @@ public class RemoteSdkException extends ExecutionException {
   }
 
   public static RemoteSdkException cantObtainRemoteCredentials(Throwable e) {
-    return new RemoteSdkException("Cant obtain remote credentials", e);
+    // TODO needs review
+    if (e.getCause() instanceof RemoteCredentialException) {
+      return new RemoteSdkException("Cant obtain remote credentials", e);
+    }
+    else {
+      return new RemoteSdkException(e.getMessage(), e);
+    }
   }
 }
index b9090bcd3ee866b898fcf9ddc6a39c67d5c4d7fb..d8802935492220bf27e58c372b9af850c632a7d0 100644 (file)
@@ -5,7 +5,7 @@ import com.intellij.execution.process.SelfKiller;
 /**
  * @author traff
  */
-abstract public class RemoteSshProcess extends Process implements SelfKiller {
+abstract public class RemoteSshProcess extends RemoteProcess implements SelfKiller {
   /**
    * Makes host:localPort server which is available on local side available on remote side as localhost:remotePort.
    */
@@ -16,7 +16,24 @@ abstract public class RemoteSshProcess extends Process implements SelfKiller {
    */
   public abstract void addLocalTunnel(int localPort, String host, int remotePort) throws RemoteSdkException;
 
-  public abstract boolean hasPty();
+  /**
+   * @deprecated use {@link #killProcessTree()}
+   */
+  @Deprecated
+  protected abstract boolean hasPty();
+
+  /**
+   * @deprecated use {@link #killProcessTree()}
+   */
+  @Deprecated
+  protected abstract boolean sendCtrlC();
 
-  public abstract boolean sendCtrlC();
+  public boolean killProcessTree() {
+    if (hasPty()) {
+      return sendCtrlC();
+    }
+    else {
+      return false;
+    }
+  }
 }
index d09f6d918e186e562c7d9f829f59d1bc1d27c887..e916e1d695f3146ffec49c08c59ba0cff6648f2c 100644 (file)
@@ -1,6 +1,7 @@
 # encoding: utf-8
 import atexit
 import zipfile
+import shutil
 
 # TODO: Move all CLR-specific functions to clr_tools
 
@@ -153,7 +154,7 @@ def list_binaries(paths):
     return list(res.values())
 
 
-def list_sources(paths):
+def list_sources(paths, target_path):
     #noinspection PyBroadException
     try:
         for path in paths:
@@ -161,14 +162,29 @@ def list_sources(paths):
 
             path = os.path.normpath(path)
 
+            target_dir_path = ''
+            extra_info = ''
+
             if path.endswith('.egg') and os.path.isfile(path):
-                say("%s\t%s\t%d", path, path, os.path.getsize(path))
+                if target_path is not None:
+                    extra_info = '\t' + os.path.basename(path)
+                say("%s\t%s\t%d%s", path, path, os.path.getsize(path), extra_info)
+            else:
+                target_dir_path = compute_path_hash(path)
+                if target_path is not None:
+                    extra_info = '\t' + target_dir_path
 
             for root, files in walk_python_path(path):
                 for name in files:
                     if name.endswith('.py'):
                         file_path = os.path.join(root, name)
-                        say("%s\t%s\t%d", os.path.normpath(file_path), path, os.path.getsize(file_path))
+                        if target_path is not None:
+                            relpath = os.path.relpath(root, path)
+                            folder_path = os.path.join(target_path, target_dir_path, relpath)
+                            if not os.path.exists(folder_path):
+                                os.makedirs(folder_path)
+                            shutil.copyfile(file_path, os.path.join(folder_path, name))
+                        say("%s\t%s\t%d%s", os.path.normpath(file_path), path, os.path.getsize(file_path), extra_info)
         say('END')
         sys.stdout.flush()
     except:
@@ -177,6 +193,13 @@ def list_sources(paths):
         traceback.print_exc()
         sys.exit(1)
 
+def compute_path_hash(path):
+    # computes hash string of provided path
+    h = 0
+    for c in path:
+        h = (31 * h + ord(c)) & 0xFFFFFFFF
+    h = ((h + 0x80000000) & 0xFFFFFFFF) - 0x80000000
+    return str(h)
 
 #noinspection PyBroadException
 def zip_sources(zip_path):
@@ -342,7 +365,7 @@ if __name__ == "__main__":
     from getopt import getopt
 
     helptext = get_help_text()
-    opts, args = getopt(sys.argv[1:], "d:hbqxvc:ps:LSz")
+    opts, args = getopt(sys.argv[1:], "d:hbqxvc:ps:C:LSz")
     opts = dict(opts)
 
     quiet = '-q' in opts
@@ -386,7 +409,7 @@ if __name__ == "__main__":
             report("Expected no args with -S, got %d args", len(args))
             sys.exit(1)
         say(VERSION)
-        list_sources(sys.path)
+        list_sources(sys.path, opts.get('-C', None))
         sys.exit(0)
 
     if "-z" in opts:
index c1d41f3385d3f45e315130b632d96560fef541bf..bd96a0aa04a7a184fb49a189db6edf96a3ad3c80 100644 (file)
@@ -29,7 +29,7 @@
       <library name="SnuggleTeX">
         <CLASSES>
           <root url="jar://$MODULE_DIR$/lib/batik.jar!/" />
-          <root url="jar://$MODULE_DIR$/lib/commons-io-1.4.jar!/" />
+          <root url="jar://$MODULE_DIR$/lib/commons-io-2.3.jar!/" />
           <root url="jar://$MODULE_DIR$/lib/jeuclid-core-3.1.9.jar!/" />
           <root url="jar://$MODULE_DIR$/lib/snuggletex-core-1.3-SNAPSHOT.jar!/" />
           <root url="jar://$MODULE_DIR$/lib/xmlgraphics-commons-1.3.1.jar!/" />
diff --git a/python/ipnb/lib/commons-io-1.4.jar b/python/ipnb/lib/commons-io-1.4.jar
deleted file mode 100644 (file)
index 133dc6c..0000000
Binary files a/python/ipnb/lib/commons-io-1.4.jar and /dev/null differ
diff --git a/python/ipnb/lib/commons-io-2.3.jar b/python/ipnb/lib/commons-io-2.3.jar
new file mode 100644 (file)
index 0000000..d5a0771
Binary files /dev/null and b/python/ipnb/lib/commons-io-2.3.jar differ
index 52b9200c09476fb38eb9d78880544c10c96df7f3..dedf9b380cfa6b42bd2ec8cb18a69bf7619b7295 100644 (file)
     <extensionPoint qualifiedName="Pythonid.runConfigurationExtension" interface="com.jetbrains.python.run.PythonRunConfigurationExtension"/>
     <extensionPoint qualifiedName="Pythonid.visitorFilter" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
     <extensionPoint qualifiedName="Pythonid.remoteInterpreterManager" interface="com.jetbrains.python.remote.PythonRemoteInterpreterManager"/>
+    <extensionPoint qualifiedName="Pythonid.remoteProcessStarterManager" interface="com.jetbrains.python.run.PyRemoteProcessStarterManager"/>
     <extensionPoint qualifiedName="Pythonid.keywordArgumentProvider" interface="com.jetbrains.python.psi.impl.PyKeywordArgumentProvider"/>
     <extensionPoint qualifiedName="Pythonid.canonicalPathProvider" interface="com.jetbrains.python.psi.resolve.PyCanonicalPathProvider"/>
     <extensionPoint qualifiedName="Pythonid.templateContextProvider" interface="com.jetbrains.python.templateLanguages.TemplateContextProvider"/>
index fc6fc5092bae36ac2eef348e956fbf9cf8b9201d..adfb44e1625f62bb6dea6216f2fcb79a73f6b9e1 100644 (file)
@@ -35,7 +35,6 @@ import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.PsiElement;
@@ -293,9 +292,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
 
   protected static int getRemoteTunneledPort(int localPort, @NotNull RemoteProcessHandlerBase handler) throws IOException {
     try {
-      Pair<String, Integer> remoteSocket = handler.obtainRemoteSocket();
-      handler.addRemoteForwarding(remoteSocket.getSecond(), localPort);
-      return remoteSocket.getSecond();
+      return handler.getRemoteSocket(localPort).getSecond();
     }
     catch (Exception e) {
       throw new IOException(e);
index b03892390a7e31de1ef99b889755f87764222aa5..6fdb63e4a012dfe6c8c1ccb870e8ce93f85f846b 100644 (file)
@@ -24,14 +24,12 @@ import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.projectRoots.Sdk;
 import com.intellij.openapi.projectRoots.SdkAdditionalData;
 import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.remote.RemoteFile;
-import com.intellij.remote.RemoteSdkAdditionalData;
-import com.intellij.remote.RemoteSdkCredentials;
-import com.intellij.remote.VagrantNotStartedException;
+import com.intellij.remote.*;
 import com.intellij.util.ArrayUtil;
 import com.jetbrains.python.remote.PyRemotePathMapper;
 import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
 import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import com.jetbrains.python.run.PyRemoteProcessStarterManagerUtil;
 import com.jetbrains.python.sdk.PythonSdkType;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -55,11 +53,18 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
   protected String getHelperPath(String helper) throws ExecutionException {
     final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
     if (sdkData instanceof PyRemoteSdkAdditionalDataBase) {
-      final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+      final PyRemoteSdkAdditionalDataBase remoteSdkData = (PyRemoteSdkAdditionalDataBase) sdkData;
       try {
-        final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
-        if (!StringUtil.isEmpty(remoteSdkCredentials.getHelpersPath())) {
-          return new RemoteFile(remoteSdkCredentials.getHelpersPath(), helper).getPath();
+        String helpersPath;
+        if (remoteSdkData.getRemoteConnectionType() != CredentialsType.DOCKER) {
+          final RemoteSdkCredentials remoteSdkCredentials = remoteSdkData.getRemoteSdkCredentials(false);
+          helpersPath = remoteSdkCredentials.getHelpersPath();
+        }
+        else {
+          helpersPath = remoteSdkData.getHelpersPath();
+        }
+        if (!StringUtil.isEmpty(helpersPath)) {
+          return new RemoteFile(helpersPath, helper).getPath();
         }
         else {
           return null;
@@ -80,26 +85,38 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
   protected ProcessOutput getPythonProcessOutput(@NotNull String helperPath,
                                                  @NotNull List<String> args,
                                                  boolean askForSudo,
-                                                 boolean showProgress, @Nullable String workingDir) throws ExecutionException {
+                                                 boolean showProgress, @Nullable final String workingDir) throws ExecutionException {
     final String homePath = mySdk.getHomePath();
     if (homePath == null) {
       throw new ExecutionException("Cannot find Python interpreter for SDK " + mySdk.getName());
     }
     final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
     if (sdkData instanceof PyRemoteSdkAdditionalDataBase) { //remote interpreter
+      final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+
       RemoteSdkCredentials remoteSdkCredentials;
-      try {
-        remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
-      }
-      catch (InterruptedException e) {
-        LOG.error(e);
-        remoteSdkCredentials = null;
-      }
-      catch (ExecutionException e) {
-        throw analyzeException(e, helperPath, args);
+      if (((PyRemoteSdkAdditionalDataBase)sdkData).getRemoteConnectionType() != CredentialsType.DOCKER) {
+        try {
+          remoteSdkCredentials = ((RemoteSdkAdditionalData)sdkData).getRemoteSdkCredentials(false);
+        }
+        catch (InterruptedException e) {
+          LOG.error(e);
+          remoteSdkCredentials = null;
+        }
+        catch (ExecutionException e) {
+          throw analyzeException(e, helperPath, args);
+        }
+        if (manager != null && remoteSdkCredentials != null) {
+          if (askForSudo) {
+            askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
+          }
+        }
+        else {
+          throw new PyExecutionException(PythonRemoteInterpreterManager.WEB_DEPLOYMENT_PLUGIN_IS_DISABLED, helperPath, args);
+        }
       }
-      final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
-      if (manager != null && remoteSdkCredentials != null) {
+
+      if (manager != null) {
         final List<String> cmdline = new ArrayList<String>();
         cmdline.add(homePath);
         cmdline.add(RemoteFile.detectSystemByPath(homePath).createRemoteFile(helperPath).getPath());
@@ -109,14 +126,23 @@ public class PyRemotePackageManagerImpl extends PyPackageManagerImpl {
             return quoteIfNeeded(input);
           }
         }));
-        if (askForSudo) {
-          askForSudo = !manager.ensureCanWrite(null, remoteSdkCredentials, remoteSdkCredentials.getInterpreterPath());
-        }
         ProcessOutput processOutput;
         do {
-          PyRemotePathMapper pathMapper = manager.setupMappings(null, (PyRemoteSdkAdditionalDataBase)sdkData, null);
-          processOutput =
-            manager.runRemoteProcess(null, remoteSdkCredentials, pathMapper, ArrayUtil.toStringArray(cmdline), workingDir, askForSudo);
+          final PyRemoteSdkAdditionalDataBase remoteSdkAdditionalData = (PyRemoteSdkAdditionalDataBase)sdkData;
+          final PyRemotePathMapper pathMapper = manager.setupMappings(null, remoteSdkAdditionalData, null);
+          try {
+            processOutput = PyRemoteProcessStarterManagerUtil.getManager(remoteSdkAdditionalData).executeRemoteProcess(null,
+                                                                                                                       ArrayUtil
+                                                                                                                         .toStringArray(
+                                                                                                                           cmdline),
+                                                                                                                       workingDir, manager,
+                                                                                                                       remoteSdkAdditionalData,
+                                                                                                                       pathMapper,
+                                                                                                                       askForSudo, true);
+          }
+          catch (InterruptedException e) {
+            throw new ExecutionException(e);
+          }
           if (askForSudo && processOutput.getStderr().contains("sudo: 3 incorrect password attempts")) {
             continue;
           }
diff --git a/python/src/com/jetbrains/python/remote/PyRemoteProcessConnectionData.java b/python/src/com/jetbrains/python/remote/PyRemoteProcessConnectionData.java
new file mode 100644 (file)
index 0000000..0601eb4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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.remote;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public interface PyRemoteProcessConnectionData {
+}
index 218fcf3a01aad056ec8036e1772a396a7afe0d86..3a7da17edf3be07e5216815e03f1c636e4c34fd6 100644 (file)
@@ -59,12 +59,20 @@ public abstract class PythonRemoteInterpreterManager {
 
   public final static Key<PathMapper> PATH_MAPPING_SETTINGS_KEY = Key.create("PATH_MAPPING_SETTINGS_KEY");
 
+  /**
+   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#startRemoteProcess(Project, GeneralCommandLine, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper)}
+   */
+  @Deprecated
   public abstract ProcessHandler startRemoteProcess(@Nullable Project project,
                                                     @NotNull PyRemoteSdkCredentials data,
                                                     @NotNull GeneralCommandLine commandLine,
                                                     @NotNull PyRemotePathMapper pathMapper)
     throws RemoteSdkException;
 
+  /**
+   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#startRemoteProcess(Project, GeneralCommandLine, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper)}
+   */
+  @Deprecated
   public abstract ProcessHandler startRemoteProcessWithPid(@Nullable Project project,
                                                            @NotNull PyRemoteSdkCredentials data,
                                                            @NotNull GeneralCommandLine commandLine,
@@ -75,6 +83,9 @@ public abstract class PythonRemoteInterpreterManager {
                                     NullableConsumer<Sdk> sdkCallback);
 
 
+  /**
+   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#executeRemoteProcess(Project, String[], String, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper, boolean)}
+   */
   public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
                                                  RemoteSdkCredentials data,
                                                  @NotNull PyRemotePathMapper pathMapper,
@@ -83,6 +94,18 @@ public abstract class PythonRemoteInterpreterManager {
                                                  boolean askForSudo)
     throws RemoteSdkException;
 
+  /**
+   * @deprecated use {@link com.jetbrains.python.run.PyRemoteProcessStarterManager#executeRemoteProcess(Project, String[], String, PythonRemoteInterpreterManager, PyRemoteSdkAdditionalDataBase, PyRemotePathMapper, boolean)}
+   */
+  @Deprecated
+  public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
+                                                 RemoteSdkCredentials data,
+                                                 @NotNull PyRemotePathMapper pathMapper,
+                                                 String[] command,
+                                                 @Nullable String workingDir,
+                                                 boolean askForSudo, String sdkHomePath)
+    throws RemoteSdkException;
+
   @NotNull
   public abstract RemoteSshProcess createRemoteProcess(@Nullable Project project,
                                                        @NotNull PyRemoteSdkCredentials data,
@@ -174,4 +197,3 @@ public abstract class PythonRemoteInterpreterManager {
 
   public abstract void runVagrant(@NotNull String vagrantFolder, @Nullable String machineName) throws ExecutionException;
 }
-
index 90914651eae87d4840ccced446798527b9ea21b2..5e2a94c45a068b3435a4020d7e08d6a81fdedba5 100644 (file)
@@ -62,22 +62,24 @@ public class PyRemoteProcessStarter {
   }
 
   protected ProcessHandler doStartRemoteProcess(@NotNull Sdk sdk,
-                                                @NotNull GeneralCommandLine commandLine,
-                                                @NotNull PythonRemoteInterpreterManager manager,
-                                                @Nullable Project project,
+                                                @NotNull final GeneralCommandLine commandLine,
+                                                @NotNull final PythonRemoteInterpreterManager manager,
+                                                @Nullable final Project project,
                                                 @Nullable PyRemotePathMapper pathMapper)
     throws ExecutionException {
-
     SdkAdditionalData data = sdk.getSdkAdditionalData();
     assert data instanceof PyRemoteSdkAdditionalDataBase;
-    PyRemoteSdkAdditionalDataBase pyRemoteSdkAdditionalDataBase = (PyRemoteSdkAdditionalDataBase)data;
-    try {
-      pathMapper = manager.setupMappings(project, pyRemoteSdkAdditionalDataBase, pathMapper);
+    final PyRemoteSdkAdditionalDataBase pyRemoteSdkAdditionalDataBase = (PyRemoteSdkAdditionalDataBase)data;
 
-      return manager.startRemoteProcess(project, pyRemoteSdkAdditionalDataBase.getRemoteSdkCredentials(true), commandLine, pathMapper);
+    final PyRemotePathMapper extendedPathMapper = manager.setupMappings(project, pyRemoteSdkAdditionalDataBase, pathMapper);
+
+    try {
+      return PyRemoteProcessStarterManagerUtil
+        .getManager(pyRemoteSdkAdditionalDataBase).startRemoteProcess(project, commandLine, manager, pyRemoteSdkAdditionalDataBase,
+                                                     extendedPathMapper);
     }
     catch (InterruptedException e) {
-      throw new ExecutionException(e); //TODO: handle exception
+      throw new ExecutionException(e);
     }
   }
 }
diff --git a/python/src/com/jetbrains/python/run/PyRemoteProcessStarterManager.java b/python/src/com/jetbrains/python/run/PyRemoteProcessStarterManager.java
new file mode 100644 (file)
index 0000000..e83bb0d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * 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.run;
+
+import com.intellij.execution.ExecutionException;
+import com.intellij.execution.configurations.GeneralCommandLine;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.execution.process.ProcessOutput;
+import com.intellij.openapi.extensions.ExtensionPointName;
+import com.intellij.openapi.project.Project;
+import com.jetbrains.python.remote.PyRemotePathMapper;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import com.jetbrains.python.remote.PythonRemoteInterpreterManager;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public interface PyRemoteProcessStarterManager {
+  ExtensionPointName<PyRemoteProcessStarterManager> EP_NAME = ExtensionPointName.create("Pythonid.remoteProcessStarterManager");
+
+  boolean supports(@NotNull PyRemoteSdkAdditionalDataBase sdkAdditionalData);
+
+  @NotNull
+  ProcessHandler startRemoteProcess(@Nullable Project project,
+                                    @NotNull GeneralCommandLine commandLine,
+                                    @NotNull PythonRemoteInterpreterManager manager,
+                                    @NotNull PyRemoteSdkAdditionalDataBase sdkAdditionalData,
+                                    @NotNull PyRemotePathMapper pathMapper) throws ExecutionException, InterruptedException;
+
+  @NotNull
+  ProcessOutput executeRemoteProcess(@Nullable Project project,
+                                     @NotNull String[] command,
+                                     @Nullable String workingDir,
+                                     @NotNull PythonRemoteInterpreterManager manager,
+                                     @NotNull PyRemoteSdkAdditionalDataBase sdkAdditionalData,
+                                     @NotNull PyRemotePathMapper pathMapper, boolean askForSudo, boolean checkHelpers) throws ExecutionException, InterruptedException;
+}
diff --git a/python/src/com/jetbrains/python/run/PyRemoteProcessStarterManagerUtil.java b/python/src/com/jetbrains/python/run/PyRemoteProcessStarterManagerUtil.java
new file mode 100644 (file)
index 0000000..89015d7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * 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.run;
+
+import com.intellij.execution.ExecutionException;
+import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Alexander Koshevoy
+ */
+public class PyRemoteProcessStarterManagerUtil {
+  private PyRemoteProcessStarterManagerUtil() {
+  }
+
+  @NotNull
+  public static PyRemoteProcessStarterManager getManager(@NotNull PyRemoteSdkAdditionalDataBase pyRemoteSdkAdditionalDataBase)
+    throws ExecutionException {
+
+    for (PyRemoteProcessStarterManager processManager : PyRemoteProcessStarterManager.EP_NAME.getExtensions()) {
+      if (processManager.supports(pyRemoteSdkAdditionalDataBase)) {
+        return processManager;
+      }
+    }
+    throw new IllegalStateException("Unable to find support for " + pyRemoteSdkAdditionalDataBase + " SDK");
+  }
+}
index c08e908d8cb066917b7b85037472e46f3cc46bd9..4bb9cefe4ad69319f1350abe03e382201f468f82 100644 (file)
@@ -762,6 +762,9 @@ public class PythonSdkType extends SdkType {
     return path;
   }
 
+  /**
+   * Returns skeletons location on the local machine. Independent of SDK credentials type (e.g. ssh, Vagrant, Docker or else).
+   */
   public static String getSkeletonsPath(String basePath, String sdkHome) {
     String sep = File.separator;
     return getSkeletonsRootPath(basePath) + sep + FileUtil.toSystemIndependentName(sdkHome).hashCode() + sep;
@@ -1072,6 +1075,10 @@ public class PythonSdkType extends SdkType {
         @Override
         public void deployment(@NotNull WebDeploymentCredentialsHolder cred) {
         }
+
+        @Override
+        public void docker(@NotNull DockerCredentialsHolder credentials) {
+        }
       });
       return result.get();
     }