diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 360e44fbdd0b..4385a4db9248 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -1,5 +1,6 @@ #IMPORTANT: pydevd_constants must be the 1st thing defined because it'll keep a reference to the original sys._getframe from pydevd_constants import * #@UnusedWildImport +from pydevd_breakpoints import * #@UnusedWildImport from pydevd_comm import CMD_CHANGE_VARIABLE, \ CMD_EVALUATE_EXPRESSION, \ @@ -52,6 +53,7 @@ import pydevd_tracing import pydevd_io from pydevd_additional_thread_info import PyDBAdditionalThreadInfo import time + threadingEnumerate = threading.enumerate threadingCurrentThread = threading.currentThread @@ -126,95 +128,6 @@ class PyDBCommandThread(PyDBDaemonThread): #only got this error in interpreter shutdown #PydevdLog(0, 'Finishing debug communication...(3)') - -_original_excepthook = None -_handle_exceptions = None -_exception_set = set() - - -#======================================================================================================================= -# excepthook -#======================================================================================================================= -def excepthook(exctype, value, tb): - if _handle_exceptions is not None: - if not issubclass(exctype, _handle_exceptions): - return _original_excepthook(exctype, value, tb) - - #Always call the original excepthook before going on to call the debugger post mortem to show it. - _original_excepthook(exctype, value, tb) - - frames = [] - - traceback = tb - while tb: - frames.append(tb.tb_frame) - tb = tb.tb_next - - thread = threadingCurrentThread() - frames_byid = dict([(id(frame),frame) for frame in frames]) - frame = frames[-1] - thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid) - thread.additionalInfo.message = exctype.__name__ - #sys.exc_info = lambda : (exctype, value, traceback) - debugger = GetGlobalDebugger() - debugger.force_post_mortem_stop += 1 - - -#======================================================================================================================= -# set_pm_excepthook -#======================================================================================================================= -def set_pm_excepthook(handle_exceptions=None): - ''' - Should be called to register the excepthook to be used. - - It's only useful for uncaucht exceptions. I.e.: exceptions that go up to the excepthook. - - Can receive a parameter to stop only on some exceptions. - - E.g.: - register_excepthook((IndexError, ValueError)) - - or - - register_excepthook(IndexError) - - if passed without a parameter, will break on any exception - - @param handle_exceptions: exception or tuple(exceptions) - The exceptions that should be handled. - ''' - global _handle_exceptions - global _original_excepthook - if sys.excepthook != excepthook: - #Only keep the original if it's not our own excepthook (if called many times). - _original_excepthook = sys.excepthook - - _handle_exceptions = handle_exceptions - sys.excepthook = excepthook - -def restore_pm_excepthook(): - global _original_excepthook - if (_original_excepthook): - sys.excepthook = _original_excepthook - _original_excepthook = None - - -def update_exception_hook(): - if len(_exception_set) >0: - set_pm_excepthook(tuple(_exception_set)) - else: - restore_pm_excepthook() - -def get_class( kls ): - parts = kls.split('.') - module = ".".join(parts[:-1]) - if (module == ""): - module = "__builtin__" - m = __import__( module ) - for comp in parts[-1:]: - m = getattr(m, comp) - return m - try: import thread except ImportError: @@ -437,7 +350,23 @@ class PyDB: finally: self.release() - + + def enable_tracing(self): + #and enable the tracing for existing threads (because there may be frames being executed that + #are currently untraced). + threads = threadingEnumerate() + for t in threads: + if not t.getName().startswith('pydevd.'): + #TODO: optimize so that we only actually add that tracing if it's in + #the new breakpoint context. + additionalInfo = getattr(t, 'additionalInfo', None) + + if additionalInfo is not None: + for frame in additionalInfo.IterFrames(): + frame.f_trace = self.trace_dispatch + SetTraceForParents(frame, self.trace_dispatch) + del frame + def processNetCommand(self, cmd_id, seq, text): '''Processes a command received from the Java side @@ -628,25 +557,8 @@ class PyDB: self.breakpoints[file] = breakDict - - #and enable the tracing for existing threads (because there may be frames being executed that - #are currently untraced). - threads = threadingEnumerate() - for t in threads: - if not t.getName().startswith('pydevd.'): - #TODO: optimize so that we only actually add that tracing if it's in - #the new breakpoint context. - additionalInfo = None - try: - additionalInfo = t.additionalInfo - except AttributeError: - pass #that's ok, no info currently set - - if additionalInfo is not None: - for frame in additionalInfo.IterFrames(): - frame.f_trace = self.trace_dispatch - SetTraceForParents(frame, self.trace_dispatch) - del frame + + self.enable_tracing() elif cmd_id == CMD_REMOVE_BREAK: #command to remove some breakpoint @@ -685,17 +597,28 @@ class PyDB: self.postInternalCommand(int_cmd, thread_id) elif cmd_id == CMD_ADD_EXCEPTION_BREAK: - exception = text + global exception_set + exception, notify_always, notify_on_terminate = text.split('\t', 2) exc_type = get_class(exception) + is_notify_always = int(notify_always) == 1 + is_notify_on_terminate = int(notify_on_terminate) == 1 + if exc_type is not None: - _exception_set.add(exc_type) - update_exception_hook() + exception_set.add(ExceptionBreakpoint(exc_type, is_notify_always, is_notify_on_terminate)) + + if is_notify_on_terminate: + update_exception_hook() + if is_notify_always: + global always_exception_set + always_exception_set.add(exc_type) + self.enable_tracing() elif cmd_id == CMD_REMOVE_EXCEPTION_BREAK: exception = text exc_type = get_class(exception) if exc_type is not None: - _exception_set.remove(exc_type) + exception_set.remove(exc_type) + always_exception_set.remove(exc_type) update_exception_hook() else: diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py new file mode 100644 index 000000000000..2dbc3c187340 --- /dev/null +++ b/python/helpers/pydev/pydevd_breakpoints.py @@ -0,0 +1,117 @@ +import sys + +_original_excepthook = None +_handle_exceptions = None +exception_set = set() +always_exception_set = set() + +NOTIFY_ALWAYS="NOTIFY_ALWAYS" +NOTIFY_ON_TERMINATE="NOTIFY_ON_TERMINATE" + +import threading +threadingCurrentThread = threading.currentThread + +from pydevd_comm import GetGlobalDebugger + +class ExceptionBreakpoint: + def __init__(self, exctype, notify_always, notify_on_terminate): + self.name = exctype.__name__ + self.type = exctype + self.notify = {NOTIFY_ALWAYS: notify_always, NOTIFY_ON_TERMINATE: notify_on_terminate} + +def get_exception_breakpoint(exctype, exceptions, notify_class): + exc = None + if exceptions is not None: + for e in exceptions: + if e.notify[notify_class] and issubclass(exctype, e.type): + if exc is None or issubclass(e.type, exc.type): + exc = e + return exc + +#======================================================================================================================= +# excepthook +#======================================================================================================================= +def excepthook(exctype, value, tb): + global _handle_exceptions + if _handle_exceptions is not None: + exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions, NOTIFY_ON_TERMINATE) + else: + exception_breakpoint = None + + if exception_breakpoint is None: + return _original_excepthook(exctype, value, tb) + + #Always call the original excepthook before going on to call the debugger post mortem to show it. + _original_excepthook(exctype, value, tb) + + frames = [] + + traceback = tb + while tb: + frames.append(tb.tb_frame) + tb = tb.tb_next + + thread = threadingCurrentThread() + frames_byid = dict([(id(frame),frame) for frame in frames]) + frame = frames[-1] + thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid) + thread.additionalInfo.message = exception_breakpoint.name + #sys.exc_info = lambda : (exctype, value, traceback) + debugger = GetGlobalDebugger() + debugger.force_post_mortem_stop += 1 + + +#======================================================================================================================= +# set_pm_excepthook +#======================================================================================================================= +def set_pm_excepthook(handle_exceptions_arg=None): + ''' + Should be called to register the excepthook to be used. + + It's only useful for uncaucht exceptions. I.e.: exceptions that go up to the excepthook. + + Can receive a parameter to stop only on some exceptions. + + E.g.: + register_excepthook((IndexError, ValueError)) + + or + + register_excepthook(IndexError) + + if passed without a parameter, will break on any exception + + @param handle_exceptions: exception or tuple(exceptions) + The exceptions that should be handled. + ''' + global _handle_exceptions + global _original_excepthook + if sys.excepthook != excepthook: + #Only keep the original if it's not our own excepthook (if called many times). + _original_excepthook = sys.excepthook + + _handle_exceptions = handle_exceptions_arg + sys.excepthook = excepthook + +def restore_pm_excepthook(): + global _original_excepthook + if (_original_excepthook): + sys.excepthook = _original_excepthook + _original_excepthook = None + + +def update_exception_hook(): + if len(exception_set) >0: + set_pm_excepthook(tuple(exception_set)) + else: + restore_pm_excepthook() + +def get_class( kls ): + parts = kls.split('.') + module = ".".join(parts[:-1]) + if (module == ""): + module = "__builtin__" + m = __import__( module ) + for comp in parts[-1:]: + m = getattr(m, comp) + return m \ No newline at end of file diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py index 801eee966c98..e258499e59e6 100644 --- a/python/helpers/pydev/pydevd_frame.py +++ b/python/helpers/pydev/pydevd_frame.py @@ -1,8 +1,10 @@ from pydevd_comm import * #@UnusedWildImport from pydevd_constants import * #@UnusedWildImport +from pydevd_breakpoints import * #@UnusedWildImport import traceback #@Reimport import os.path import sys + basename = os.path.basename #======================================================================================================================= @@ -28,64 +30,70 @@ class PyDBFrame: def trace_dispatch(self, frame, event, arg): if event not in ('line', 'call', 'return', 'exception'): return None - + mainDebugger, filename, info, thread = self._args - - breakpoint = mainDebugger.breakpoints.get(filename) - if info.pydev_state == STATE_RUN: - #we can skip if: - #- we have no stop marked - #- we should make a step return/step over and we're not in the current frame - can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\ - or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame) - else: + if event is not 'exception': + breakpoint = mainDebugger.breakpoints.get(filename) + can_skip = False - - # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, - # we will return nothing for the next trace - #also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, - #so, that's why the additional checks are there. - if not breakpoint: - if can_skip: - return None + if len(always_exception_set) == 0: + if info.pydev_state == STATE_RUN: + #we can skip if: + #- we have no stop marked + #- we should make a step return/step over and we're not in the current frame + can_skip = (info.pydev_step_cmd is None and info.pydev_step_stop is None)\ + or (info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and info.pydev_step_stop is not frame) - else: - #checks the breakpoint to see if there is a context match in some function - curr_func_name = frame.f_code.co_name - - #global context is set with an empty name - if curr_func_name in ('?', ''): - curr_func_name = '' - - for _b, condition, func_name, expression in breakpoint.values(): #jython does not support itervalues() - #will match either global or some function - if func_name in ('None', curr_func_name): - break - - else: # if we had some break, it won't get here (so, that's a context that we want to skip) + + # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, + # we will return nothing for the next trace + #also, after we hit a breakpoint and go to some other debugging state, we have to force the set trace anyway, + #so, that's why the additional checks are there. + if not breakpoint: if can_skip: - #print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd - return None - + return None + + else: + #checks the breakpoint to see if there is a context match in some function + curr_func_name = frame.f_code.co_name + + #global context is set with an empty name + if curr_func_name in ('?', ''): + curr_func_name = '' + + for _b, condition, func_name, expression in breakpoint.values(): #jython does not support itervalues() + #will match either global or some function + if func_name in ('None', curr_func_name): + break + + else: # if we had some break, it won't get here (so, that's a context that we want to skip) + if can_skip: + #print 'skipping', frame.f_lineno, info.pydev_state, info.pydev_step_stop, info.pydev_step_cmd + return None + else: + breakpoint = None + #We may have hit a breakpoint or we are already in step mode. Either way, let's check what we should do in this frame - #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name + #print 'NOT skipped', frame.f_lineno, frame.f_code.co_name, event - try: line = frame.f_lineno - #if event == 'exception' and info.pydev_state != STATE_SUSPEND and breakpoint is not None: - # (exception, value, traceback) = arg - # print exception - # curr_func_name = frame.f_code.co_name - # if curr_func_name in ('?', ''): - # self.setSuspend(thread, CMD_SET_BREAK) - # self.doWaitSuspend(thread, frame, event, arg) + if event == 'exception' and info.pydev_state != STATE_SUSPEND: #and breakpoint is not None: + (exception, value, traceback) = arg + global exception_set + + exception_breakpoint = get_exception_breakpoint(exception, tuple(exception_set), NOTIFY_ALWAYS) + if exception_breakpoint is not None: + curr_func_name = frame.f_code.co_name + self.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK) + thread.additionalInfo.message = exception_breakpoint.name + #self.doWaitSuspend(thread, frame, event, arg) #return is not taken into account for breakpoint hit because we'd have a double-hit in this case #(one for the line and the other for the return). - if event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoint is not None \ + elif event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoint is not None \ and DictContains(breakpoint, line): #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java index 3cba151ac715..d70d95e355a6 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractCommand.java @@ -39,7 +39,13 @@ public abstract class AbstractCommand { } @Nullable - public abstract String getPayload(); + public final String getPayload() { + Payload payload = new Payload(); + buildPayload(payload); + return payload.getText(); + } + + protected abstract void buildPayload(Payload payload); public boolean isResponseExpected() { return false; @@ -69,4 +75,40 @@ public abstract class AbstractCommand { } } + protected static class Payload { + private final StringBuilder myBuilder = new StringBuilder(); + private static final char SEPARATOR = '\t'; + + + public Payload add(boolean flag) { + return doAdd(flag ? "1" : "0"); + } + + public Payload add(String text) { + return doAdd(text); + } + + private Payload doAdd(String text) { + if (myBuilder.length() > 0) { + return separator().append(text); + } + else { + return append(text); + } + } + + private Payload append(String text) { + myBuilder.append(ProtocolParser.encodeExpression(text)); + return this; + } + + private Payload separator() { + myBuilder.append(SEPARATOR); + return this; + } + + public String getText() { + return myBuilder.toString(); + } + } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractFrameCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractFrameCommand.java index 3597a64f8922..dc8695618fc1 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractFrameCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractFrameCommand.java @@ -10,4 +10,9 @@ public abstract class AbstractFrameCommand extends AbstractThreadCommand { myFrameId = frameId; } + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add(myFrameId); + } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractThreadCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractThreadCommand.java index c462ed6abf2a..5c85b07be8ef 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractThreadCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AbstractThreadCommand.java @@ -2,7 +2,6 @@ package com.jetbrains.python.debugger.pydev; public abstract class AbstractThreadCommand extends AbstractCommand { - protected final String myThreadId; protected AbstractThreadCommand(final RemoteDebugger debugger, final int commandCode, final String threadId) { @@ -10,6 +9,11 @@ public abstract class AbstractThreadCommand extends AbstractCommand { myThreadId = threadId; } + @Override + protected void buildPayload(Payload payload) { + payload.add(myThreadId); + } + public static boolean isThreadCommand(int command) { return command == CREATE_THREAD || command == KILL_THREAD || diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/AddExceptionBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AddExceptionBreakpointCommand.java new file mode 100644 index 000000000000..0547eaaed248 --- /dev/null +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/AddExceptionBreakpointCommand.java @@ -0,0 +1,40 @@ +package com.jetbrains.python.debugger.pydev; + +import org.jetbrains.annotations.NotNull; + +/** + * @author traff + */ +public class AddExceptionBreakpointCommand extends ExceptionBreakpointCommand { + final ExceptionBreakpointNotifyPolicy myNotifyPolicy; + + public AddExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, + @NotNull String exception, @NotNull ExceptionBreakpointNotifyPolicy notifyPolicy) { + super(debugger, ADD_EXCEPTION_BREAKPOINT, exception); + myNotifyPolicy = notifyPolicy; + } + + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add(myNotifyPolicy.isNotifyAlways()).add(myNotifyPolicy.isNotifyOnTerminate()); + } + + public static class ExceptionBreakpointNotifyPolicy { + private final boolean myNotifyAlways; + private final boolean myNotifyOnTerminate; + + public ExceptionBreakpointNotifyPolicy(boolean notifyAlways, boolean notifyOnTerminate) { + myNotifyAlways = notifyAlways; + myNotifyOnTerminate = notifyOnTerminate; + } + + public boolean isNotifyAlways() { + return myNotifyAlways; + } + + public boolean isNotifyOnTerminate() { + return myNotifyOnTerminate; + } + } +} diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ChangeVariableCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ChangeVariableCommand.java index f09ed32388d6..ed04531e0f04 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ChangeVariableCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ChangeVariableCommand.java @@ -20,9 +20,10 @@ public class ChangeVariableCommand extends AbstractFrameCommand { myDebugProcess = debugger.getDebugProcess(); } - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t").append(myVariableName) - .append('\t').append(ProtocolParser.encodeExpression(myValue)).toString(); + + @Override + protected void buildPayload(Payload payload) { + payload.add("FRAME").add(myVariableName).add(myValue); } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java index b5f411b3f5ad..a3f599f65983 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ConsoleExecCommand.java @@ -13,9 +13,10 @@ public class ConsoleExecCommand extends AbstractFrameCommand { myExpression = expression; } - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t") - .append(ProtocolParser.encodeExpression(myExpression)).toString(); + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add(myThreadId).add(myFrameId).add("FRAME").add(myExpression); } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/EvaluateCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/EvaluateCommand.java index a25c90914df5..1d08a865f4c5 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/EvaluateCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/EvaluateCommand.java @@ -23,9 +23,11 @@ public class EvaluateCommand extends AbstractFrameCommand { myTrimResult = trimResult; } - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t") - .append(ProtocolParser.encodeExpression(myExpression)).append('\t').append(myTrimResult ? "1" : "0").toString(); + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add("FRAME").add(myExpression).add(myTrimResult); + } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ExceptionBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ExceptionBreakpointCommand.java index 9ed7d3aed9b3..4a0fedda16a6 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ExceptionBreakpointCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ExceptionBreakpointCommand.java @@ -7,20 +7,24 @@ import org.jetbrains.annotations.NotNull; */ public class ExceptionBreakpointCommand extends AbstractCommand { - protected String myException; + @NotNull + protected final String myException; - private ExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, final int commandCode, String exception) { + + protected ExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, + final int commandCode, + @NotNull String exception) { super(debugger, commandCode); myException = exception; } @Override - public String getPayload() { - return new StringBuilder().append(myException).toString(); + protected void buildPayload(Payload payload) { + payload.add(myException); } - public static ExceptionBreakpointCommand addExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, String exception) { - return new ExceptionBreakpointCommand(debugger, ADD_EXCEPTION_BREAKPOINT, exception); + public static ExceptionBreakpointCommand addExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, String exception, AddExceptionBreakpointCommand.ExceptionBreakpointNotifyPolicy notifyPolicy) { + return new AddExceptionBreakpointCommand(debugger, exception, notifyPolicy); } public static ExceptionBreakpointCommand removeExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, String exception) { diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetCompletionsCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetCompletionsCommand.java index af345a0000cb..5c4dfa9b32f6 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetCompletionsCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetCompletionsCommand.java @@ -40,10 +40,11 @@ public class GetCompletionsCommand extends AbstractFrameCommand { } } + @Override - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t") - .append(myActionToken).toString(); + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add("FRAME").add(myActionToken); } @Nullable diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetFrameCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetFrameCommand.java index 2fee61023d75..2020ca16a2d9 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetFrameCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetFrameCommand.java @@ -22,8 +22,10 @@ public class GetFrameCommand extends AbstractFrameCommand { myDebugProcess = debugger.getDebugProcess(); } - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME").toString(); + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add("FRAME"); } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java index 0062d5dfbcbc..c3ee585c9d7a 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/GetVariableCommand.java @@ -18,9 +18,10 @@ public class GetVariableCommand extends GetFrameCommand { myParent = parent; } - public String getPayload() { - return new StringBuilder().append(myThreadId).append('\t').append(myFrameId).append('\t').append("FRAME\t").append(myVariableName) - .toString(); + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add(myVariableName); } @Override diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java index 0ca44bf7c5c7..173fe04eccdf 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/LineBreakpointCommand.java @@ -17,4 +17,9 @@ public abstract class LineBreakpointCommand extends AbstractCommand { myFile = file; myLine = line; } + + @Override + protected void buildPayload(Payload payload) { + payload.add(myFile).add(Integer.toString(myLine)); + } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolFrame.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolFrame.java index 3d265f69071a..a0eabd87ca45 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolFrame.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ProtocolFrame.java @@ -10,9 +10,9 @@ public class ProtocolFrame { private final int myCommand; private final int mySequence; - private final String myPayload; + private @NotNull final String myPayload; - public ProtocolFrame(final int command, final int sequence, final String payload) throws PyDebuggerException { + public ProtocolFrame(final int command, final int sequence, @NotNull final String payload) throws PyDebuggerException { myCommand = command; mySequence = sequence; myPayload = payload; @@ -26,7 +26,7 @@ public class ProtocolFrame { myCommand = Integer.parseInt(parts[0]); mySequence = Integer.parseInt(parts[1]); - myPayload = (parts.length == 3 && !"".equals(parts[2]) ? ProtocolParser.decode(parts[2]) : null); + myPayload = (parts.length == 3 && !"".equals(parts[2]) ? ProtocolParser.decode(parts[2]) : ""); } public int getCommand() { @@ -37,6 +37,7 @@ public class ProtocolFrame { return mySequence; } + @NotNull public String getPayload() { return myPayload; } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java index 77bf53829f04..13f664f6b9da 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RemoveBreakpointCommand.java @@ -6,9 +6,4 @@ public class RemoveBreakpointCommand extends LineBreakpointCommand { public RemoveBreakpointCommand(final RemoteDebugger debugger, final String file, final int line) { super(debugger, REMOVE_BREAKPOINT, file, line); } - - public String getPayload() { - return new StringBuilder().append(myFile).append('\t').append(Integer.toString(myLine)).toString(); - } - } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ResumeCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ResumeCommand.java index b18e98c04281..a3e407128da8 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/ResumeCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/ResumeCommand.java @@ -20,9 +20,4 @@ public class ResumeCommand extends AbstractThreadCommand { public String getThreadId() { return myThreadId; } - - public String getPayload() { - return myThreadId; - } - } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCommand.java index 47cb988bd178..37313ab86b8d 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/RunCommand.java @@ -7,8 +7,7 @@ public class RunCommand extends AbstractCommand { super(debugger, RUN); } - public String getPayload() { - return null; + @Override + protected void buildPayload(Payload payload) { } - } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java index c4a306bd698f..db9c538eb053 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SetBreakpointCommand.java @@ -24,8 +24,10 @@ public class SetBreakpointCommand extends LineBreakpointCommand { myLogExpression = logExpression; } - public String getPayload() { - return new StringBuilder().append(myFile).append('\t').append(Integer.toString(myLine)).append("\t").append(buildCondition(myCondition)).append("\t").append(buildCondition(myLogExpression)).toString(); + @Override + protected void buildPayload(Payload payload) { + super.buildPayload(payload); + payload.add(buildCondition(myCondition)).add(buildCondition(myLogExpression)); } @NotNull diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SuspendCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SuspendCommand.java index 3cfff1463268..4e58c452592f 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/SuspendCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/SuspendCommand.java @@ -7,9 +7,4 @@ public class SuspendCommand extends AbstractThreadCommand { protected SuspendCommand(final RemoteDebugger debugger, final String threadId) { super(debugger, SUSPEND_THREAD, threadId); } - - @Override - public String getPayload() { - return myThreadId; - } } diff --git a/python/pydevSrc/com/jetbrains/python/debugger/pydev/VersionCommand.java b/python/pydevSrc/com/jetbrains/python/debugger/pydev/VersionCommand.java index 531c915f4638..3afc73ac5204 100644 --- a/python/pydevSrc/com/jetbrains/python/debugger/pydev/VersionCommand.java +++ b/python/pydevSrc/com/jetbrains/python/debugger/pydev/VersionCommand.java @@ -12,8 +12,9 @@ public class VersionCommand extends AbstractCommand { myVersion = version; } - public String getPayload() { - return myVersion; + @Override + protected void buildPayload(Payload payload) { + payload.add(myVersion); } @Override diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index f2f2332ee616..5c82211b0f87 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -285,7 +285,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess { myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint); if (myDebugger.isConnected()) { final ExceptionBreakpointCommand command = - ExceptionBreakpointCommand.addExceptionBreakpointCommand(myDebugger, breakpoint.getProperties().getException()); + ExceptionBreakpointCommand.addExceptionBreakpointCommand(myDebugger, breakpoint.getProperties().getException(), new AddExceptionBreakpointCommand.ExceptionBreakpointNotifyPolicy(breakpoint.getProperties().isNotifyAlways(), breakpoint.getProperties().isNotifyOnTerminate())); myDebugger.execute(command); } } diff --git a/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java b/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java index 96586e0f96a3..0103c3ebce28 100644 --- a/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java +++ b/python/src/com/jetbrains/python/debugger/PyExceptionBreakpointProperties.java @@ -7,9 +7,14 @@ import org.jetbrains.annotations.NotNull; /** * @author traff */ -public class PyExceptionBreakpointProperties extends XBreakpointProperties{ +public class PyExceptionBreakpointProperties extends XBreakpointProperties { @Attribute("exception") public String myException; + @Attribute("notifyAlways") + public boolean myNotifyAlways; + @Attribute("notifyOnTerminate") + public boolean myNotifyOnTerminate; + @SuppressWarnings({"UnusedDeclaration"}) public PyExceptionBreakpointProperties() { @@ -27,9 +32,27 @@ public class PyExceptionBreakpointProperties extends XBreakpointProperties, PyExceptionBreakpointProperties> { + private static final String BASE_EXCEPTION = "BaseException"; + public PyExceptionBreakpointType() { super("python-exception", "Python Exception Breakpoint", false); } - @Override public PyExceptionBreakpointProperties createProperties() { - return new PyExceptionBreakpointProperties("BaseException"); - } + return p(); + }private PyExceptionBreakpointProperties p() { + return new PyExceptionBreakpointProperties(BASE_EXCEPTION);} @Override public boolean isAddBreakpointButtonVisible() { @@ -48,7 +54,6 @@ public class PyExceptionBreakpointType dialog.showDialog(); - // on ok final PyClass pyClass = dialog.getSelected(); if (pyClass != null) { @@ -96,8 +101,94 @@ public class PyExceptionBreakpointType public String getDisplayText(XBreakpoint breakpoint) { PyExceptionBreakpointProperties properties = breakpoint.getProperties(); if (properties != null) { - return properties.getException(); + String exception = properties.getException(); + if (BASE_EXCEPTION.equals(exception)) { + return "All exceptions"; + } + return exception; } return ""; } + + @Override + public XBreakpoint createDefaultBreakpoint(@NotNull XBreakpointCreator creator) { + final XBreakpoint breakpoint = creator.createBreakpoint(createDefaultBreakpointProperties()); + breakpoint.setEnabled(true); + return breakpoint; + } + + private static PyExceptionBreakpointProperties createDefaultBreakpointProperties() { + PyExceptionBreakpointProperties p = new PyExceptionBreakpointProperties(BASE_EXCEPTION); + p.setNotifyOnTerminate(true); + p.setNotifyAlways(false); + return p; + } + + @Override + public XBreakpointCustomPropertiesPanel> createCustomPropertiesPanel() { + return new PyExceptionBreakpointPropertiesPanel(); + } + + + private static class PyExceptionBreakpointPropertiesPanel + extends XBreakpointCustomPropertiesPanel> { + private JCheckBox myNotifyOnTerminateCheckBox; + private JCheckBox myNotifyAlwaysCheckBox; + + @NotNull + @Override + public JComponent getComponent() { + myNotifyOnTerminateCheckBox = new JCheckBox("On terminate"); + myNotifyAlwaysCheckBox = new JCheckBox("Always"); + + Box notificationsBox = Box.createVerticalBox(); + JPanel panel = new JPanel(new BorderLayout()); + panel.add(myNotifyOnTerminateCheckBox, BorderLayout.NORTH); + notificationsBox.add(panel); + panel = new JPanel(new BorderLayout()); + panel.add(myNotifyAlwaysCheckBox, BorderLayout.NORTH); + notificationsBox.add(panel); + + panel = new JPanel(new BorderLayout()); + JPanel innterPanel = new JPanel(new BorderLayout()); + innterPanel.add(notificationsBox, BorderLayout.CENTER); + innterPanel.add(Box.createHorizontalStrut(3), BorderLayout.WEST); + innterPanel.add(Box.createHorizontalStrut(3), BorderLayout.EAST); + panel.add(innterPanel, BorderLayout.NORTH); + panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Notifications")); + + ActionListener listener = new ActionListener() { + public void actionPerformed(ActionEvent e) { + if (!myNotifyOnTerminateCheckBox.isSelected() && !myNotifyAlwaysCheckBox.isSelected()) { + Object source = e.getSource(); + JCheckBox toCheck = null; + if (myNotifyOnTerminateCheckBox.equals(source)) { + toCheck = myNotifyAlwaysCheckBox; + } + else if (myNotifyAlwaysCheckBox.equals(source)) { + toCheck = myNotifyOnTerminateCheckBox; + } + if (toCheck != null) { + toCheck.setSelected(true); + } + } + } + }; + myNotifyOnTerminateCheckBox.addActionListener(listener); + myNotifyAlwaysCheckBox.addActionListener(listener); + return panel; + } + + @Override + public void saveTo(@NotNull XBreakpoint breakpoint) { + breakpoint.getProperties().setNotifyAlways(myNotifyAlwaysCheckBox.isSelected()); + breakpoint.getProperties().setNotifyOnTerminate(myNotifyOnTerminateCheckBox.isSelected()); + } + + @Override + public void loadFrom(@NotNull XBreakpoint breakpoint) { + myNotifyAlwaysCheckBox.setSelected(breakpoint.getProperties().isNotifyAlways()); + myNotifyOnTerminateCheckBox.setSelected(breakpoint.getProperties().isNotifyOnTerminate()); + } + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java index fd0417e19932..c4e8e663367a 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java @@ -279,7 +279,7 @@ public class PyStringLiteralExpressionImpl extends PyElementImpl implements PySt @NotNull public PsiReference[] getReferences() { - return ReferenceProvidersRegistry.getReferencesFromProviders(this, PyStringLiteralExpression.class); + return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS); } public List> getInjectedPsi() {