diff options
Diffstat (limited to 'python/pydevSrc/com/jetbrains/python/debugger')
18 files changed, 429 insertions, 45 deletions
diff --git a/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java b/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java index ab6ccfab0bc6..762225f18f12 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/IPyDebugProcess.java @@ -1,6 +1,8 @@ package com.jetbrains.python.debugger; import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; import java.io.IOException; @@ -12,7 +14,7 @@ public interface IPyDebugProcess extends PyFrameAccessor { void threadSuspended(PyThreadInfo thread); - boolean isVariable(String name); + boolean canSaveToTemp(String name); void threadResumed(PyThreadInfo thread); @@ -25,4 +27,6 @@ public interface IPyDebugProcess extends PyFrameAccessor { void recordSignature(PySignature signature); void showConsole(PyThreadInfo thread); + + void loadReferrers(PyReferringObjectsValue var, PyDebugCallback<XValueChildrenList> callback); } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java b/python/pydevSrc/com/jetbrains/python/debugger/PyDebugValue.java index 69b64827c40f..ddec6797ea8d 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,14 +22,13 @@ 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; - private final boolean myErrorOnEval; + private PyVariableLocator myVariableLocator; - 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); - } + private final boolean myErrorOnEval; public PyDebugValue(@NotNull final String name, final String type, final String value, final boolean container, boolean errorOnEval, final PyFrameAccessor frameAccessor) { @@ -97,7 +97,7 @@ public class PyDebugValue extends XNamedValue { myParent.buildExpression(result); if (("dict".equals(myParent.getType()) || "list".equals(myParent.getType()) || "tuple".equals(myParent.getType())) && !isLen(myName)) { - result.append('[').append(removeId(myName)).append(']'); + result.append('[').append(removeLeadingZeros(removeId(myName))).append(']'); } else if (("set".equals(myParent.getType())) && !isLen(myName)) { //set doesn't support indexing @@ -105,6 +105,9 @@ public class PyDebugValue extends XNamedValue { else if (isLen(myName)) { result.append('.').append(myName).append("()"); } + else if (("ndarray".equals(myParent.getType()) || "matrix".equals(myParent.getType())) && myName.startsWith("[")) { + result.append(removeLeadingZeros(myName)); + } else { result.append('.').append(myName); } @@ -119,6 +122,11 @@ public class PyDebugValue extends XNamedValue { return name; } + private static String removeLeadingZeros(@NotNull String name) { + //bugs.python.org/issue15254: "0" prefix for octal + return name.replaceFirst("^0+(?!$)", ""); + } + private static boolean isLen(String name) { return "__len__".equals(name); } @@ -179,4 +187,39 @@ 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() { + if (myFrameAccessor.getReferrersLoader() != null) { + return new XReferrersProvider() { + @Override + public XValue getReferringObjectsValue() { + return new PyReferringObjectsValue(PyDebugValue.this); + } + }; + } else { + return null; + } + } + + 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..01666456f420 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyFrameAccessor.java @@ -17,4 +17,7 @@ public interface PyFrameAccessor { XValueChildrenList loadVariable(PyDebugValue var) throws PyDebuggerException; void changeVariable(PyDebugValue variable, String expression) throws PyDebuggerException; + + @Nullable + PyReferrersLoader getReferrersLoader(); } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyReferrersLoader.java b/python/pydevSrc/com/jetbrains/python/debugger/PyReferrersLoader.java new file mode 100644 index 000000000000..edb2f641b192 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyReferrersLoader.java @@ -0,0 +1,20 @@ +package com.jetbrains.python.debugger; + +import com.intellij.xdebugger.frame.XReferrersProvider; +import com.intellij.xdebugger.frame.XValueChildrenList; +import com.jetbrains.python.debugger.pydev.PyDebugCallback; + +/** + * @author traff + */ +public class PyReferrersLoader { + private final IPyDebugProcess myProcess; + + public PyReferrersLoader(IPyDebugProcess process) { + myProcess = process; + } + + public void loadReferrers(PyReferringObjectsValue value, PyDebugCallback<XValueChildrenList> callback) { + myProcess.loadReferrers(value, 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..283653659d0d --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyReferringObjectsValue.java @@ -0,0 +1,71 @@ +/* + * 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 @NotNull PyReferrersLoader myReferrersLoader; + + public PyReferringObjectsValue(@NotNull String name, + String type, + String value, + boolean container, boolean errorOnEval, @NotNull PyFrameAccessor frameAccessor) { + super(name, type, value, container, errorOnEval, frameAccessor); + myReferrersLoader = frameAccessor.getReferrersLoader(); + } + + public PyReferringObjectsValue(PyDebugValue debugValue) { + this(debugValue.getName(), debugValue.getType(), debugValue.getValue(), debugValue.isContainer(), debugValue.isErrorOnEval(), debugValue.getFrameAccessor()); + } + + @Override + public boolean canNavigateToSource() { + return true; + } + + @Override + public void computeChildren(@NotNull final XCompositeNode node) { + if (node.isObsolete()) return; + + myReferrersLoader.loadReferrers(this, new PyDebugCallback<XValueChildrenList>() { + @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 + } +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyThreadInfo.java b/python/pydevSrc/com/jetbrains/python/debugger/PyThreadInfo.java index c01be15384ab..7fffc636a9fb 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyThreadInfo.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyThreadInfo.java @@ -80,7 +80,7 @@ public class PyThreadInfo { } public boolean isExceptionBreak() { - return myStopReason == AbstractCommand.ADD_EXCEPTION_BREAKPOINT || myStopReason == AbstractCommand.ADD_DJANGO_EXCEPTION_BREAKPOINT; + return myStopReason == AbstractCommand.ADD_EXCEPTION_BREAKPOINT; } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/PyTypeHandler.java b/python/pydevSrc/com/jetbrains/python/debugger/PyTypeHandler.java index 6db612488d4b..f882aae4d0e7 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/PyTypeHandler.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/PyTypeHandler.java @@ -37,6 +37,9 @@ public class PyTypeHandler { FORMATTERS = new HashMap<String, Formatter>(); FORMATTERS.put("str", STR_FORMATTER); FORMATTERS.put("unicode", UNI_FORMATTER); + //numpy types + FORMATTERS.put("string_", STR_FORMATTER); + FORMATTERS.put("unicode_", UNI_FORMATTER); } private PyTypeHandler() { } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java index 15bb76b6c3d1..d13addaade04 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java @@ -28,11 +28,10 @@ public abstract class AbstractCommand<T> { public static final int ADD_EXCEPTION_BREAKPOINT = 122; public static final int REMOVE_EXCEPTION_BREAKPOINT = 123; public static final int LOAD_SOURCE = 124; - public static final int ADD_DJANGO_EXCEPTION_BREAKPOINT = 125; - public static final int REMOVE_DJANGO_EXCEPTION_BREAKPOINT = 126; 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; @@ -41,7 +40,7 @@ public abstract class AbstractCommand<T> { public static final String TAB_CHAR = "@_@TAB_CHAR@_@"; - @NotNull protected final RemoteDebugger myDebugger; + @NotNull private final RemoteDebugger myDebugger; private final int myCommandCode; private final ResponseProcessor<T> myResponseProcessor; @@ -107,7 +106,7 @@ public abstract class AbstractCommand<T> { } } - public void execute(final ProcessDebugger.DebugCallback<T> callback) { + public void execute(final PyDebugCallback<T> callback) { final int sequence = myDebugger.getNextSequence(); final ResponseProcessor<T> processor = getResponseProcessor(); @@ -186,6 +185,11 @@ public abstract class AbstractCommand<T> { return command == ERROR; } + @NotNull + public RemoteDebugger getDebugger() { + return myDebugger; + } + protected static class Payload { private final StringBuilder myBuilder = new StringBuilder(); private static final char SEPARATOR = '\t'; diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java index 14d8c084a39d..c03890f088e8 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java @@ -28,7 +28,7 @@ public class ConsoleExecCommand extends AbstractFrameCommand<String> { return new ResponseProcessor<String>() { @Override protected String parseResponse(ProtocolFrame response) throws PyDebuggerException { - final PyDebugValue value = ProtocolParser.parseValue(response.getPayload(), myDebugger.getDebugProcess()); + final PyDebugValue value = ProtocolParser.parseValue(response.getPayload(), getDebugger().getDebugProcess()); return value.getValue(); } }; diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetReferrersCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetReferrersCommand.java new file mode 100644 index 000000000000..3698266f66b7 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetReferrersCommand.java @@ -0,0 +1,50 @@ +package com.jetbrains.python.debugger.pydev; + +import com.jetbrains.python.debugger.PyDebugValue; +import com.jetbrains.python.debugger.PyDebuggerException; +import com.jetbrains.python.debugger.PyReferringObjectsValue; + +import java.util.List; + +/** + * @author traff + */ +public class GetReferrersCommand extends RunCustomOperationCommand<List<PyDebugValue>> { + + public GetReferrersCommand(RemoteDebugger target, String threadId, String frameId, PyReferringObjectsValue value) { + super(target, createVariableLocator(threadId, frameId, value), "from pydevd_referrers import get_referrer_info", + "get_referrer_info"); + } + + @Override + protected ResponseProcessor<List<PyDebugValue>> createResponseProcessor() { + return new ResponseProcessor<List<PyDebugValue>>() { + @Override + protected List<PyDebugValue> parseResponse(ProtocolFrame response) throws PyDebuggerException { + return ProtocolParser.parseReferrers(decode(response.getPayload()), getDebugger().getDebugProcess()); + } + }; + } + + + private static 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"; + } + }; + } +} 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<String> callback) { + public void consoleExec(String threadId, String frameId, String expression, PyDebugCallback<String> 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<XValueChildrenList> 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<String> callback); + void consoleExec(String threadId, String frameId, String expression, PyDebugCallback<String> 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<XValueChildrenList> 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<T> { - 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 @@ -122,6 +122,32 @@ public class ProtocolParser { } @NotNull + public static List<PyDebugValue> parseReferrers(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException { + final List<PyDebugValue> values = new LinkedList<PyDebugValue>(); + + 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 <var> or <for>, found " + reader.getNodeName()); + } + reader.moveUp(); + } + + return values; + } + + + @NotNull public static List<PyDebugValue> parseValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException { final List<PyDebugValue> values = new LinkedList<PyDebugValue>(); 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<T> { + 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..4ce7bdf67434 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<String> callback) { + public void consoleExec(String threadId, String frameId, String expression, PyDebugCallback<String> callback) { final ConsoleExecCommand command = new ConsoleExecCommand(this, threadId, frameId, expression); command.execute(callback); } @@ -154,6 +154,31 @@ public class RemoteDebugger implements ProcessDebugger { return command.getVariables(); } + + @Override + public void loadReferrers(final String threadId, + final String frameId, + final PyReferringObjectsValue var, + final PyDebugCallback<XValueChildrenList> callback) { + RunCustomOperationCommand cmd = new GetReferrersCommand(this, threadId, frameId, var); + + cmd.execute(new PyDebugCallback<List<PyDebugValue>>() { + @Override + public void ok(List<PyDebugValue> value) { + XValueChildrenList list = new XValueChildrenList(); + for (PyDebugValue v : value) { + list.add(v); + } + callback.ok(list); + } + + @Override + public void error(PyDebuggerException exception) { + callback.error(exception); + } + }); + } + @Override public PyDebugValue changeVariable(final String threadId, final String frameId, final PyDebugValue var, final String value) throws PyDebuggerException { @@ -191,7 +216,7 @@ public class RemoteDebugger implements ProcessDebugger { // todo: change variable in lists doesn't work - either fix in pydevd or format var name appropriately private void setTempVariable(final String threadId, final String frameId, final PyDebugValue var) { final PyDebugValue topVar = var.getTopParent(); - if (myDebugProcess.isVariable(topVar.getName())) { + if (!myDebugProcess.canSaveToTemp(topVar.getName())) { return; } if (myTempVars.contains(threadId, frameId, topVar.getTempName())) { @@ -343,7 +368,7 @@ public class RemoteDebugger implements ProcessDebugger { } } if (myDebuggerReader != null) { - myDebuggerReader.close(); + myDebuggerReader.stop(); } fireCloseEvent(); } @@ -428,7 +453,6 @@ public class RemoteDebugger implements ProcessDebugger { } private class DebuggerReader extends BaseOutputReader { - private boolean myClosing = false; private Reader myReader; private DebuggerReader(final Reader reader) throws IOException { @@ -442,7 +466,7 @@ public class RemoteDebugger implements ProcessDebugger { while (true) { boolean read = readAvailable(); - if (myClosing) { + if (isStopped) { break; } @@ -453,7 +477,7 @@ public class RemoteDebugger implements ProcessDebugger { fireCommunicationError(); } finally { - closeReader(myReader); + close(); fireExitEvent(); } } @@ -475,10 +499,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); } } @@ -568,7 +592,13 @@ public class RemoteDebugger implements ProcessDebugger { } public void close() { - myClosing = true; + closeReader(myReader); + } + + @Override + public void stop() { + super.stop(); + close(); } @Override 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..7a3315b4acce --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCustomOperationCommand.java @@ -0,0 +1,86 @@ +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. + * <p> + * 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. + * <p> + * 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. + * <p> + * For an example, see {@link PrettyPrintCommandHandler} + */ +public class RunCustomOperationCommand<T> extends AbstractCommand<T> { + private static final Logger LOG = Logger.getInstance(RunCustomOperationCommand.class); + + private String myEncodedCodeOrFile; + private String myOperationFnName; + private PyVariableLocator myLocator; + private String myStyle; + + private RunCustomOperationCommand(RemoteDebugger target, PyVariableLocator locator, + String style, String codeOrFile, String operationFnName) { + super(target, CMD_RUN_CUSTOM_OPERATION); + + this.myLocator = locator; + this.myStyle = style; + this.myEncodedCodeOrFile = encode(codeOrFile); + this.myOperationFnName = 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); + } + + + @Override + protected void buildPayload(Payload payload) { + payload.add(myLocator.getPyDBLocation() + "||" + myStyle).add(myEncodedCodeOrFile).add(myOperationFnName); + } + + @Override + public boolean isResponseExpected() { + return true; + } + + 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 ""; + } + } + +} + |