From 7c71b78d2fcc8563ced4beac8cb55b54196e26bc Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 2 Sep 2014 13:39:33 +0200 Subject: [PATCH] Show referring objects in Python debugger. --- .../python/debugger/PyDebugValue.java | 41 ++++++- .../python/debugger/PyFrameAccessor.java | 4 + .../debugger/PyReferringObjectsValue.java | 83 ++++++++++++++ .../debugger/pydev/AbstractCommand.java | 3 +- .../debugger/pydev/GetVariableCommand.java | 32 ++++-- .../debugger/pydev/MultiProcessDebugger.java | 12 +- .../debugger/pydev/ProcessDebugger.java | 17 +-- .../python/debugger/pydev/ProtocolParser.java | 26 +++++ .../debugger/pydev/PyDebugCallback.java | 12 ++ .../debugger/pydev/PyVariableLocator.java | 17 +++ .../python/debugger/pydev/RemoteDebugger.java | 67 ++++++++++- .../pydev/RunCustomOperationCommand.java | 108 ++++++++++++++++++ .../console/PydevConsoleCommunication.java | 10 +- .../PythonDebugConsoleCommunication.java | 8 +- .../python/debugger/PyDebugProcess.java | 14 ++- .../env/python/PythonDebuggerTest.java | 4 +- 16 files changed, 413 insertions(+), 45 deletions(-) create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/PyDebugCallback.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/PyVariableLocator.java create mode 100644 python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index 69b64827c40f..7320f2706b18 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java @@ -5,6 +5,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.xdebugger.frame.XNamedValue; import com.intellij.xdebugger.frame.*; +import com.jetbrains.python.debugger.pydev.PyVariableLocator; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -21,15 +22,14 @@ public class PyDebugValue extends XNamedValue { private final String myValue; private final boolean myContainer; private final PyDebugValue myParent; + private String myId = null; - private final PyFrameAccessor myFrameAccessor; + protected final PyFrameAccessor myFrameAccessor; + + private PyVariableLocator myVariableLocator; private final boolean myErrorOnEval; - public PyDebugValue(@NotNull final String name, final String type, final String value, final boolean container, boolean errorOnEval) { - this(name, type, value, container, errorOnEval, null, null); - } - public PyDebugValue(@NotNull final String name, final String type, final String value, final boolean container, boolean errorOnEval, final PyFrameAccessor frameAccessor) { this(name, type, value, container, errorOnEval, null, frameAccessor); @@ -179,4 +179,35 @@ public class PyDebugValue extends XNamedValue { public PyDebugValue setName(String newName) { return new PyDebugValue(newName, myType, myValue, myContainer, myErrorOnEval, myParent, myFrameAccessor); } + + @Nullable + @Override + public XReferrersProvider getReferrersProvider() { + return new XReferrersProvider() { + @Override + public XValue getReferringObjectsValue() { + return new PyReferringObjectsValue(PyDebugValue.this); + } + }; + } + + public PyFrameAccessor getFrameAccessor() { + return myFrameAccessor; + } + + public PyVariableLocator getVariableLocator() { + return myVariableLocator; + } + + public void setVariableLocator(PyVariableLocator variableLocator) { + myVariableLocator = variableLocator; + } + + public String getId() { + return myId; + } + + public void setId(String id) { + myId = id; + } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java b/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java index 530035e78a4b..15a08b301df5 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java @@ -1,6 +1,8 @@ package com.jetbrains.python.debugger; +import com.intellij.util.Consumer; import com.intellij.xdebugger.frame.XValueChildrenList; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; import org.jetbrains.annotations.Nullable; /** @@ -17,4 +19,6 @@ public interface PyFrameAccessor { XValueChildrenList loadVariable(PyDebugValue var) throws PyDebuggerException; void changeVariable(PyDebugValue variable, String expression) throws PyDebuggerException; + + void loadReferrers(PyReferringObjectsValue value, PyDebugCallback callback); } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java new file mode 100644 index 000000000000..165cb6448641 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java @@ -0,0 +1,83 @@ +/* + * 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 com.intellij.openapi.diagnostic.Logger; +import com.intellij.xdebugger.frame.XCompositeNode; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; +import org.jetbrains.annotations.NotNull; + +public class PyReferringObjectsValue extends PyDebugValue { + private static final Logger LOG = Logger.getInstance(PyReferringObjectsValue.class); + + //private final boolean myIsField; + + private String myId; + private String myFoundAs; + + public PyReferringObjectsValue(@NotNull String name, + String type, + String value, + boolean container, boolean errorOnEval, PyFrameAccessor frameAccessor) { + super(name, type, value, container, errorOnEval, frameAccessor); + } + + public PyReferringObjectsValue(PyDebugValue debugValue) { + this(debugValue.getName(), debugValue.getType(), debugValue.getValue(), debugValue.isContainer(), debugValue.isErrorOnEval(), debugValue.getFrameAccessor()); + //myIsField = isField; + } + + @Override + public boolean canNavigateToSource() { + return true; + } + + @Override + public void computeChildren(@NotNull final XCompositeNode node) { + if (node.isObsolete()) return; + + myFrameAccessor.loadReferrers(this, new PyDebugCallback() { + @Override + public void ok(XValueChildrenList value) { + if (!node.isObsolete()) { + node.addChildren(value, true); + } + } + + @Override + public void error(PyDebuggerException e) { + if (!node.isObsolete()) { + node.setErrorMessage("Unable to display children:" + e.getMessage()); + } + LOG.warn(e); + } + }); + } + + public boolean isField() { + return false; //TODO + } + + public String getId() { + return myId; + } + + public void setReferrerFoundInfo(String id, String foundAs) { + myId = id; + myFoundAs = foundAs; + } +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java index 15bb76b6c3d1..b2798fb05fdf 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java @@ -33,6 +33,7 @@ public abstract class AbstractCommand { public static final int SMART_STEP_INTO = 128; public static final int EXIT = 129; public static final int CALL_SIGNATURE_TRACE = 130; + public static final int CMD_RUN_CUSTOM_OPERATION = 135; public static final int SHOW_CONSOLE = 142; public static final int ERROR = 901; @@ -107,7 +108,7 @@ public abstract class AbstractCommand { } } - public void execute(final ProcessDebugger.DebugCallback callback) { + public void execute(final PyDebugCallback callback) { final int sequence = myDebugger.getNextSequence(); final ResponseProcessor processor = getResponseProcessor(); diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java index ef4bd12059d7..e5a218607ac8 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java @@ -5,6 +5,7 @@ import com.jetbrains.python.debugger.PyDebugValue; public class GetVariableCommand extends GetFrameCommand { + public static final String BY_ID = "BY_ID"; private final String myVariableName; private final PyDebugValue myParent; @@ -15,23 +16,40 @@ public class GetVariableCommand extends GetFrameCommand { } public static String composeName(final PyDebugValue var) { - final StringBuilder sb = new StringBuilder(var.getTempName()); + final StringBuilder sb = new StringBuilder(); PyDebugValue p = var; - while ((p = p.getParent()) != null) { - sb.insert(0, '\t').insert(0, p.getTempName()); + while (p != null) { + if (sb.length() > 0 ) { + sb.insert(0, '\t'); + } + if (p.getId() != null) { + sb.insert(0, BY_ID).insert(0, '\t').insert(0, p.getId()); + break; + } else { + sb.insert(0, p.getTempName()); + } + p = p.getParent(); } return sb.toString(); } @Override protected void buildPayload(Payload payload) { - super.buildPayload(payload); - payload.add(myVariableName); + if (myParent.getVariableLocator() != null) { + payload.add(myParent.getVariableLocator().getThreadId()).add(myParent.getVariableLocator().getPyDBLocation()); + } + else if (myVariableName.contains(BY_ID)) { + payload.add(getThreadId()).add(myVariableName); + } + else { + super.buildPayload(payload); + payload.add(myVariableName); + } } @Override protected PyDebugValue extend(final PyDebugValue value) { - return new PyDebugValue(value.getName(), value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(), myParent, myDebugProcess); + return new PyDebugValue(value.getName(), value.getType(), value.getValue(), value.isContainer(), value.isErrorOnEval(), myParent, + myDebugProcess); } - } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java index d210d7ae03a8..d54bac8de85f 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/MultiProcessDebugger.java @@ -8,10 +8,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.xdebugger.frame.XValueChildrenList; import com.jetbrains.python.console.pydev.PydevCompletionVariant; -import com.jetbrains.python.debugger.IPyDebugProcess; -import com.jetbrains.python.debugger.PyDebugValue; -import com.jetbrains.python.debugger.PyDebuggerException; -import com.jetbrains.python.debugger.PyThreadInfo; +import com.jetbrains.python.debugger.*; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -168,7 +165,7 @@ public class MultiProcessDebugger implements ProcessDebugger { } @Override - public void consoleExec(String threadId, String frameId, String expression, DebugCallback callback) { + public void consoleExec(String threadId, String frameId, String expression, PyDebugCallback callback) { debugger(threadId).consoleExec(threadId, frameId, expression, callback); } @@ -182,6 +179,11 @@ public class MultiProcessDebugger implements ProcessDebugger { return debugger(threadId).loadVariable(threadId, frameId, var); } + @Override + public void loadReferrers(String threadId, String frameId, PyReferringObjectsValue var, PyDebugCallback callback) { + debugger(threadId).loadReferrers(threadId, frameId, var, callback); + } + @NotNull private ProcessDebugger debugger(@NotNull String threadId) { ProcessDebugger debugger = myThreadRegistry.getDebugger(threadId); diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java index 2d864f0df2ca..0fa5a5c21931 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProcessDebugger.java @@ -4,6 +4,7 @@ import com.intellij.xdebugger.frame.XValueChildrenList; import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.PyDebugValue; import com.jetbrains.python.debugger.PyDebuggerException; +import com.jetbrains.python.debugger.PyReferringObjectsValue; import com.jetbrains.python.debugger.PyThreadInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -26,15 +27,17 @@ public interface ProcessDebugger { String expression, boolean execute, boolean trimResult) - throws PyDebuggerException; + throws PyDebuggerException; - void consoleExec(String threadId, String frameId, String expression, DebugCallback callback); + void consoleExec(String threadId, String frameId, String expression, PyDebugCallback callback); XValueChildrenList loadFrame(String threadId, String frameId) throws PyDebuggerException; // todo: don't generate temp variables for qualified expressions - just split 'em XValueChildrenList loadVariable(String threadId, String frameId, PyDebugValue var) throws PyDebuggerException; + void loadReferrers(String threadId, String frameId, PyReferringObjectsValue var, PyDebugCallback callback); + PyDebugValue changeVariable(String threadId, String frameId, PyDebugValue var, String value) throws PyDebuggerException; @@ -50,7 +53,7 @@ public interface ProcessDebugger { void suspendThread(String threadId); /** - * Disconnects current debug process. Closes all resources. + * Disconnects current debug process. Closes all resources. */ void close(); @@ -84,12 +87,4 @@ public interface ProcessDebugger { void addExceptionBreakpoint(ExceptionBreakpointCommandFactory factory); void removeExceptionBreakpoint(ExceptionBreakpointCommandFactory factory); - - /** - * @author traff - */ - interface DebugCallback { - void ok(T value); - void error(PyDebuggerException exception); - } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java index 0801077fbea3..1822b17efaa3 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolParser.java @@ -121,6 +121,32 @@ public class ProtocolParser { return parseValue(reader, frameAccessor); } + @NotNull + public static List parseReferrers(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException { + final List values = new LinkedList(); + + final XppReader reader = openReader(text, false); + + while (reader.hasMoreChildren()) { + reader.moveDown(); + if (reader.getNodeName().equals("var")) { + PyDebugValue value = parseValue(reader, frameAccessor); + value.setId(readString(reader, "id", null)); + values.add(value); + } + else if (reader.getNodeName().equals("for")) { + //TODO + } + else { + throw new PyDebuggerException("Expected or , found " + reader.getNodeName()); + } + reader.moveUp(); + } + + return values; + } + + @NotNull public static List parseValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException { final List values = new LinkedList(); diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyDebugCallback.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyDebugCallback.java new file mode 100644 index 000000000000..c78f3e20bdc5 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyDebugCallback.java @@ -0,0 +1,12 @@ +package com.jetbrains.python.debugger.pydev; + +import com.jetbrains.python.debugger.PyDebuggerException; + +/** + * @author traff + */ +public interface PyDebugCallback { + void ok(T value); + + void error(PyDebuggerException exception); +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyVariableLocator.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyVariableLocator.java new file mode 100644 index 000000000000..3ba4e1cd96de --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/PyVariableLocator.java @@ -0,0 +1,17 @@ +package com.jetbrains.python.debugger.pydev; + +/** + * IVariableLocator knows how to produce location information + * for CMD_GET_VARIABLE + * + * The location is specified as: + * + * thread_id, stack_frame, LOCAL|GLOBAL, attribute* + */ +public interface PyVariableLocator { + + public String getThreadId(); + + public String getPyDBLocation(); + +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java index df33148edb10..11e4cfe44010 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoteDebugger.java @@ -133,7 +133,7 @@ public class RemoteDebugger implements ProcessDebugger { } @Override - public void consoleExec(String threadId, String frameId, String expression, DebugCallback callback) { + public void consoleExec(String threadId, String frameId, String expression, PyDebugCallback callback) { final ConsoleExecCommand command = new ConsoleExecCommand(this, threadId, frameId, expression); command.execute(callback); } @@ -154,6 +154,65 @@ public class RemoteDebugger implements ProcessDebugger { return command.getVariables(); } + + @Override + public void loadReferrers(final String threadId, + final String frameId, + final PyReferringObjectsValue var, + final PyDebugCallback callback) { + RunCustomOperationCommand cmd = new RunCustomOperationCommand>(this, createVariableLocator(threadId, frameId, var), + "from pydevd_referrers import get_referrer_info", + "get_referrer_info") { + + @Override + protected ResponseProcessor> createResponseProcessor() { + return new ResponseProcessor>() { + @Override + protected List parseResponse(ProtocolFrame response) throws PyDebuggerException { + return ProtocolParser.parseReferrers(decode(response.getPayload()), RemoteDebugger.this.getDebugProcess()); + } + }; + } + }; + cmd.execute(new PyDebugCallback>() { + @Override + public void ok(List value) { + XValueChildrenList list = new XValueChildrenList(); + for (PyDebugValue v: value) { + list.add(v); + } + callback.ok(list); + } + + @Override + public void error(PyDebuggerException exception) { + callback.error(exception); + } + }); + } + + private PyVariableLocator createVariableLocator(final String threadId, final String frameId, final PyReferringObjectsValue var) { + return new PyVariableLocator() { + @Override + public String getThreadId() { + return threadId; + } + + + @Override + public String getPyDBLocation() { + if (var.getId() == null) { + return threadId + "\t" + frameId + "\tFRAME\t" + var.getName(); + } + //Ok, this only happens when we're dealing with references with no proper scope given and we need to get + //things by id (which is usually not ideal). In this case we keep the proper thread id and set the frame id + //as the id of the object to be searched later on based on the list of all alive objects. + return getThreadId() + "\t" + var.getId() + "\tBY_ID"; + } + }; + } + + @Override public PyDebugValue changeVariable(final String threadId, final String frameId, final PyDebugValue var, final String value) throws PyDebuggerException { @@ -475,10 +534,10 @@ public class RemoteDebugger implements ProcessDebugger { else if (AbstractCommand.isCallSignatureTrace(frame.getCommand())) { recordCallSignature(ProtocolParser.parseCallSignature(frame.getPayload())); } - else if (AbstractCommand.isErrorEvent(frame.getCommand())) { - LOG.error("Error response from debugger: " + frame.getPayload()); - } else { + if (AbstractCommand.isErrorEvent(frame.getCommand())) { + LOG.error("Error response from debugger: " + frame.getPayload()); + } placeResponse(frame.getSequence(), frame); } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java new file mode 100644 index 000000000000..622330cba4f1 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java @@ -0,0 +1,108 @@ +package com.jetbrains.python.debugger.pydev; + +import com.intellij.openapi.diagnostic.Logger; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; + + +/** + * Run a custom bit of Python in the context of the specified debug target. + *

+ * This command takes a variable or expression (expressed as an {@link PyVariableLocator#getPyDBLocation()} style + * location) and passes it to the function provided in the constructor. The constructor also takes either a code + * snippet that should define the function, or a file to execfile that should define the function. + *

+ * Once created, the command should be posted to the target with {@link AbstractDebugTarget#postCommand(AbstractDebuggerCommand)}. + * Optionally, the function run on the target can return a string for further processing. In this case the command's + * {@link #setCompletionListener(ICommandResponseListener)} should be set and on completion, {@link #getResponsePayload()} + * can be used to obtain the returned value. + *

+ * For an example, see {@link PrettyPrintCommandHandler} + */ +public class RunCustomOperationCommand extends AbstractCommand { + private static final Logger LOG = Logger.getInstance(RunCustomOperationCommand.class); + + private String encodedCodeOrFile; + private String operationFnName; + private PyVariableLocator locator; + private String style; + private String responsePayload; + + private RunCustomOperationCommand(RemoteDebugger target, PyVariableLocator locator, + String style, String codeOrFile, String operationFnName) { + super(target, CMD_RUN_CUSTOM_OPERATION); + + this.locator = locator; + this.style = style; + this.encodedCodeOrFile = encode(codeOrFile); + this.operationFnName = operationFnName; + } + + /** + * Create a new command to run with the function defined in a string. + * + * @param target Debug Target to run on + * @param locator Location of variable or expression. + * @param operationSource Definition of the function to be run (this code is "exec"ed by the target) + * @param operationFnName Function to call, must be defined by operationSource + */ + public RunCustomOperationCommand(RemoteDebugger target, PyVariableLocator locator, + String operationSource, String operationFnName) { + this(target, locator, "EXEC", operationSource, operationFnName); + } + + /** + * Create a new command to run with the function defined in a file. + * + * @param target Debug Target to run on + * @param locator Location of variable or expression. + * @param operationPyFile Definition of the function to be run (this file is "execfile"d by the target) + * @param operationFnName Function to call, must be defined by operationSource + */ + public RunCustomOperationCommand(RemoteDebugger target, PyVariableLocator locator, + File operationPyFile, String operationFnName) { + this(target, locator, "EXECFILE", operationPyFile.toString(), operationFnName); + } + + + @Override + protected void buildPayload(Payload payload) { + payload.add(locator.getPyDBLocation() + "||" + style).add(encodedCodeOrFile).add(operationFnName); + } + + @Override + public boolean isResponseExpected() { + return true; + } + + /** + * Return the response received from the custom command + * @return the response or null if an error or no response has been received. + */ + public String getResponsePayload() { + return responsePayload; + } + + private static String encode(String in) { + try { + return URLEncoder.encode(in, "UTF-8"); + } catch (UnsupportedEncodingException e) { + LOG.error("Unreachable? UTF-8 is always supported.", e); + return ""; + } + } + + protected static String decode(String in) { + try { + return URLDecoder.decode(in, "UTF-8"); + } catch (UnsupportedEncodingException e) { + LOG.error("Unreachable? UTF-8 is always supported.", e); + return ""; + } + } + +} + diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index 9c95b1cffda8..a295fd9e5e25 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -31,12 +31,10 @@ import com.intellij.util.Function; import com.intellij.xdebugger.frame.XValueChildrenList; import com.jetbrains.python.console.parsing.PythonConsoleData; import com.jetbrains.python.console.pydev.*; -import com.jetbrains.python.debugger.PyDebugValue; -import com.jetbrains.python.debugger.PyDebuggerException; -import com.jetbrains.python.debugger.PyFrameAccessor; -import com.jetbrains.python.debugger.PydevXmlUtils; +import com.jetbrains.python.debugger.*; import com.jetbrains.python.debugger.pydev.GetVariableCommand; import com.jetbrains.python.debugger.pydev.ProtocolParser; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; import org.apache.xmlrpc.WebServer; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.XmlRpcHandler; @@ -528,6 +526,10 @@ public class PydevConsoleCommunication extends AbstractConsoleCommunication impl } } + @Override + public void loadReferrers(PyReferringObjectsValue value, PyDebugCallback callback) { + + } /** * Request that pydevconsole connect (with pydevd) to the specified port diff --git a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java index da1ac53113ef..5f361b30228a 100644 --- a/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PythonDebugConsoleCommunication.java @@ -23,7 +23,7 @@ import com.jetbrains.python.console.pydev.InterpreterResponse; import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.PyDebugProcess; import com.jetbrains.python.debugger.PyDebuggerException; -import com.jetbrains.python.debugger.pydev.ProcessDebugger; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -63,8 +63,8 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio return false; } - protected void exec(final ConsoleCodeFragment command, final ProcessDebugger.DebugCallback> callback) { - myDebugProcess.consoleExec(command.getText(), new ProcessDebugger.DebugCallback() { + protected void exec(final ConsoleCodeFragment command, final PyDebugCallback> callback) { + myDebugProcess.consoleExec(command.getText(), new PyDebugCallback() { @Override public void ok(String value) { callback.ok(parseExecResponseString(value)); @@ -79,7 +79,7 @@ public class PythonDebugConsoleCommunication extends AbstractConsoleCommunicatio public void execInterpreter(ConsoleCodeFragment code, final Function callback) { myExpression.append(code.getText()); - exec(new ConsoleCodeFragment(myExpression.toString(), false), new ProcessDebugger.DebugCallback>() { + exec(new ConsoleCodeFragment(myExpression.toString(), false), new PyDebugCallback>() { @Override public void ok(Pair executed) { boolean more = executed.second; diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index cdea41298a4e..f169dd47e5e3 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -17,7 +17,6 @@ package com.jetbrains.python.debugger; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.intellij.execution.console.DuplexConsoleView; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessListener; @@ -489,7 +488,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr return myDebugger.evaluate(frame.getThreadId(), frame.getFrameId(), expression, execute, trimResult); } - public void consoleExec(String command, ProcessDebugger.DebugCallback callback) { + public void consoleExec(String command, PyDebugCallback callback) { dropFrameCaches(); try { final PyStackFrame frame = currentFrame(); @@ -538,6 +537,17 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr return myDebugger.loadVariable(frame.getThreadId(), frame.getFrameId(), var); } + @Override + public void loadReferrers(PyReferringObjectsValue var, PyDebugCallback callback) { + try { + final PyStackFrame frame = currentFrame(); + myDebugger.loadReferrers(frame.getThreadId(), frame.getFrameId(), var, callback); + } + catch (PyDebuggerException e) { + callback.error(e); + } + } + @Override public void changeVariable(final PyDebugValue var, final String value) throws PyDebuggerException { final PyStackFrame frame = currentFrame(); diff --git a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java index 4372715371eb..10f6fd552b2d 100644 --- a/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java +++ b/python/testSrc/com/jetbrains/env/python/PythonDebuggerTest.java @@ -13,7 +13,7 @@ import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.PyDebuggerException; import com.jetbrains.python.debugger.PyExceptionBreakpointProperties; import com.jetbrains.python.debugger.PyExceptionBreakpointType; -import com.jetbrains.python.debugger.pydev.ProcessDebugger; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import java.util.List; @@ -128,7 +128,7 @@ public class PythonDebuggerTest extends PyEnvTestCase { } private void consoleExec(String command) { - myDebugProcess.consoleExec(command, new ProcessDebugger.DebugCallback() { + myDebugProcess.consoleExec(command, new PyDebugCallback() { @Override public void ok(String value) {