@@ -0,0 +1,32 @@
package org.python.pydev.debug.ui.launching;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.internal.console.OpenConsoleAction;
import org.python.pydev.debug.core.PydevDebugPlugin;
import org.python.pydev.debug.newconsole.PydevConsoleFactory;

public class InteractiveConsoleConfigurationDelegate extends LaunchConfigurationDelegate {

@Override
public void launch(ILaunchConfiguration configuration, String mode,
ILaunch launch, IProgressMonitor monitor) throws CoreException {

// We need to cancel this automatic recreation because we are launching from scratch again
monitor.setCanceled(true);
Display.getDefault().asyncExec(new Runnable() {

@Override
public void run() {
PydevConsoleFactory pydevConsoleFactory = new PydevConsoleFactory();
pydevConsoleFactory.openConsole();
}
});
}


}
@@ -97,8 +97,8 @@ private static void runDebug(PythonRunnerConfig config, ILaunch launch, IProgres
subMonitor.beginTask("Launching python", 1);

// Launch & connect to the debugger
RemoteDebugger debugger = new RemoteDebugger(config);
debugger.startConnect(subMonitor);
RemoteDebugger debugger = new RemoteDebugger();
debugger.startConnect(subMonitor, config);
subMonitor.subTask("Constructing command_line...");
String[] cmdLine = config.getCommandLine(true);

@@ -0,0 +1,22 @@
package org.python.pydev.debug.newconsole;

/**
* This is an interface used to connect a debug target that wants notifications
* from the interactive console.
*/
public interface IPydevConsoleDebugTarget {

/**
* The interactive console (via {@link PydevConsoleCommunication} will call
* setSuspended(true) when there is no user command currently running. When
* a user command starts setSuspended(false) will be called.
*
* Note that the console stays running (i.e. not suspended) even when
* collecting input from the user via calls such as raw_input.
*
* @param suspended
* Current suspended state
*/
void setSuspended(boolean suspended);

}
@@ -48,6 +48,12 @@ public class PydevConsole extends ScriptConsole {
public static int nextId = -1;

private String additionalInitialComands;

/**
* Eclipse process that this console is viewing. Only non-null if there is a
* corresponding Launch/Debug Target connected to the same console
*/
private IProcess process = null;


private static String getNextId() {
@@ -196,5 +202,23 @@ public void addLink(IHyperlink link, int offset, int length) {
}
}


/**
* Eclipse process that this console is viewing. Only non-null if there is a
* corresponding Launch/Debug Target connected to the same console
*
* @return IProcess of viewed process
*/
public IProcess getProcess() {
return process;
}

/**
* Eclipse process that this console is viewing.
*
* @param process
* being viewed
*/
public void setProcess(IProcess process) {
this.process = process;
}
}
@@ -18,6 +18,7 @@
import org.apache.xmlrpc.server.XmlRpcNoSuchHandlerException;
import org.apache.xmlrpc.server.XmlRpcServer;
import org.apache.xmlrpc.webserver.WebServer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
@@ -29,6 +30,10 @@
import org.python.pydev.core.Tuple;
import org.python.pydev.core.callbacks.ICallback;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.core.PydevDebugPlugin;
import org.python.pydev.debug.model.PyDebugTargetConsole;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.newconsole.env.UserCanceledException;
import org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs;
import org.python.pydev.dltk.console.IScriptConsoleCommunication;
import org.python.pydev.dltk.console.InterpreterResponse;
@@ -163,7 +168,11 @@ protected IStatus run(IProgressMonitor monitor) {
*/
private volatile boolean firstCommWorked = false;


/**
* When non-null, the Debug Target to notify when the underlying process is suspended or running.
*/
private IPydevConsoleDebugTarget debugTarget = null;

/**
* Called when the server is requesting some input from this class.
*/
@@ -176,8 +185,8 @@ public Object execute(XmlRpcRequest request) throws XmlRpcException {
String stderrContents = stdErrReader.getAndClearContents();
//let the busy loop from execInterpreter free and enter a busy loop
//in this function until execInterpreter gives us an input
nextResponse = new InterpreterResponse(stdOutContents,
stderrContents, false, needInput);
setNextResponse(new InterpreterResponse(stdOutContents,
stderrContents, false, needInput));

//busy loop until we have an input
while(inputReceived == null){
@@ -201,7 +210,7 @@ public void execInterpreter(
final String command,
final ICallback<Object, InterpreterResponse> onResponseReceived,
final ICallback<Object, Tuple<String, String>> onContentsReceived){
nextResponse = null;
setNextResponse(null);
if(waitingForInput){
inputReceived = command;
waitingForInput = false;
@@ -307,12 +316,12 @@ protected IStatus run(IProgressMonitor monitor) {
errorContents += "\n"+stdErrReader.getAndClearContents();
}
stdOutContents = stdOutReader.getAndClearContents();
nextResponse = new InterpreterResponse(stdOutContents,
errorContents, more, needInput);
setNextResponse(new InterpreterResponse(stdOutContents,
errorContents, more, needInput));

}catch(Exception e){
nextResponse = new InterpreterResponse("", "Exception while pushing line to console:"+e.getMessage(),
false, needInput);
setNextResponse(new InterpreterResponse("", "Exception while pushing line to console:"+e.getMessage(),
false, needInput));
}
return Status.OK_STATUS;
}
@@ -474,5 +483,143 @@ public String getDescription(String text) throws Exception {
return client.execute("getDescription", new Object[]{text}).toString();
}

/**
* The Debug Target to notify when the underlying process is suspended or
* running.
*
* @param debugTarget
*/
public void setDebugTarget(IPydevConsoleDebugTarget debugTarget) {
this.debugTarget = debugTarget;
}

/**
* The Debug Target to notify when the underlying process is suspended or
* running.
*/
public IPydevConsoleDebugTarget getDebugTarget() {
return debugTarget;
}

/**
* Common code to handle all cases of setting nextResponse so that the
* attached debug target can be notified of effective state.
*
* @param nextResponse new next response
*/
private void setNextResponse(InterpreterResponse nextResponse) {
this.nextResponse = nextResponse;
updateDebugTarget();
}

/**
* Update the debug target (if non-null) of suspended state of console.
*/
private void updateDebugTarget() {
if (debugTarget != null) {
if (nextResponse == null || nextResponse.need_input == true)
debugTarget.setSuspended(false);
else
debugTarget.setSuspended(true);
}
}

/**
* Request that pydevconsole connect (with pydevd) to the specified port
*
* @param localPort
* port for pydevd to connect to.
* @throws Exception if connection fails
*/
public void connectToDebugger(int localPort) throws Exception {
if(waitingForInput){
throw new Exception("Can't connect debugger now, waiting for input");
}
Object result = client.execute("connectToDebugger", new Object[]{localPort});
Exception exception = null;
if (result instanceof Object[]) {
Object[] resultarray = (Object[])result;
if (resultarray.length == 1) {
if ("connect complete".equals(resultarray[0])) {
return;
}
if (resultarray[0] instanceof String) {
exception = new Exception((String)resultarray[0]);
}
if (resultarray[0] instanceof Exception) {
exception = (Exception)resultarray[0];
}
}
}
throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, "pydevconsole failed to execute connectToDebugger", exception));
}

/**
* Send a debugger command to the pydevconsole's instantiation of pydevd.
*
* It is necessary to use postCommand here as the write path, see {@link PyDebugTargetConsole#postCommand(AbstractDebuggerCommand)}
*
* @param cmd
* @throws Exception
*/
public void postCommand(AbstractDebuggerCommand cmd) throws Exception {
if(waitingForInput){
throw new Exception("Can't connect debugger now, waiting for input");
}
cmd.aboutToSend();
client.execute("postCommand", new Object[]{cmd.getOutgoing()});
}

/**
* Wait for an established connection.
* @param monitor
* @throws Exception if no suitable response is received before the timeout
* @throws UserCanceledException if user cancelled with monitor
*/
public void hello(IProgressMonitor monitor) throws Exception, UserCanceledException {
int maximumAttempts = InteractiveConsolePrefs.getMaximumAttempts();
monitor.beginTask("Establishing Connection To Console Process", maximumAttempts);
try {
if (firstCommWorked) {
return;
}

// We'll do a connection attempt, we can try to
// connect n times (until the 1st time the connection
// is accepted) -- that's mostly because the server may take
// a while to get started.

for (int commAttempts = 0; commAttempts < maximumAttempts; commAttempts++) {
if (monitor.isCanceled())
throw new UserCanceledException("Canceled before hello was successful");
String result = null;
try {
Object[] resulta;
resulta = (Object[]) client.execute("hello", new Object[] { "Hello pydevconsole" });
result = resulta[0].toString();
} catch (XmlRpcException e) {
// We'll retry in a moment
}

if ("Hello eclipse".equals(result)) {
firstCommWorked = true;
break;
}

try {
Thread.sleep(250);
} catch (InterruptedException e) {
// Retry now
}
monitor.worked(1);
}

if (!firstCommWorked) {
throw new Exception("Failed to recive suitable Hello response from pydevconsole");
}
} finally {
monitor.done();
}
}

}
@@ -57,6 +57,9 @@ public final class PydevConsoleConstants {
public static final String INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND = "INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND";
public static final boolean DEFAULT_INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND = true;

public static final String INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW = "INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW";
public static final boolean DEFAULT_INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW = false;

public static final String INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR = "INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR";
public static final boolean DEFAULT_INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR = true;

@@ -7,19 +7,37 @@
package org.python.pydev.debug.newconsole;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.ui.console.IConsoleFactory;
import org.python.pydev.core.IInterpreterInfo;
import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.log.Log;
import org.python.pydev.debug.core.PydevDebugPlugin;
import org.python.pydev.debug.model.PyDebugTargetConsole;
import org.python.pydev.debug.model.remote.ListenConnector;
import org.python.pydev.debug.model.remote.RemoteDebuggerConsole;
import org.python.pydev.debug.newconsole.env.IProcessFactory;
import org.python.pydev.debug.newconsole.env.IProcessFactory.PydevConsoleLaunchInfo;
import org.python.pydev.debug.newconsole.env.JythonEclipseProcess;
import org.python.pydev.debug.newconsole.env.UserCanceledException;
import org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs;
import org.python.pydev.dltk.console.ui.ScriptConsoleManager;
import org.python.pydev.editor.preferences.PydevEditorPrefs;
import org.python.pydev.plugin.preferences.PydevPrefs;

/**
* Could ask to configure the interpreter in the preferences
@@ -42,43 +60,115 @@ public void openConsole() {
}

/**
* @return a new PydevConsole or null if unable to create it (user cancels it)
*/
public PydevConsole createConsole() {
try {
return createConsole(createDefaultPydevInterpreter(), null);
} catch (Exception e) {
Log.log(e);
}
return null;
}
public void createConsole() {
createConsole(null);
}

/**
* @return a new PydevConsole or null if unable to create it (user cancels it)
*/
public PydevConsole createConsole(String additionalInitialComands) {
public void createConsole(String additionalInitialComands) {
try {
return createConsole(createDefaultPydevInterpreter(), additionalInitialComands);
createConsole(createDefaultPydevInterpreter(), additionalInitialComands);
} catch (Exception e) {
Log.log(e);
}
return null;
}

public PydevConsole createConsole(PydevConsoleInterpreter interpreter, String additionalInitialComands) {
ScriptConsoleManager manager = ScriptConsoleManager.getInstance();
try {
if(interpreter != null){
PydevConsole console = new PydevConsole(interpreter, additionalInitialComands);
manager.add(console, true);
return console;
}
} catch (Exception e) {
Log.log(e);
}
return null;

}
public void createConsole(final PydevConsoleInterpreter interpreter, final String additionalInitialComands) {
Job job = new Job("Create Interactive Console") {

@Override
protected IStatus run(IProgressMonitor monitor) {
monitor.beginTask("Create Interactive Console", 10);
try {
ScriptConsoleManager manager = ScriptConsoleManager.getInstance();
monitor.worked(1);
if (interpreter != null) {
final PydevConsole console = new PydevConsole(interpreter, additionalInitialComands);
monitor.worked(1);
createDebugTarget(interpreter, console, new SubProgressMonitor(monitor, 8));
manager.add(console, true);
}
return Status.OK_STATUS;
} catch (UserCanceledException uce) {
return Status.CANCEL_STATUS;
} catch (Exception e) {
return PydevDebugPlugin
.makeStatus(
IStatus.ERROR,
"Unable to connect debugger to Interactive Console\n"
+ "The interactive console will continue to operate without the additional debugger features",
e);
} finally {
monitor.done();
}
}

};
job.setUser(true);
job.schedule();
}

private void createDebugTarget(PydevConsoleInterpreter interpreter, PydevConsole console, IProgressMonitor monitor) throws IOException, CoreException, DebugException, UserCanceledException {
monitor.beginTask("Connect Debug Target", 2);
try {
// Jython within Eclipse does not yet support these new features
Process process = interpreter.getProcess();
if (InteractiveConsolePrefs.getConsoleConnectVariableView() && !(process instanceof JythonEclipseProcess)) {
PyDebugTargetConsole pyDebugTargetConsole = null;
PydevConsoleCommunication consoleCommunication = (PydevConsoleCommunication) interpreter.getConsoleCommunication();
IProcess eclipseProcess = interpreter.getLaunch().getProcesses()[0];
RemoteDebuggerConsole debugger = new RemoteDebuggerConsole();
int acceptTimeout = PydevPrefs.getPreferences().getInt(PydevEditorPrefs.CONNECT_TIMEOUT);
ListenConnector connector = new ListenConnector(acceptTimeout);
debugger.startConnect(connector);
pyDebugTargetConsole = new PyDebugTargetConsole(consoleCommunication, interpreter.getLaunch(),
eclipseProcess, debugger);

Socket socket = null;
try {
consoleCommunication.hello(new SubProgressMonitor(monitor, 1));
consoleCommunication.connectToDebugger(connector.getLocalPort());
socket = debugger.waitForConnect(monitor, process, eclipseProcess);
if (socket == null) {
throw new UserCanceledException("Cancelled");
}
} catch (Exception ex) {
try {
consoleCommunication.close();
debugger.dispose();
} catch (Exception e) {
// Don't hide the more important exception from user
Log.log(e);
}
if (ex instanceof UserCanceledException) {
UserCanceledException userCancelled = (UserCanceledException)ex;
throw userCancelled;
}
String message = "Unexpected error setting up the debugger";
if (ex instanceof SocketTimeoutException)
message = "Timed out after " + Float.toString(acceptTimeout/1000) + " seconds while waiting for python script to connect.";
throw new CoreException(PydevDebugPlugin.makeStatus(IStatus.ERROR, message, ex));
}

pyDebugTargetConsole.startTransmission(socket); // this starts reading/writing from sockets
pyDebugTargetConsole.initialize();

consoleCommunication.setDebugTarget(pyDebugTargetConsole);
interpreter.getLaunch().addDebugTarget(pyDebugTargetConsole);
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
launchManager.addLaunch(interpreter.getLaunch());

pyDebugTargetConsole.setConsole(console);
console.setProcess(pyDebugTargetConsole.getProcess());
pyDebugTargetConsole.finishedInit = true;
}
} finally {
monitor.done();
}
}

/**
* @return A PydevConsoleInterpreter with its communication configured.
@@ -126,6 +216,8 @@ public static PydevConsoleInterpreter createPydevInterpreter(
new PydevConsoleCommunication(port, process, clientPort));
consoleInterpreter.setNaturesUsed(natures);
consoleInterpreter.setInterpreterInfo(interpreterInfo);
consoleInterpreter.setLaunch(launch);
consoleInterpreter.setProcess(process);

PydevDebugPlugin.getDefault().addConsoleLaunch(launch);

@@ -15,6 +15,7 @@
import java.util.Set;
import java.util.TreeSet;

import org.eclipse.debug.core.ILaunch;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
@@ -62,6 +63,10 @@ public class PydevConsoleInterpreter implements IScriptConsoleInterpreter {

private IInterpreterInfo interpreterInfo;

private ILaunch launch;

private Process process;

@SuppressWarnings("unchecked")
public PydevConsoleInterpreter() {
List<Object> p = ExtensionHelper.getParticipants(ExtensionHelper.PYDEV_SIMPLE_ASSIST);
@@ -254,6 +259,9 @@ public void setConsoleCommunication(IScriptConsoleCommunication protocol) {
this.consoleCommunication = protocol;
}

public IScriptConsoleCommunication getConsoleCommunication() {
return consoleCommunication;
}

public void addCloseOperation(Runnable runnable) {
this.closeRunnables.add(runnable);
@@ -274,5 +282,21 @@ public IInterpreterInfo getInterpreterInfo() {
return this.interpreterInfo;
}

public void setLaunch(ILaunch launch) {
this.launch = launch;
}

public ILaunch getLaunch() {
return launch;
}

public void setProcess(Process process) {
this.process = process;
}

public Process getProcess() {
return process;
}


}
@@ -52,6 +52,9 @@ public void initializeDefaultPreferences() {
node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND,
PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND);

node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW,
PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW);

node.putBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR,
PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_SEND_INITIAL_COMMAND_WHEN_CREATED_FROM_EDITOR);
}
@@ -11,10 +11,18 @@

import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.core.Launch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
@@ -29,6 +37,7 @@
import org.python.pydev.core.Tuple;
import org.python.pydev.debug.core.PydevDebugPlugin;
import org.python.pydev.debug.newconsole.PydevConsoleConstants;
import org.python.pydev.debug.newconsole.prefs.InteractiveConsolePrefs;
import org.python.pydev.editor.PyEdit;
import org.python.pydev.plugin.PydevPlugin;
import org.python.pydev.plugin.SocketUtil;
@@ -43,7 +52,7 @@
*/
public class IProcessFactory {

public static final class PydevConsoleLaunchInfo{
public static final class PydevConsoleLaunchInfo{
public final Launch launch;
public final Process process;
public final int clientPort;
@@ -162,7 +171,23 @@ public PydevConsoleLaunchInfo createInteractiveLaunch() throws UserCanceledExcep
return null;
}

public PydevConsoleLaunchInfo createLaunch(
private static ILaunchConfiguration createLaunchConfig() {
ILaunchManager manager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType launchConfigurationType = manager
.getLaunchConfigurationType("org.python.pydev.debug.interactiveConsoleConfigurationType");
ILaunchConfigurationWorkingCopy newInstance;
try {
newInstance = launchConfigurationType
.newInstance(
null,
manager.generateLaunchConfigurationName("PyDev Interactive Launch"));
} catch (CoreException e) {
return null;
}
newInstance.setAttribute(IDebugUIConstants.ATTR_PRIVATE, true);
return newInstance;
}
public PydevConsoleLaunchInfo createLaunch(
IInterpreterManager interpreterManager, IInterpreterInfo interpreter,
Collection<String> pythonpath, IPythonNature nature, List<IPythonNature> naturesUsed) throws Exception {
Process process = null;
@@ -171,12 +196,20 @@ public PydevConsoleLaunchInfo createLaunch(
int port = ports[0];
int clientPort = ports[1];

final Launch launch = new Launch(null, "interactive", null);
final Launch launch = new Launch(createLaunchConfig(), "interactive", null);
launch.setAttribute(DebugPlugin.ATTR_CAPTURE_OUTPUT, "false");
launch.setAttribute(INTERACTIVE_LAUNCH_PORT, ""+port);

File scriptWithinPySrc = PydevPlugin.getScriptWithinPySrc("pydevconsole.py");
String pythonpathEnv = SimpleRunner.makePythonPathEnvFromPaths(pythonpath);
File scriptWithinPySrc = PydevPlugin.getScriptWithinPySrc("pydevconsole.py");
Collection<String> extraPath = pythonpath;
if (InteractiveConsolePrefs.getConsoleConnectVariableView()
&& interpreterManager.getInterpreterType() != IInterpreterManager.INTERPRETER_TYPE_JYTHON_ECLIPSE) {
// Add PydevDebugPlugin's PySrc so we can access pydevd
extraPath = new HashSet<String>();
extraPath.addAll(pythonpath);
extraPath.add(PydevDebugPlugin.getPySrcPath().getAbsolutePath());
}
String pythonpathEnv = SimpleRunner.makePythonPathEnvFromPaths(extraPath);
String[] commandLine;
switch(interpreterManager.getInterpreterType()){

@@ -215,10 +248,10 @@ public PydevConsoleLaunchInfo createLaunch(
pythonpathEnv, interpreter.getExecutableOrJar(), interpreterManager, nature);
process = SimpleRunner.createProcess(commandLine, env, null);
}
PydevSpawnedInterpreterProcess spawnedInterpreterProcess =
new PydevSpawnedInterpreterProcess(process, launch);

launch.addProcess(spawnedInterpreterProcess);
IProcess newProcess = new PydevSpawnedInterpreterProcess(launch, process, interpreter.getNameForUI(), null);

launch.addProcess(newProcess);

return new PydevConsoleLaunchInfo(launch, process, clientPort, interpreter);
}
@@ -6,91 +6,32 @@
*/
package org.python.pydev.debug.newconsole.env;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IStreamsProxy;
import org.eclipse.ui.console.IOConsole;
import org.python.pydev.core.log.Log;
import org.eclipse.debug.core.model.RuntimeProcess;
import org.python.pydev.debug.core.Constants;

/**
* This class defines a process that pydev will spawn for the console.
*/
public class PydevSpawnedInterpreterProcess implements IProcess {

/**
* Boolean determining if this process was already terminated or not.
*/
private boolean terminated;

private Process spawnedInterpreterProcess;
private ILaunch launch;
private HashMap<String, String> attributes;

public PydevSpawnedInterpreterProcess(Process spawnedInterpreterProcess, ILaunch launch){
this.spawnedInterpreterProcess = spawnedInterpreterProcess;
this.launch = launch;
this.attributes = new HashMap<String, String>();
this.setAttribute(IProcess.ATTR_PROCESS_TYPE, Constants.PROCESS_TYPE);
}

/**
* @return the console associated with the run (null in this case)
*/
public IOConsole getIOConsole() {
return null;
}

public String getLabel() {
return "PyDev Interactive Interpreter Process";
}

public ILaunch getLaunch() {
return this.launch;
}

public IStreamsProxy getStreamsProxy() {
return null;
}

public void setAttribute(String key, String value) {
this.attributes.put(key, value);
}

public String getAttribute(String key) {
return this.attributes.get(key);
}

public int getExitValue() throws DebugException {
return 0;
}

public Object getAdapter(Class adapter) {
return null;
}

public boolean canTerminate() {
return true;
}

public boolean isTerminated() {
return terminated;
}

public void terminate() throws DebugException {
try {
if(this.spawnedInterpreterProcess != null){
this.spawnedInterpreterProcess.destroy();
}
} catch (RuntimeException e) {
Log.log(e);
}
this.spawnedInterpreterProcess = null;
terminated = true;
}


public class PydevSpawnedInterpreterProcess extends RuntimeProcess {


public PydevSpawnedInterpreterProcess(ILaunch launch, Process process, String name, Map attributes) {
super(launch, process, name, attributes);
this.setAttribute(IProcess.ATTR_PROCESS_TYPE, Constants.PROCESS_TYPE);
}

/**
* PydevSpawnedInterpreterProcess handles the IO in a custom way, so we don't
* use the streams proxy.
*/
@Override
protected IStreamsProxy createStreamsProxy() {
// do nothing
return null;
}
}
@@ -59,6 +59,9 @@ protected void createFieldEditors() {
addField(new BooleanFieldEditor(PydevConsoleConstants.INTERACTIVE_CONSOLE_FOCUS_ON_SEND_COMMAND,
"Focus console when an evaluate\ncommand is sent from the editor?", BooleanFieldEditor.SEPARATE_LABEL, p));

addField(new BooleanFieldEditor(PydevConsoleConstants.INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW,
"Connect console to Variables Debug View?", BooleanFieldEditor.SEPARATE_LABEL, p));

}

public void init(IWorkbench workbench) {
@@ -93,6 +96,15 @@ public static boolean getFocusConsoleOnSendCommand() {
}
}

public static boolean getConsoleConnectVariableView() {
PydevDebugPlugin plugin = PydevDebugPlugin.getDefault();
if(plugin != null){
return plugin.getPreferenceStore().getBoolean(PydevConsoleConstants.INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW);
}else{
return PydevConsoleConstants.DEFAULT_INTERACTIVE_CONSOLE_CONNECT_VARIABLE_VIEW;
}
}

public static boolean getSendCommandOnCreationFromEditor() {
PydevDebugPlugin plugin = PydevDebugPlugin.getDefault();
if(plugin != null){
@@ -0,0 +1,227 @@
package org.python.pydev.debug.newconsole;

import java.io.File;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.python.pydev.core.REF;
import org.python.pydev.core.TestDependent;
import org.python.pydev.core.Tuple;
import org.python.pydev.core.callbacks.ICallback;
import org.python.pydev.debug.model.AbstractDebugTarget;
import org.python.pydev.debug.model.AbstractDebugTargetWithTransmission;
import org.python.pydev.debug.model.IVariableLocator;
import org.python.pydev.debug.model.PyVariable;
import org.python.pydev.debug.model.PyVariableCollection;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.model.remote.GetFrameCommand;
import org.python.pydev.debug.model.remote.VersionCommand;
import org.python.pydev.dltk.console.InterpreterResponse;
import org.python.pydev.plugin.SocketUtil;
import org.python.pydev.runners.SimpleRunner;

/**
* The purpose of this test is to verify the pydevconsole + pydevd works. This
* test does not try to test the console or debugger itself, just the
* combination and new code paths that the feature introduces.
*
* TODO: Iterate over Jython/Python with and without IPython available.
*
*/
public class PydevConsoleDebugCommsTest extends TestCase {

private PydevConsoleCommunication pydevConsoleCommunication;
private Process process;
private DummyDebugTarget debugTarget;
/** Fake $HOME for IPython */
private File homeDir;

@Override
protected void setUp() throws Exception {
String consoleFile = TestDependent.TEST_PYDEV_PLUGIN_LOC + "PySrc/pydevconsole.py";
String pydevdDir = TestDependent.TEST_PYDEV_DEBUG_PLUGIN_LOC + "pysrc/";
Integer[] ports = SocketUtil.findUnusedLocalPorts(2);
int port = ports[0];
int clientPort = ports[1];

homeDir = REF.getTempFileAt(new File("."), "fake_homedir");
if (homeDir.exists()) {
REF.deleteDirectoryTree(homeDir);
}
homeDir = homeDir.getAbsoluteFile();
homeDir.mkdir();
String[] cmdarray = new String[] { TestDependent.PYTHON_EXE, consoleFile, String.valueOf(port),
String.valueOf(clientPort) };
String[] envp = new String[] { "PYTHONPATH=" + pydevdDir, "HOME=" + homeDir.toString() };
process = SimpleRunner.createProcess(cmdarray, envp, null);
pydevConsoleCommunication = new PydevConsoleCommunication(port, process, clientPort);
pydevConsoleCommunication.hello(new NullProgressMonitor());

ServerSocket socket = new ServerSocket(0);
pydevConsoleCommunication.connectToDebugger(socket.getLocalPort());
socket.setSoTimeout(5000);
Socket accept = socket.accept();

debugTarget = new DummyDebugTarget();
debugTarget.startTransmission(accept);
}

@Override
protected void tearDown() throws Exception {
if (homeDir.exists()) {
REF.deleteDirectoryTree(homeDir);
}
process.destroy();
pydevConsoleCommunication.close();
debugTarget.terminate();

}

private final class CustomGetFrameCommand extends GetFrameCommand {
private final Boolean[] passed;

private CustomGetFrameCommand(Boolean[] passed, AbstractDebugTarget debugger, String locator) {
super(debugger, locator);
this.passed = passed;
}

@Override
public void processErrorResponse(int cmdCode, String payload) {
passed[0] = false;
}

@Override
public void processOKResponse(int cmdCode, String payload) {
super.processOKResponse(cmdCode, payload);
passed[0] = true;
}
}

private class DummyDebugTarget extends AbstractDebugTarget {

@Override
public void processCommand(String sCmdCode, String sSeqCode, String payload) {
System.out.println(sCmdCode + ":" + sSeqCode + ":" + payload);
}

@Override
public IProcess getProcess() {
return null;
}

@Override
public void launchRemoved(ILaunch launch) {
}

@Override
public boolean canTerminate() {
return false;
}

@Override
public boolean isTerminated() {
return false;
}

}

private void waitUntilNonNull(Object[] object) {
for (int i = 0; i < 50; i++) {
if (object[0] != null)
return;
try {
Thread.sleep(250);
} catch (InterruptedException e) {
// Retry now
}
}
Assert.fail("Timed out waiting for non-null");
}

/**
* This test is the basic comms working, send the command down via XML-RPC
* based new method postCommand and receive response back via
* {@link AbstractDebugTargetWithTransmission}
*/
public void testVersion() throws Exception {

final Boolean passed[] = new Boolean[1];
pydevConsoleCommunication.postCommand(new VersionCommand(debugTarget) {
@Override
public void processOKResponse(int cmdCode, String payload) {
if (cmdCode == AbstractDebuggerCommand.CMD_VERSION && "1.1".equals(payload))
passed[0] = true;
else
passed[0] = false;
}

@Override
public void processErrorResponse(int cmdCode, String payload) {
passed[0] = false;
}
});

waitUntilNonNull(passed);
Assert.assertTrue(passed[0]);

}

private void execInterpreter(String command) {
final Boolean done[] = new Boolean[1];
ICallback<Object, InterpreterResponse> onResponseReceived = new ICallback<Object, InterpreterResponse>() {

@Override
public Object call(InterpreterResponse arg) {
done[0] = true;
return null;
}
};
ICallback<Object, Tuple<String, String>> onContentsReceived = new ICallback<Object, Tuple<String, String>>() {

@Override
public Object call(Tuple<String, String> arg) {
return null;
}

};
pydevConsoleCommunication.execInterpreter(command, onResponseReceived, onContentsReceived);
waitUntilNonNull(done);
}

/**
* Test that variables can be seen
*/
public void testVariable() throws Exception {

execInterpreter("my_var=1");

IVariableLocator frameLocator = new IVariableLocator() {
public String getPyDBLocation() {
return "console_main\t0\tFRAME";
}
};

final Boolean passed[] = new Boolean[1];
CustomGetFrameCommand cmd = new CustomGetFrameCommand(passed, debugTarget, frameLocator.getPyDBLocation());
pydevConsoleCommunication.postCommand(cmd);
waitUntilNonNull(passed);
Assert.assertTrue(passed[0]);

PyVariable[] variables = PyVariableCollection.getCommandVariables(cmd, debugTarget, frameLocator);
Map<String, PyVariable> variableMap = new HashMap<String, PyVariable>();
for (PyVariable variable : variables) {
variableMap.put(variable.getName(), variable);
}
Assert.assertTrue(variableMap.containsKey("my_var"));
Assert.assertEquals("int: 1", variableMap.get("my_var").getValueString());
}

}
@@ -1,3 +1,12 @@
try:
# Try to import the packages needed to attach the debugger
import pydevd
import pydevd_vars
from pydevd_comm import PydevQueue
import threading
except:
# This happens on Jython embedded in host eclipse
pydevd = None
from pydev_imports import xmlrpclib
import sys

@@ -245,4 +254,55 @@ def getDescription(self, text):
return ''
except:
import traceback;traceback.print_exc()
return ''
return ''

def _findFrame(self, thread_id, frame_id):
f = FakeFrame()
f.f_locals = self.getNamespace()
f.f_globals = self.getNamespace()
return f

def connectToDebugger(self, debuggerPort):
if pydevd is None:
return ('pydevd is not available, cannot connect',)
import pydev_localhost
threading.currentThread().__pydevd_id__ = "console_main"

pydevd_vars.findFrame = self._findFrame

self.debugger = pydevd.PyDB()
try:
self.debugger.connect(pydev_localhost.get_localhost(), debuggerPort)
except Exception, e:
return ('Failed to connect to target debugger: ' + str(e),)
return ('connect complete',)

def postCommand(self, cmd_str):
if self.debugger is None:
raise Exception('connectToDebugger must be called before postCommand')
try:
args = cmd_str.split('\t', 2)
self.debugger.processNetCommand(int(args[0]), int(args[1]), args[2])
self.debugger._main_lock.acquire()
try:
queue = self.debugger.getInternalQueue("console_main")
try:
while True:
int_cmd = queue.get(False)
if int_cmd.canBeExecutedBy("console_main"):
int_cmd.doIt(self.debugger)

except PydevQueue.Empty: #@UndefinedVariable
pass
finally:
self.debugger._main_lock.release()
except:
import traceback;traceback.print_exc()
return ('',)

def hello(self, input_str):
# Don't care what the input string is
return ("Hello eclipse",)

class FakeFrame:
pass
@@ -1,3 +1,8 @@
try:
import pydevd
except:
# This happens on Jython embedded in host eclipse
pydevd = None
try:
from code import InteractiveConsole
except ImportError:
@@ -93,7 +98,26 @@ class InterpreterInterface(BaseInterpreterInterface):
def __init__(self, host, client_port):
self.client_port = client_port
self.host = host
self.namespace = globals()
if pydevd is None:
self.namespace = globals()
else:
#Adapted from the code in pydevd
#patch provided by: Scott Schlesier - when script is run, it does not
#pretend pydevconsole is not the main module, and
#convince the file to be debugged that it was loaded as main
sys.modules['pydevconsole'] = sys.modules['__main__']
sys.modules['pydevconsole'].__name__ = 'pydevconsole'

from imp import new_module
m = new_module('__main__')
sys.modules['__main__'] = m
m.__file__ = file
ns = m.__dict__
try:
ns['__builtins__'] = __builtins__
except NameError:
pass #Not there on Jython...
self.namespace = ns
self.interpreter = InteractiveConsole(self.namespace)
self._input_error_printed = False

@@ -169,6 +193,9 @@ def StartServer(host, port, client_port):
server.register_function(interpreter.getCompletions)
server.register_function(interpreter.getDescription)
server.register_function(interpreter.close)
server.register_function(interpreter.connectToDebugger)
server.register_function(interpreter.postCommand)
server.register_function(interpreter.hello)
server.serve_forever()

else:
@@ -5,7 +5,6 @@

sys.argv[0] = os.path.dirname(sys.argv[0])
sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0])))

import pydevconsole
from pydev_imports import xmlrpclib, SimpleXMLRPCServer, StringIO

@@ -30,6 +29,19 @@ def tearDown(self):
sys.stdout = self.original_stdout
#print_ ret.getvalue() -- use to see test output

def testConsoleHello(self):
client_port, _server_port = self.getFreeAddresses()
client_thread = self.startClientThread(client_port) #@UnusedVariable
import time
time.sleep(.3) #let's give it some time to start the threads

import pydev_localhost
interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port)

(result,) = interpreter.hello("Hello pydevconsole")
self.assertEqual(result, "Hello eclipse")


def testConsoleRequests(self):
client_port, _server_port = self.getFreeAddresses()
client_thread = self.startClientThread(client_port) #@UnusedVariable
@@ -130,6 +142,26 @@ def RequestInput(self):
return client_thread


def startDebuggerServerThread(self, debugger_port, socket_code):
class DebuggerServerThread(threading.Thread):
def __init__(self, debugger_port, socket_code):
threading.Thread.__init__(self)
self.debugger_port = debugger_port
self.socket_code = socket_code
def run(self):
import socket
s = socket.socket()
s.bind(('',debugger_port))
s.listen(1)
socket, unused_addr = s.accept()
socket_code(socket)

debugger_thread = DebuggerServerThread(debugger_port, socket_code)
debugger_thread.setDaemon(True)
debugger_thread.start()
return debugger_thread


def getFreeAddresses(self):
import socket
s = socket.socket()