mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Cannot do keyboard input in debug mode with interactive console turned on (PY-16825 )
In debug console we should handle input calls on Python side and notify Java side, like we do it in Interactive Python Console. After handling input we should send it back to Python process.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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, '')
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -37,5 +37,7 @@ public interface IPyDebugProcess extends PyFrameAccessor {
|
||||
|
||||
boolean isSuspendedOnAllThreadsPolicy();
|
||||
|
||||
void consoleInputRequested();
|
||||
|
||||
XDebugSession getSession();
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public abstract class AbstractCommand<T> {
|
||||
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<T> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<InterpreterResponse, Object> callback) {
|
||||
myExpression.append(code.getText());
|
||||
exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback<Pair<String, Boolean>>() {
|
||||
@Override
|
||||
public void ok(Pair<String, Boolean> 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<Pair<String, Boolean>>() {
|
||||
@Override
|
||||
public void ok(Pair<String, Boolean> 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
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user