Fixed console debug for remote interpreter (PY-12959).
authorDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Thu, 15 May 2014 21:59:45 +0000 (23:59 +0200)
committerDmitry Trofimov <dmitry.trofimov@jetbrains.com>
Thu, 15 May 2014 21:59:45 +0000 (23:59 +0200)
python/src/com/jetbrains/python/console/PyConsoleDebugProcess.java
python/src/com/jetbrains/python/console/PyConsoleDebugProcessHandler.java
python/src/com/jetbrains/python/console/PydevConsoleRunner.java
python/src/com/jetbrains/python/console/PydevRemoteConsoleCommunication.java [new file with mode: 0644]
python/src/com/jetbrains/python/debugger/PositionConverterProvider.java [new file with mode: 0644]
python/src/com/jetbrains/python/debugger/PyDebugProcess.java
python/src/com/jetbrains/python/remote/PythonRemoteInterpreterManager.java
python/src/com/jetbrains/python/remote/RemoteDebuggableProcessHandler.java

index 893d0f6a10f1a2dbbd568bf3b726b35dfd800486..616dc7c8e80350e1559927c12dc988f45010a9fe 100644 (file)
  */
 package com.jetbrains.python.console;
 
-import com.intellij.execution.process.ProcessHandler;
 import com.intellij.execution.ui.ConsoleViewContentType;
 import com.intellij.execution.ui.ExecutionConsole;
-import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.remote.RemoteProcessHandlerBase;
 import com.intellij.util.ui.UIUtil;
 import com.intellij.xdebugger.XDebugSession;
 import com.jetbrains.python.debugger.PyDebugProcess;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.net.ServerSocket;
 
