Execute multi-line code fragments by runsrouce with 'exec' option instead of one line by one (PY-10769).

This commit is contained in:
Dmitry Trofimov
2013-11-18 17:33:40 +01:00
parent 04d706e0b2
commit cbabd0bb04
8 changed files with 111 additions and 142 deletions
+28 -13
View File
@@ -132,6 +132,11 @@ class StdIn(BaseStdIn):
return '\n'
class ExecCode:
def __init__(self, code, is_single_line=True):
self.code = code
self.is_single_line = is_single_line
#=======================================================================================================================
# BaseInterpreterInterface
#=======================================================================================================================
@@ -142,20 +147,14 @@ class BaseInterpreterInterface:
self.exec_queue = _queue.Queue(0)
self.buffer = []
def needMore(self, buffer, line):
if not buffer:
buffer = []
buffer.append(line)
source = "\n".join(buffer)
def needMoreForCode(self, source):
if hasattr(self.interpreter, 'is_complete'):
return not self.interpreter.is_complete(source)
try:
code = self.interpreter.compile(source, "<input>", "single")
code = self.interpreter.compile(source, "<input>", "exec")
except (OverflowError, SyntaxError, ValueError):
# Case 1
return False
if code is None:
# Case 2
return True
@@ -163,8 +162,16 @@ class BaseInterpreterInterface:
# Case 3
return False
def needMore(self, buffer, line):
if not buffer:
buffer = []
buffer.append(line)
source = "\n".join(buffer)
return self.needMoreForCode(source)
def addExec(self, line):
def addExec(self, command):
original_in = sys.stdin
try:
help = None
@@ -203,7 +210,7 @@ class BaseInterpreterInterface:
try:
self.startExec()
more = self.doAddExec(line)
more = self.doAddExec(command)
self.finishExec()
finally:
if help is not None:
@@ -229,7 +236,7 @@ class BaseInterpreterInterface:
return more, need_input
def doAddExec(self, line):
def doAddExec(self, command):
'''
Subclasses should override.
@@ -309,14 +316,22 @@ class BaseInterpreterInterface:
def execLine(self, line):
try:
#buffer = self.interpreter.buffer[:]
self.exec_queue.put(line)
self.exec_queue.put(ExecCode(line, True))
return self.needMore(self.buffer, line)
except:
traceback.print_exc()
return False
def execMultipleLines(self, lines):
try:
self.exec_queue.put(ExecCode(lines, False))
return self.needMoreForCode(lines)
except:
traceback.print_exc()
return False
def interrupt(self):
try:
if self.interruptable:
@@ -39,14 +39,14 @@ class InterpreterInterface(BaseInterpreterInterface):
def get_greeting_msg(self):
return self.interpreter.get_greeting_msg()
def doAddExec(self, line):
def doAddExec(self, command):
self.notify_about_magic()
if (line.rstrip().endswith('??')):
if (command.rstrip().endswith('??')):
print('IPython-->')
try:
res = bool(self.interpreter.addExec(line))
res = bool(self.interpreter.addExec(command))
finally:
if (line.rstrip().endswith('??')):
if (command.rstrip().endswith('??')):
print('<--IPython')
return res
+20 -51
View File
@@ -10,7 +10,6 @@ import os
import sys
from pydevd_constants import USE_LIB_COPY
from pydevd_utils import *
if USE_LIB_COPY:
import _pydev_threading as threading
@@ -72,56 +71,25 @@ try:
except ImportError:
import _pydev_xmlrpclib as xmlrpclib
try:
class ExecState:
FIRST_CALL = True
PYDEV_CONSOLE_RUN_IN_UI = False #Defines if we should run commands in the UI thread.
from org.python.pydev.core.uiutils import RunInUiThread #@UnresolvedImport
from java.lang import Runnable #@UnresolvedImport
class Command:
def __init__(self, interpreter, command):
"""
:type command: object
:type interpreter: InteractiveConsole
"""
self.interpreter = interpreter
self.command = command
self.more = None
class Command(Runnable):
def __init__(self, interpreter, line):
self.interpreter = interpreter
self.line = line
def run(self):
if ExecState.FIRST_CALL:
ExecState.FIRST_CALL = False
sys.stdout.write('\nYou are now in a console within Eclipse.\nUse it with care as it can halt the VM.\n')
sys.stdout.write(
'Typing a line with "PYDEV_CONSOLE_TOGGLE_RUN_IN_UI"\nwill start executing all the commands in the UI thread.\n\n')
if self.line == 'PYDEV_CONSOLE_TOGGLE_RUN_IN_UI':
ExecState.PYDEV_CONSOLE_RUN_IN_UI = not ExecState.PYDEV_CONSOLE_RUN_IN_UI
if ExecState.PYDEV_CONSOLE_RUN_IN_UI:
sys.stdout.write(
'Running commands in UI mode. WARNING: using sys.stdin (i.e.: calling raw_input()) WILL HALT ECLIPSE.\n')
else:
sys.stdout.write('No longer running commands in UI mode.\n')
self.more = False
else:
self.more = self.interpreter.push(self.line)
def Sync(runnable):
if ExecState.PYDEV_CONSOLE_RUN_IN_UI:
return RunInUiThread.sync(runnable)
def run(self):
if self.command.is_single_line:
self.more = self.interpreter.push(self.command.code)
else:
return runnable.run()
self.more = self.interpreter.runsource(self.command.code, '<input>', 'exec')
except:
#If things are not there, define a way in which there's no 'real' sync, only the default execution.
class Command:
def __init__(self, interpreter, line):
self.interpreter = interpreter
self.line = line
def run(self):
self.more = self.interpreter.push(self.line)
def Sync(runnable):
runnable.run()
def Sync(runnable):
runnable.run()
try:
try:
@@ -152,8 +120,8 @@ class InterpreterInterface(BaseInterpreterInterface):
self._input_error_printed = False
def doAddExec(self, line):
command = Command(self.interpreter, line)
def doAddExec(self, command):
command = Command(self.interpreter, command)
Sync(command)
return command.more
@@ -185,11 +153,11 @@ def process_exec_queue(interpreter):
while 1:
try:
try:
line = interpreter.exec_queue.get(block=True, timeout=0.05)
command = interpreter.exec_queue.get(block=True, timeout=0.05)
except _queue.Empty:
continue
if not interpreter.addExec(line): #TODO: think about locks here
if not interpreter.addExec(command): #TODO: think about locks here
interpreter.buffer = []
except KeyboardInterrupt:
interpreter.buffer = []
@@ -274,6 +242,7 @@ def start_server(host, port, interpreter):
raise
server.register_function(interpreter.execLine)
server.register_function(interpreter.execMultipleLines)
server.register_function(interpreter.getCompletions)
server.register_function(interpreter.getFrame)
server.register_function(interpreter.getVariable)
@@ -18,7 +18,7 @@ public interface ConsoleCommunication {
boolean isExecuting();
void execInterpreter(String s, Function<InterpreterResponse, Object> callback);
void execInterpreter(ConsoleCodeFragment code, Function<InterpreterResponse, Object> callback);
void interrupt();
@@ -27,4 +27,21 @@ public interface ConsoleCommunication {
void notifyCommandExecuted();
void notifyInputRequested();
class ConsoleCodeFragment {
private final String myText;
private final boolean myIsSingleLine;
public ConsoleCodeFragment(String text, boolean isSingleLine) {
myText = text;
myIsSingleLine = isSingleLine;
}
public String getText() {
return myText;
}
public boolean isSingleLine() {
return myIsSingleLine;
}
}
}
@@ -1,54 +0,0 @@
/*******************************************************************************
* Copyright (c) 2005, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
*******************************************************************************/
package com.jetbrains.python.console.pydev;
import com.intellij.util.Function;
/**
* Interface for the console communication.
*
* This interface is meant to be the way to communicate with the shell.
*/
public interface IScriptConsoleCommunication {
/**
* Executes a given command in the interpreter (push a line)
*
* @param command the command to be executed
* @return the response from the interpreter (contains the stdout, stderr, etc).
* @throws Exception
*/
void execInterpreter(String command, Function<InterpreterResponse, Object> onResponseReceived);
/**
* Creates the completions to be applied in the interpreter.
*
* @param text the text with what should be completed (e.g.: xxx.bar.foo)
* @param offset the offset where the completion was requested in the console document
* @return a list of proposals that can be applied for the given text.
* @throws Exception
*/
//public ICompletionProposal[] getCompletions(String text, int offset) throws Exception;
/**
* Gets the description to be shown on hover to the user
*
* @param text the text representing the completion to be applied
* @return the description to be shown to the user
* @throws Exception
*/
String getDescription(String text) throws Exception;
/**
* Stops the communication with the server. Should ask the server to terminate at this point.
* @throws Exception
*/
void close();
}
@@ -53,10 +53,11 @@ import java.util.Vector;
*
* @author Fabio
*/
public class PydevConsoleCommunication extends AbstractConsoleCommunication implements IScriptConsoleCommunication, XmlRpcHandler,
public class PydevConsoleCommunication extends AbstractConsoleCommunication implements XmlRpcHandler,
PyFrameAccessor {
private static final String EXEC_LINE = "execLine";
private static final String EXEC_MULTILINE = "execMultipleLines";
private static final String GET_COMPLETIONS = "getCompletions";
private static final String GET_DESCRIPTION = "getDescription";
private static final String GET_FRAME = "getFrame";
@@ -64,6 +65,7 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
private static final String CHANGE_VARIABLE = "changeVariable";
private static final String HANDSHAKE = "handshake";
private static final String CLOSE = "close";
/**
* XML-RPC client for sending messages to the server.
*/
@@ -261,12 +263,13 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
/**
* Executes the needed command
*
* @param command
* @return a Pair with (null, more) or (error, false)
* @throws XmlRpcException
*/
protected Pair<String, Boolean> exec(final String command) throws XmlRpcException {
protected Pair<String, Boolean> exec(final ConsoleCodeFragment command) throws XmlRpcException {
setExecuting(true);
Object execute = myClient.execute(EXEC_LINE, new Object[]{command});
Object execute = myClient.execute(command.isSingleLine() ? EXEC_LINE : EXEC_MULTILINE, new Object[]{command.getText()});
Object object;
if (execute instanceof Vector) {
@@ -314,10 +317,10 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl
*
* @param command the command to be executed in the client
*/
public void execInterpreter(final String command, final Function<InterpreterResponse, Object> onResponseReceived) {
public void execInterpreter(final ConsoleCodeFragment command, final Function<InterpreterResponse, Object> onResponseReceived) {
nextResponse = null;
if (waitingForInput) {
inputReceived = command;
inputReceived = command.getText();
waitingForInput = false;
//the thread that we started in the last exec is still alive if we were waiting for an input.
}
@@ -40,8 +40,6 @@ import com.jetbrains.python.console.pydev.InterpreterResponse;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Scanner;
/**
* @author traff
*/
@@ -76,10 +74,11 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle
processOneLine(text);
}
else {
Scanner s = new Scanner(text);
while (s.hasNextLine()) {
String line = s.nextLine();
processOneLine(line);
if (StringUtil.countNewLines(text.trim()) > 0) {
executeMultiLine(text);
}
else {
processOneLine(text);
}
}
if (execAnyway && myCurrentIndentSize > 0 && indentBefore == 0) { //if code was indented and we need to exec anyway
@@ -87,6 +86,19 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle
}
}
private void executeMultiLine(@NotNull String text) {
if (myInputBuffer == null) {
myInputBuffer = new StringBuilder();
}
myInputBuffer.append(text);
final LanguageConsoleImpl console = myConsoleView.getConsole();
final Editor currentEditor = console.getConsoleEditor();
sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), false), null, console, currentEditor);
}
private void processOneLine(String line) {
int indentSize = IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false);
line = StringUtil.trimTrailing(line);
@@ -174,16 +186,22 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle
}
sendLineToConsole(new ConsoleCommunication.ConsoleCodeFragment(myInputBuffer.toString(), true), line, console, currentEditor);
}
private void sendLineToConsole(@NotNull final ConsoleCommunication.ConsoleCodeFragment code,
@Nullable final String line,
@NotNull final LanguageConsoleImpl console,
@NotNull final Editor currentEditor) {
if (myConsoleCommunication != null) {
final boolean waitedForInputBefore = myConsoleCommunication.isWaitingForInput();
final String command = myInputBuffer.toString();
if (myConsoleCommunication.isWaitingForInput()) {
myInputBuffer.setLength(0);
}
else {
executingPrompt(console);
}
myConsoleCommunication.execInterpreter(command, new Function<InterpreterResponse, Object>() {
myConsoleCommunication.execInterpreter(code, new Function<InterpreterResponse, Object>() {
public Object fun(final InterpreterResponse interpreterResponse) {
// clear
myInputBuffer = null;
@@ -192,7 +210,8 @@ public class PydevConsoleExecuteActionHandler extends ConsoleExecuteActionHandle
more(console, currentEditor);
if (myCurrentIndentSize == 0) {
// compute current indentation
setCurrentIndentSize(IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) + getPythonIndent());
setCurrentIndentSize(
(line != null ? IndentHelperImpl.getIndent(getProject(), PythonFileType.INSTANCE, line, false) : 0) + getPythonIndent());
// In this case we can insert indent automatically
indentEditor(currentEditor, myCurrentIndentSize);
}
@@ -63,8 +63,8 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
return false;
}
protected void exec(final String command, final ProcessDebugger.DebugCallback<Pair<String, Boolean>> callback) {
myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback<String>() {
protected void exec(final ConsoleCodeFragment command, final ProcessDebugger.DebugCallback<Pair<String, Boolean>> callback) {
myDebugProcess.consoleExec(command.getText(), new ProcessDebugger.DebugCallback<String>() {
@Override
public void ok(String value) {
callback.ok(parseExecResponseString(value));
@@ -77,9 +77,9 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio
});
}
public void execInterpreter(String s, final Function<InterpreterResponse, Object> callback) {
myExpression.append(s);
exec(myExpression.toString(), new ProcessDebugger.DebugCallback<Pair<String, Boolean>>() {
public void execInterpreter(ConsoleCodeFragment code, final Function<InterpreterResponse, Object> callback) {
myExpression.append(code.getText());
exec(new ConsoleCodeFragment(myExpression.toString(), false), new ProcessDebugger.DebugCallback<Pair<String, Boolean>>() {
@Override
public void ok(Pair<String, Boolean> executed) {
boolean more = executed.second;