Implement condition and log expression for Exception breakpoints (PY-24593)

This commit is contained in:
Elizaveta Shashkova
2017-06-23 16:09:50 +03:00
parent 0f3731ebc9
commit 7ccde74227
13 changed files with 130 additions and 22 deletions

View File

@@ -19,6 +19,8 @@ class ExceptionBreakpoint:
def __init__(
self,
qname,
condition,
expression,
notify_always,
notify_on_terminate,
notify_on_first_raise_only,
@@ -31,6 +33,8 @@ class ExceptionBreakpoint:
else:
self.name = None
self.condition = condition
self.expression = expression
self.notify_on_terminate = notify_on_terminate
self.notify_always = notify_always
self.notify_on_first_raise_only = notify_on_first_raise_only

View File

@@ -13,7 +13,7 @@ from _pydevd_bundle.pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETUR
from _pydevd_bundle.pydevd_constants import STATE_SUSPEND, dict_contains, get_thread_id, STATE_RUN, dict_iter_values, IS_PY3K, \
dict_keys, RETURN_VALUES_DICT
from _pydevd_bundle.pydevd_dont_trace_files import DONT_TRACE, PYDEV_FILE
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame, just_raised, remove_exception_from_frame
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
from pydevd_file_utils import get_abs_path_real_path_and_base_from_frame
@@ -167,6 +167,12 @@ class PyDBFrame:
exception, main_debugger.break_on_caught_exceptions)
if exception_breakpoint is not None:
add_exception_to_frame(frame, (exception, value, trace))
if exception_breakpoint.condition is not None:
eval_result = handle_breakpoint_condition(main_debugger, info, exception_breakpoint, frame)
if not eval_result:
return False, frame
if exception_breakpoint.ignore_libraries:
if exception_breakpoint.notify_on_first_raise_only:
if main_debugger.first_appearance_in_scope(trace):
@@ -198,6 +204,12 @@ class PyDBFrame:
except:
flag = False
if flag:
if exception_breakpoint.expression is not None:
handle_breakpoint_expression(exception_breakpoint, info, frame)
else:
remove_exception_from_frame(frame)
return flag, frame
def handle_exception(self, frame, event, arg):

View File

@@ -29,6 +29,11 @@ class FCode(object):
def add_exception_to_frame(frame, exception_info):
frame.f_locals['__exception__'] = exception_info
def remove_exception_from_frame(frame):
frame.f_locals.pop('__exception__')
FILES_WITH_IMPORT_HOOKS = ['pydev_monkey_qt.py', 'pydev_import_hook.py']
def just_raised(trace):

View File

@@ -493,11 +493,26 @@ def process_net_command(py_db, cmd_id, seq, text):
pass
elif cmd_id == CMD_ADD_EXCEPTION_BREAK:
condition = None
expression = None
if text.find('\t') != -1:
exception, notify_always, notify_on_terminate, ignore_libraries = text.split('\t', 3)
try:
exception, condition, expression, notify_always, notify_on_terminate, ignore_libraries = text.split('\t', 5)
except:
exception, notify_always, notify_on_terminate, ignore_libraries = text.split('\t', 3)
else:
exception, notify_always, notify_on_terminate, ignore_libraries = text, 0, 0, 0
condition = condition.replace("@_@NEW_LINE_CHAR@_@", '\n').replace("@_@TAB_CHAR@_@", '\t').strip()
if len(condition) == 0 or condition == "None":
condition = None
expression = expression.replace("@_@NEW_LINE_CHAR@_@", '\n').replace("@_@TAB_CHAR@_@", '\t').strip()
if len(expression) == 0 or expression == "None":
expression = None
if exception.find('-') != -1:
breakpoint_type, exception = exception.split('-')
else:
@@ -508,8 +523,10 @@ def process_net_command(py_db, cmd_id, seq, text):
pydev_log.warn("Deprecated parameter: 'notify always' policy removed in PyCharm\n")
exception_breakpoint = py_db.add_break_on_exception(
exception,
condition=condition,
expression=expression,
notify_always=int(notify_always) > 0,
notify_on_terminate = int(notify_on_terminate) == 1,
notify_on_terminate=int(notify_on_terminate) == 1,
notify_on_first_raise_only=int(notify_always) == 2,
ignore_libraries=int(ignore_libraries) > 0
)

