diff --git a/python/helpers/pydev/_pydev_bundle/pydev_console_utils.py b/python/helpers/pydev/_pydev_bundle/pydev_console_utils.py index 19be9971bda1..112d0ae36de9 100644 --- a/python/helpers/pydev/_pydev_bundle/pydev_console_utils.py +++ b/python/helpers/pydev/_pydev_bundle/pydev_console_utils.py @@ -114,6 +114,31 @@ class StdIn(BaseStdIn): return '\n' +#======================================================================================================================= +# DebugConsoleStdIn +#======================================================================================================================= +class DebugConsoleStdIn(BaseStdIn): + ''' + Object to be added to stdin (to emulate it as non-blocking while the next line arrives) + ''' + + def __init__(self, dbg, original_stdin): + BaseStdIn.__init__(self) + self.debugger = dbg + self.original_stdin = original_stdin + + def readline(self, *args, **kwargs): + # Notify Java side about input and call original function + try: + cmd = self.debugger.cmd_factory.make_input_requested_message() + self.debugger.writer.add_command(cmd) + return self.original_stdin.readline(*args, **kwargs) + except Exception: + import traceback + traceback.print_exc() + return '\n' + + class CodeFragment: def __init__(self, text, is_single_line=True): self.text = text diff --git a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py index babdc3571d0a..ec0c13d692b5 100644 --- a/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py +++ b/python/helpers/pydev/_pydevd_bundle/pydevd_comm.py @@ -138,6 +138,7 @@ CMD_GET_ARRAY = 143 CMD_STEP_INTO_MY_CODE = 144 CMD_GET_CONCURRENCY_EVENT = 145 CMD_SHOW_RETURN_VALUES = 146 +CMD_INPUT_REQUESTED = 147 CMD_VERSION = 501 CMD_RETURN = 502 @@ -191,6 +192,7 @@ ID_TO_MEANING = { '144': 'CMD_STEP_INTO_MY_CODE', '145': 'CMD_GET_CONCURRENCY_EVENT', '146': 'CMD_SHOW_RETURN_VALUES', + '147': 'CMD_INPUT_REQUESTED', '501': 'CMD_VERSION', '502': 'CMD_RETURN', @@ -779,6 +781,13 @@ class NetCommandFactory: except: return self.make_error_message(0, get_exception_traceback_str()) + def make_input_requested_message(self): + try: + return NetCommand(CMD_INPUT_REQUESTED, 0, '') + except: + return self.make_error_message(0, get_exception_traceback_str()) + + def make_exit_message(self): try: net = NetCommand(CMD_EXIT, 0, '') diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index b27daef92c57..e333f90df1c7 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -1180,6 +1180,7 @@ def _locked_settrace( if bufferStdErrToServer: init_stderr_redirect() + patch_stdin(debugger) debugger.set_trace_for_frame_and_parents(get_frame(), False, overwrite_prev_trace=overwrite_prev_trace) @@ -1382,6 +1383,12 @@ def apply_debugger_options(setup_options): enable_qt_support() +def patch_stdin(debugger): + from _pydev_bundle.pydev_console_utils import DebugConsoleStdIn + orig_stdin = sys.stdin + sys.stdin = DebugConsoleStdIn(debugger, orig_stdin) + + #======================================================================================================================= # main #======================================================================================================================= @@ -1512,6 +1519,7 @@ if __name__ == '__main__': pass # It's ok not having stackless there... is_module = setup['module'] + patch_stdin(debugger) if fix_app_engine_debug: sys.stderr.write("pydev debugger: google app engine integration enabled\n") diff --git a/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java b/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java index 82155bacd88f..ce55d8e87e83 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java @@ -37,5 +37,7 @@ public interface IPyDebugProcess extends PyFrameAccessor { boolean isSuspendedOnAllThreadsPolicy(); + void consoleInputRequested(); + XDebugSession getSession(); } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java index 4ed466cacbc6..941e39343ba4 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java @@ -50,6 +50,7 @@ public abstract class AbstractCommand { public static final int STEP_INTO_MY_CODE = 144; public static final int LOG_CONCURRENCY_EVENT = 145; public static final int SHOW_RETURN_VALUES = 146; + public static final int INPUT_REQUESTED = 147; public static final int ERROR = 901; @@ -197,6 +198,10 @@ public abstract class AbstractCommand { return command == WRITE_TO_CONSOLE; } + public static boolean isInputRequested(final int command) { + return command == INPUT_REQUESTED; + } + public static boolean isExitEvent(final int command) { return command == EXIT; } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java index 7b15821aef8b..08b93e8fbfa1 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java @@ -547,6 +547,9 @@ public class RemoteDebugger implements ProcessDebugger { else if (AbstractCommand.isConcurrencyEvent(frame.getCommand())) { recordConcurrencyEvent(ProtocolParser.parseConcurrencyEvent(frame.getPayload(), myDebugProcess.getPositionConverter())); } + else if (AbstractCommand.isInputRequested(frame.getCommand())) { + myDebugProcess.consoleInputRequested(); + } else { placeResponse(frame.getSequence(), frame); } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java index e3e17b295f02..d6f4f63f0834 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java @@ -95,6 +95,15 @@ public class PythonConsoleView extends LanguageConsoleImpl implements Observable myExecuteActionHandler = consoleExecuteActionHandler; } + public void inputRequested() { + final ConsoleCommunication consoleCommunication = myExecuteActionHandler.getConsoleCommunication(); + if (consoleCommunication instanceof PythonDebugConsoleCommunication) { + ((PythonDebugConsoleCommunication)consoleCommunication).waitingForInput = true; + myExecuteActionHandler.inputRequested(); + myExecuteActionHandler.setEnabled(true); + } + } + @Override public void requestFocus() { IdeFocusManager.findInstance().requestFocus(getConsoleEditor().getContentComponent(), true); diff --git a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java index 25eb30cc0552..e357c4dd89ab 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java @@ -15,8 +15,10 @@ */ package com.jetbrains.python.console; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.encoding.EncodingProjectManager; import com.intellij.util.Function; import com.jetbrains.python.console.pydev.AbstractConsoleCommunication; import com.jetbrains.python.console.pydev.InterpreterResponse; @@ -26,12 +28,16 @@ import com.jetbrains.python.debugger.PyDebuggerException; import com.jetbrains.python.debugger.pydev.PyDebugCallback; import org.jetbrains.annotations.NotNull; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.List; /** * @author traff */ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunication { + private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.console.pydev.PythonDebugConsoleCommunication"); private final PyDebugProcess myDebugProcess; private final StringBuilder myExpression = new StringBuilder(); @@ -55,7 +61,7 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio @Override public boolean isWaitingForInput() { - return false; + return waitingForInput; } @Override @@ -78,24 +84,43 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio } public void execInterpreter(ConsoleCodeFragment code, final Function callback) { - myExpression.append(code.getText()); - exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback>() { - @Override - public void ok(Pair executed) { - boolean more = executed.second; - - if (!more) { - myExpression.setLength(0); + if (waitingForInput) { + final OutputStream processInput = myDebugProcess.getProcessHandler().getProcessInput(); + if (processInput != null) { + try { + final Charset defaultCharset = EncodingProjectManager.getInstance(myDebugProcess.getProject()).getDefaultCharset(); + String text = code.getText(); + // we save previous command text!!! + processInput.write((text + "\n").getBytes(defaultCharset)); + processInput.flush(); + } + catch (IOException e) { + LOG.error(e.getMessage()); } - callback.fun(new InterpreterResponse(more, isWaitingForInput())); } + waitingForInput = false; + } + else { - @Override - public void error(PyDebuggerException exception) { - myExpression.setLength(0); - callback.fun(new InterpreterResponse(false, isWaitingForInput())); - } - }); + myExpression.append(code.getText()); + exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback>() { + @Override + public void ok(Pair executed) { + boolean more = executed.second; + + if (!more) { + myExpression.setLength(0); + } + callback.fun(new InterpreterResponse(more, isWaitingForInput())); + } + + @Override + public void error(PyDebuggerException exception) { + myExpression.setLength(0); + callback.fun(new InterpreterResponse(false, isWaitingForInput())); + } + }); + } } @Override diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index ea7c3afccd9c..3ab7886b6237 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -346,6 +346,13 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr } } + @Override + public void consoleInputRequested() { + if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) { + ((PythonDebugLanguageConsoleView)myExecutionConsole).getPydevConsoleView().inputRequested(); + } + } + protected void afterConnect() { }