@@ -32,13 +30,15 @@ import java.net.ServerSocket;
  */
 public class PyConsoleDebugProcess extends PyDebugProcess {
   private final int myLocalPort;
+  private final PyConsoleDebugProcessHandler myConsoleDebugProcessHandler;
 
   public PyConsoleDebugProcess(@NotNull XDebugSession session,
                                @NotNull final ServerSocket serverSocket,
                                @NotNull final ExecutionConsole executionConsole,
-                               @Nullable final ProcessHandler processHandler) {
-    super(session, serverSocket, executionConsole, processHandler, false);
+                               @NotNull final PyConsoleDebugProcessHandler consoleDebugProcessHandler) {
+    super(session, serverSocket, executionConsole, consoleDebugProcessHandler, false);
     myLocalPort = serverSocket.getLocalPort();
+    myConsoleDebugProcessHandler = consoleDebugProcessHandler;
   }
 
   @Override
@@ -77,7 +77,14 @@ public class PyConsoleDebugProcess extends PyDebugProcess {
   }
 
   public void connect(PydevConsoleCommunication consoleCommunication) throws Exception {
-    consoleCommunication.connectToDebugger(myLocalPort);
+    int portToConnect;
+    if (myConsoleDebugProcessHandler.getConsoleProcessHandler() instanceof RemoteProcessHandlerBase) {
+      portToConnect = getRemoteTunneledPort(myLocalPort,
+                                            ((RemoteProcessHandlerBase)myConsoleDebugProcessHandler.getConsoleProcessHandler()));
+    } else {
+      portToConnect = myLocalPort;
+    }
+    consoleCommunication.connectToDebugger(portToConnect);
   }
 
   public void waitForNextConnection() {
index aa644d081eb364833796ef111811a5a73a3d16b4..9399bcae6cb3721ecebe3876ef84f7a701db542b 100644 (file)
@@ -17,17 +17,22 @@ package com.jetbrains.python.console;
 
 import com.intellij.execution.process.*;
 import com.intellij.openapi.util.Key;
+import com.jetbrains.python.debugger.PositionConverterProvider;
+import com.jetbrains.python.debugger.PyDebugProcess;
+import com.jetbrains.python.debugger.PyLocalPositionConverter;
+import com.jetbrains.python.debugger.PyPositionConverter;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.OutputStream;
 
 /**
  * @author traff
  */
-public class PyConsoleDebugProcessHandler extends ProcessHandler {
-  private PydevConsoleCommunication myConsoleCommunication;
+public class PyConsoleDebugProcessHandler extends ProcessHandler implements PositionConverterProvider {
+  private final PyConsoleProcessHandler myConsoleProcessHandler;
 
-  public PyConsoleDebugProcessHandler(final PyConsoleProcessHandler processHandler, PydevConsoleCommunication communication) {
-    myConsoleCommunication = communication;
+  public PyConsoleDebugProcessHandler(final PyConsoleProcessHandler processHandler) {
+    myConsoleProcessHandler = processHandler;
     processHandler.addProcessListener(new ProcessListener() {
       @Override
       public void startNotified(ProcessEvent event) {
@@ -77,4 +82,19 @@ public class PyConsoleDebugProcessHandler extends ProcessHandler {
   public OutputStream getProcessInput() {
     return null;
   }
+
+  public PyConsoleProcessHandler getConsoleProcessHandler() {
+    return myConsoleProcessHandler;
+  }
+
+  @Nullable
+  @Override
+  public PyPositionConverter createPositionConverter(PyDebugProcess debugProcess) {
+    if (myConsoleProcessHandler instanceof PositionConverterProvider) {
+      return ((PositionConverterProvider)myConsoleProcessHandler).createPositionConverter(debugProcess);
+    }
+    else {
+      return new PyLocalPositionConverter();
+    }
+  }
 }
index 3f9ac189a1045b8f5d74b7fcdeb88084294d5894..221337572811fe1eef17429a4d538dcec4342527 100644 (file)
@@ -79,7 +79,6 @@ import com.jetbrains.python.console.completion.PydevConsoleElement;
 import com.jetbrains.python.console.parsing.PythonConsoleData;
 import com.jetbrains.python.console.pydev.ConsoleCommunication;
 import com.jetbrains.python.debugger.PyDebugRunner;
-import com.jetbrains.python.debugger.PyLocalPositionConverter;
 import com.jetbrains.python.debugger.PySourcePosition;
 import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase;
 import com.jetbrains.python.remote.PyRemoteSdkCredentials;
@@ -88,7 +87,6 @@ import com.jetbrains.python.run.ProcessRunner;
 import com.jetbrains.python.run.PythonCommandLineState;
 import com.jetbrains.python.run.PythonTracebackFilter;
 import com.jetbrains.python.sdk.PySdkUtil;
-import com.jetbrains.python.sdk.PythonSdkType;
 import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
 import icons.PythonIcons;
 import org.apache.xmlrpc.XmlRpcException;
@@ -367,7 +365,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
       remoteProcess.addRemoteTunnel(remotePorts.second, "localhost", myPorts[1]);
 
 
-      myPydevConsoleCommunication = new PydevConsoleCommunication(getProject(), myPorts[0], remoteProcess, myPorts[1]);
+      myPydevConsoleCommunication = new PydevRemoteConsoleCommunication(getProject(), myPorts[0], remoteProcess, myPorts[1]);
       return remoteProcess;
     }
     catch (Exception e) {
@@ -422,8 +420,29 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
 
   @Override
   protected PyConsoleProcessHandler createProcessHandler(final Process process) {
-    myProcessHandler = new PyConsoleProcessHandler(process, getConsoleView(), myPydevConsoleCommunication, myCommandLine,
-                                                   CharsetToolkit.UTF8_CHARSET);
+    if (PySdkUtil.isRemote(mySdk)) {
+      PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
+      if (manager != null) {
+        PyRemoteSdkAdditionalDataBase data = (PyRemoteSdkAdditionalDataBase)mySdk.getSdkAdditionalData();
+        assert data != null;
+        try {
+          myProcessHandler =
+            manager.createConsoleProcessHandler(process, data.getRemoteSdkCredentials(), getConsoleView(), myPydevConsoleCommunication,
+                                                myCommandLine, CharsetToolkit.UTF8_CHARSET,
+                                                manager.setupMappings(getProject(), data, null));
+        }
+        catch (InterruptedException e) {
+          LOG.error("Error getting remote credentials");
+        }
+      }
+      else {
+        LOG.error("Can't create remote console process handler");
+      }
+    }
+    else {
+      myProcessHandler = new PyConsoleProcessHandler(process, getConsoleView(), myPydevConsoleCommunication, myCommandLine,
+                                                     CharsetToolkit.UTF8_CHARSET);
+    }
     return myProcessHandler;
   }
 
@@ -810,7 +829,8 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
     public void update(AnActionEvent e) {
       if (mySession != null) {
         e.getPresentation().setEnabled(false);
-      } else {
+      }
+      else {
         e.getPresentation().setEnabled(true);
       }
     }
@@ -843,19 +863,19 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
         public XDebugProcess start(@NotNull final XDebugSession session) {
           PythonDebugLanguageConsoleView debugConsoleView = new PythonDebugLanguageConsoleView(getProject(), mySdk);
 
-          PyConsoleDebugProcessHandler consoleDebugProcessHandler = new PyConsoleDebugProcessHandler(myProcessHandler, myPydevConsoleCommunication);
+          PyConsoleDebugProcessHandler consoleDebugProcessHandler =
+            new PyConsoleDebugProcessHandler(myProcessHandler);
 
           PyConsoleDebugProcess consoleDebugProcess =
             new PyConsoleDebugProcess(session, serverSocket, debugConsoleView,
                                       consoleDebugProcessHandler);
 
-          PythonDebugConsoleCommunication communication = PyDebugRunner.initDebugConsoleView(getProject(), consoleDebugProcess, debugConsoleView, consoleDebugProcessHandler);
+          PythonDebugConsoleCommunication communication =
+            PyDebugRunner.initDebugConsoleView(getProject(), consoleDebugProcess, debugConsoleView, consoleDebugProcessHandler);
 
           myPydevConsoleCommunication.setDebugCommunication(communication);
           debugConsoleView.attachToProcess(consoleDebugProcessHandler);
 
-          consoleDebugProcess.setPositionConverter(new PyLocalPositionConverter());
-
           consoleDebugProcess.waitForNextConnection();
 
           try {
diff --git a/python/src/com/jetbrains/python/console/PydevRemoteConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevRemoteConsoleCommunication.java
new file mode 100644 (file)
index 0000000..9e4264f
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2000-2014 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.console;
+
+import com.intellij.openapi.project.Project;
+
+/**
+ * @author traff
+ */
+public class PydevRemoteConsoleCommunication extends PydevConsoleCommunication {
+  /**
+   * Initializes the xml-rpc communication.
+   *
+   * @param project
+   * @param port       the port where the communication should happen.
+   * @param process    this is the process that was spawned (server for the XML-RPC)
+   * @param clientPort
+   * @throws MalformedURLException
+   */
+  public PydevRemoteConsoleCommunication(Project project, int port, Process process, int clientPort)
+    throws Exception {
+    super(project, port, process, clientPort);
+  }
+}
diff --git a/python/src/com/jetbrains/python/debugger/PositionConverterProvider.java b/python/src/com/jetbrains/python/debugger/PositionConverterProvider.java
new file mode 100644 (file)
index 0000000..0235002
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2000-2014 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.debugger;
+
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author traff
+ */
+public interface PositionConverterProvider {
+  @Nullable
+  PyPositionConverter createPositionConverter(PyDebugProcess debugProcess);
+}
index 0bae7af19732730a099907677b73e4de02326521..cff49fb1f0d8c2b92f83f2d76dd584f29e0c54d4 100644 (file)
@@ -45,7 +45,6 @@ import com.intellij.xdebugger.frame.XValueChildrenList;
 import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
 import com.jetbrains.python.console.pydev.PydevCompletionVariant;
 import com.jetbrains.python.debugger.pydev.*;
-import com.jetbrains.python.remote.RemoteDebuggableProcessHandler;
 import com.jetbrains.python.run.PythonProcessHandler;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -114,8 +113,8 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
     if (myProcessHandler != null) {
       myProcessHandler.addProcessListener(this);
     }
-    if (processHandler instanceof RemoteDebuggableProcessHandler) {
-      myPositionConverter = ((RemoteDebuggableProcessHandler)processHandler).createPositionConverter(this);
+    if (processHandler instanceof PositionConverterProvider) {
+      myPositionConverter = ((PositionConverterProvider)processHandler).createPositionConverter(this);
     }
     else {
       myPositionConverter = new PyLocalPositionConverter();
@@ -263,21 +262,24 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
   @Override
   public int handleDebugPort(int localPort) throws IOException {
     if (myProcessHandler instanceof RemoteProcessHandlerBase) {
-      RemoteProcessHandlerBase remoteProcessHandler = (RemoteProcessHandlerBase)myProcessHandler;
-      try {
-        Pair<String, Integer> remoteSocket = remoteProcessHandler.obtainRemoteSocket();
-        remoteProcessHandler.addRemoteForwarding(remoteSocket.getSecond(), localPort);
-        return remoteSocket.getSecond();
-      }
-      catch (Exception e) {
-        throw new IOException(e);
-      }
+      return getRemoteTunneledPort(localPort, (RemoteProcessHandlerBase)myProcessHandler);
     }
     else {
       return localPort;
     }
   }
 
+  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();
+    }
+    catch (Exception e) {
+      throw new IOException(e);
+    }
+  }
+
   @Override
   public void recordSignature(PySignature signature) {
     PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature));
index 2d2d12e1db1f3bb34c9c8de138d2a99acaf185ad..a7a96a09d3af6b7c0e69a301e8769db2d6de22f3 100644 (file)
@@ -31,6 +31,9 @@ import com.intellij.remote.*;
 import com.intellij.util.NullableConsumer;
 import com.intellij.util.PathMappingSettings;
 import com.jetbrains.python.PythonHelpersLocator;
+import com.jetbrains.python.console.PyConsoleProcessHandler;
+import com.jetbrains.python.console.PydevConsoleCommunication;
+import com.jetbrains.python.console.PythonConsoleView;
 import com.jetbrains.python.sdk.skeletons.PySkeletonGenerator;
 import org.jdom.Element;
 import org.jetbrains.annotations.NotNull;
@@ -38,6 +41,7 @@ import org.jetbrains.annotations.Nullable;
 
 import java.awt.*;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.Collection;
 import java.util.List;
 
@@ -69,7 +73,7 @@ public abstract class PythonRemoteInterpreterManager {
 
   public abstract ProcessOutput runRemoteProcess(@Nullable Project project,
                                                  RemoteSdkCredentials data,
-                                                 @NotNull PathMappingSettings mappings, 
+                                                 @NotNull PathMappingSettings mappings,
                                                  String[] command,
                                                  @Nullable String workingDir,
                                                  boolean askForSudo)
@@ -79,7 +83,7 @@ public abstract class PythonRemoteInterpreterManager {
   public abstract RemoteSshProcess createRemoteProcess(@Nullable Project project,
                                                        @NotNull PyRemoteSdkCredentials data,
                                                        @NotNull PathMappingSettings mappings,
-                                                         @NotNull GeneralCommandLine commandLine, boolean allocatePty)
+                                                       @NotNull GeneralCommandLine commandLine, boolean allocatePty)
     throws RemoteSdkException;
 
   public abstract boolean editSdk(@NotNull Project project, @NotNull SdkModificator sdkModificator, Collection<Sdk> existingSdks);
@@ -143,6 +147,12 @@ public abstract class PythonRemoteInterpreterManager {
 
   public abstract boolean testConnection(RemoteCredentials credentials);
 
+  public abstract PyConsoleProcessHandler createConsoleProcessHandler(Process process,
+                                                                      PyRemoteSdkCredentials data,
+                                                                      PythonConsoleView view,
+                                                                      PydevConsoleCommunication consoleCommunication,
+                                                                      String commandLine, Charset charset, PathMappingSettings settings);
+
   public static class PyRemoteInterpreterExecutionException extends ExecutionException {
 
     public PyRemoteInterpreterExecutionException() {
index db2b329e1fd7ddbeb0a08896a7a7888d33caecd5..b58648e4e388d95d6ba23dc97bd6bd2ff7801c5d 100644 (file)
 package com.jetbrains.python.remote;
 
 import com.intellij.remote.RemoteProcessHandlerBase;
-import com.jetbrains.python.debugger.PyDebugProcess;
-import com.jetbrains.python.debugger.PyPositionConverter;
+import com.jetbrains.python.debugger.PositionConverterProvider;
 
 /**
  * @author yole
  */
-public interface RemoteDebuggableProcessHandler extends RemoteProcessHandlerBase {
-  PyPositionConverter createPositionConverter(PyDebugProcess debugProcess);
+public interface RemoteDebuggableProcessHandler extends RemoteProcessHandlerBase, PositionConverterProvider{
 }