View File

@@ -609,6 +609,8 @@ class PyDB:
def add_break_on_exception(
self,
exception,
condition,
expression,
notify_always,
notify_on_terminate,
notify_on_first_raise_only,
@@ -617,6 +619,8 @@ class PyDB:
try:
eb = ExceptionBreakpoint(
exception,
condition,
expression,
notify_always,
notify_on_terminate,
notify_on_first_raise_only,

View File

@@ -94,6 +94,21 @@ public abstract class AbstractCommand<T> {
protected abstract void buildPayload(Payload payload);
@NotNull
public static String buildCondition(String expression) {
String condition;
if (expression != null) {
condition = expression.replaceAll("\n", NEW_LINE_CHAR);
condition = condition.replaceAll("\t", TAB_CHAR);
}
else {
condition = "None";
}
return condition;
}
public boolean isResponseExpected() {
return false;
}

View File

@@ -1,23 +1,33 @@
package com.jetbrains.python.debugger.pydev;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author traff
*/
public class AddExceptionBreakpointCommand extends ExceptionBreakpointCommand {
final ExceptionBreakpointNotifyPolicy myNotifyPolicy;
final String myCondition;
final String myLogExpression;
public AddExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger,
@NotNull String exception, @NotNull ExceptionBreakpointNotifyPolicy notifyPolicy) {
@NotNull String exception,
@Nullable String condition,
@Nullable String logExpression,
@NotNull ExceptionBreakpointNotifyPolicy notifyPolicy) {
super(debugger, ADD_EXCEPTION_BREAKPOINT, exception);
myNotifyPolicy = notifyPolicy;
myCondition = condition;
myLogExpression = logExpression;
}
@Override
protected void buildPayload(Payload payload) {
super.buildPayload(payload);
payload.add(myNotifyPolicy.isNotifyOnlyOnFirst() ? 2 : 0)
payload.add(buildCondition(myCondition))
.add(buildCondition(myLogExpression))
.add(myNotifyPolicy.isNotifyOnlyOnFirst() ? 2 : 0)
.add(myNotifyPolicy.isNotifyOnTerminate())
.add(myNotifyPolicy.isIgnoreLibraries());
}

View File

@@ -23,8 +23,13 @@ public class ExceptionBreakpointCommand extends AbstractCommand {
payload.add(myException);
}
public static ExceptionBreakpointCommand addExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, String exception, AddExceptionBreakpointCommand.ExceptionBreakpointNotifyPolicy notifyPolicy) {
return new AddExceptionBreakpointCommand(debugger, exception, notifyPolicy);
public static ExceptionBreakpointCommand
addExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger,
String exception,
String condition,
String logExpression,
AddExceptionBreakpointCommand.ExceptionBreakpointNotifyPolicy notifyPolicy) {
return new AddExceptionBreakpointCommand(debugger, exception, condition, logExpression, notifyPolicy);
}
public static ExceptionBreakpointCommand removeExceptionBreakpointCommand(@NotNull final RemoteDebugger debugger, String exception) {

View File

@@ -40,18 +40,4 @@ public class SetBreakpointCommand extends LineBreakpointCommand {
payload.add(buildCondition(myFuncName)).add(mySuspendPolicy.name()).add(buildCondition(myCondition))
.add(buildCondition(myLogExpression));
}
@NotNull
private static String buildCondition(String expression) {
String condition;
if (expression != null) {
condition = expression.replaceAll("\n", NEW_LINE_CHAR);
condition = condition.replaceAll("\t", TAB_CHAR);
}
else {
condition = "None";
}
return condition;
}
}

View File

@@ -18,6 +18,7 @@ package com.jetbrains.python.debugger;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.xdebugger.breakpoints.XBreakpointProperties;
import com.jetbrains.python.debugger.pydev.ExceptionBreakpointCommandFactory;
import org.jetbrains.annotations.Nullable;
/**
* @author traff
@@ -31,4 +32,10 @@ public abstract class ExceptionBreakpointProperties<T> extends XBreakpointProper
}
public abstract String getExceptionBreakpointId();
public void setCondition(@Nullable String condition) {
}
public void setLogExpression(@Nullable String condition) {
}
}

View File

@@ -888,6 +888,14 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
public void addExceptionBreakpoint(XBreakpoint<? extends ExceptionBreakpointProperties> breakpoint) {
myRegisteredExceptionBreakpoints.put(breakpoint.getProperties().getException(), breakpoint);
if (isConnected()) {
String conditionExpression = breakpoint.getConditionExpression() == null
? null
: breakpoint.getConditionExpression().getExpression();
breakpoint.getProperties().setCondition(conditionExpression);
String logExpression = breakpoint.getLogExpressionObject() == null
? null
: breakpoint.getLogExpressionObject().getExpression();
breakpoint.getProperties().setLogExpression(logExpression);
myDebugger.addExceptionBreakpoint(breakpoint.getProperties());
}
}

View File

@@ -20,6 +20,7 @@ import com.jetbrains.python.debugger.pydev.AddExceptionBreakpointCommand;
import com.jetbrains.python.debugger.pydev.ExceptionBreakpointCommand;
import com.jetbrains.python.debugger.pydev.RemoteDebugger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author traff
@@ -31,6 +32,8 @@ public class PyExceptionBreakpointProperties extends ExceptionBreakpointProperti
public boolean myNotifyOnTerminate;
@Attribute("ignoreLibraries")
public boolean myIgnoreLibraries;
public @Nullable String myCondition;
public @Nullable String myLogExpression;
@SuppressWarnings({"UnusedDeclaration"})
@@ -41,6 +44,8 @@ public class PyExceptionBreakpointProperties extends ExceptionBreakpointProperti
myException = exception;
myNotifyOnTerminate = true;
myIgnoreLibraries = false;
myCondition = null;
myLogExpression = null;
}
@Override
@@ -80,13 +85,34 @@ public class PyExceptionBreakpointProperties extends ExceptionBreakpointProperti
return myIgnoreLibraries;
}
@Nullable
public String getCondition() {
return myCondition;
}
public void setCondition(@Nullable String condition) {
myCondition = condition;
}
@Nullable
public String getLogExpression() {
return myLogExpression;
}
public void setLogExpression(@Nullable String logExpression) {
myLogExpression = logExpression;
}
public String getExceptionBreakpointId() {
return "python-" + myException;
}
@Override
public ExceptionBreakpointCommand createAddCommand(RemoteDebugger debugger) {
return ExceptionBreakpointCommand.addExceptionBreakpointCommand(debugger, getExceptionBreakpointId(),
return ExceptionBreakpointCommand.addExceptionBreakpointCommand(debugger,
getExceptionBreakpointId(),
getCondition(),
getLogExpression(),
new AddExceptionBreakpointCommand.ExceptionBreakpointNotifyPolicy(
isNotifyOnTerminate(), isNotifyOnlyOnFirst(), isIgnoreLibraries()));
}

View File

@@ -33,9 +33,11 @@ import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.breakpoints.XBreakpoint;
import com.intellij.xdebugger.breakpoints.XBreakpointType;
import com.intellij.xdebugger.breakpoints.ui.XBreakpointCustomPropertiesPanel;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -138,6 +140,13 @@ public class PyExceptionBreakpointType
return "";
}
@Nullable
@Override
public XDebuggerEditorsProvider getEditorsProvider(@NotNull XBreakpoint<PyExceptionBreakpointProperties> breakpoint,
@NotNull Project project) {
return new PyDebuggerEditorsProvider();
}
@Override
public XBreakpoint<PyExceptionBreakpointProperties> createDefaultBreakpoint(@NotNull XBreakpointCreator<PyExceptionBreakpointProperties> creator) {
final XBreakpoint<PyExceptionBreakpointProperties> breakpoint = creator.createBreakpoint(createDefaultBreakpointProperties());