From fcdb060b76d2648ae8788e4ac8e5097c4fbf24ec Mon Sep 17 00:00:00 2001 From: Fabio Zadrozny Date: Fri, 5 Oct 2018 17:48:43 -0300 Subject: [PATCH] Allow configuring whether the debugger should stop/print errors when there's an exception evaluating a breakpoint condition. Fixes #853 (#876) * Allow configuring whether the debugger should stop/print errors when there's an exception evaluating a breakpoint condition. Fixes #853 * Temporarily remove CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION configuration (needs test fixes to be applied). * Fix linting. --- .../_pydevd_bundle/pydevd_breakpoints.py | 4 +- .../pydevd/_pydevd_bundle/pydevd_comm.py | 3 + .../pydevd/_pydevd_bundle/pydevd_frame.py | 44 +++++----- .../pydevd_process_net_command.py | 50 +++++++---- ptvsd/_vendored/pydevd/pydevd.py | 18 ++-- .../pydevd/tests_python/debugger_unittest.py | 20 +++-- ..._debugger_case_breakpoint_condition_exc.py | 8 ++ .../pydevd/tests_python/test_debugger.py | 88 ++++++++++++++++++- 8 files changed, 179 insertions(+), 56 deletions(-) create mode 100644 ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_breakpoint_condition_exc.py diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py index f8aa60dd..011ab289 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py @@ -17,7 +17,7 @@ class ExceptionBreakpoint(object): notify_on_first_raise_only, ignore_libraries ): - exctype = _get_class(qname) + exctype = get_exception_class(qname) self.qname = qname if exctype is not None: self.name = exctype.__name__ @@ -156,7 +156,7 @@ def stop_on_unhandled_exception(py_db, thread, additional_info, arg): py_db.stop_on_unhandled_exception(thread, frame, frames_byid, arg) -def _get_class(kls): +def get_exception_class(kls): if IS_PY24 and "BaseException" == kls: kls = "Exception" diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py index 2ed1e589..b38f8487 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py @@ -185,6 +185,8 @@ CMD_STOP_ON_START = 154 # When the debugger is stopped in an exception, this command will provide the details of the current exception (in the current thread). CMD_GET_EXCEPTION_DETAILS = 155 +CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION = 156 + CMD_REDIRECT_OUTPUT = 200 CMD_GET_NEXT_STATEMENT_TARGETS = 201 CMD_SET_PROJECT_ROOTS = 202 @@ -252,6 +254,7 @@ ID_TO_MEANING = { '153': 'CMD_THREAD_DUMP_TO_STDERR', '154': 'CMD_STOP_ON_START', '155': 'CMD_GET_EXCEPTION_DETAILS', + '156': 'CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION', '200': 'CMD_REDIRECT_OUTPUT', '201': 'CMD_GET_NEXT_STATEMENT_TARGETS', diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py index 2f8759dd..05fd77fe 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_frame.py @@ -20,6 +20,7 @@ try: from inspect import CO_GENERATOR except: CO_GENERATOR = 0 +from _pydevd_bundle.pydevd_constants import IS_PY2 try: from _pydevd_bundle.pydevd_signature import send_signature_call_trace, send_signature_return_trace @@ -51,34 +52,37 @@ def handle_breakpoint_condition(py_db, info, breakpoint, new_frame): return False return eval(condition, new_frame.f_globals, new_frame.f_locals) - - except: - if type(condition) != type(''): - if hasattr(condition, 'encode'): + except Exception as e: + if IS_PY2: + # Must be bytes on py2. + if isinstance(condition, unicode): condition = condition.encode('utf-8') - msg = 'Error while evaluating expression: %s\n' % (condition,) - sys.stderr.write(msg) - traceback.print_exc() - if not py_db.suspend_on_breakpoint_exception: - return False - else: + if not isinstance(e, py_db.skip_print_breakpoint_exception): + sys.stderr.write('Error while evaluating expression: %s\n' % (condition,)) + + etype, value, tb = sys.exc_info() + traceback.print_exception(etype, value, tb.tb_next) + + if not isinstance(e, py_db.skip_suspend_on_breakpoint_exception): try: # add exception_type and stacktrace into thread additional info etype, value, tb = sys.exc_info() - try: - error = ''.join(traceback.format_exception_only(etype, value)) - stack = traceback.extract_stack(f=tb.tb_frame.f_back) + error = ''.join(traceback.format_exception_only(etype, value)) + stack = traceback.extract_stack(f=tb.tb_frame.f_back) - # On self.set_suspend(thread, CMD_SET_BREAK) this info will be - # sent to the client. - info.conditional_breakpoint_exception = \ - ('Condition:\n' + condition + '\n\nError:\n' + error, stack) - finally: - etype, value, tb = None, None, None + # On self.set_suspend(thread, CMD_SET_BREAK) this info will be + # sent to the client. + info.conditional_breakpoint_exception = \ + ('Condition:\n' + condition + '\n\nError:\n' + error, stack) except: traceback.print_exc() return True + + return False + + finally: + etype, value, tb = None, None, None def handle_breakpoint_expression(breakpoint, info, new_frame): @@ -619,7 +623,7 @@ class PyDBFrame: # # As for lamdba, as it only has a single statement, it's not interesting to trace # its call and later its line event as they're usually in the same line. - + # No need to reset frame.f_trace to keep the same trace function. return self.trace_dispatch diff --git a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command.py b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command.py index 8370a60d..0eb3bb41 100644 --- a/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command.py +++ b/ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command.py @@ -6,26 +6,27 @@ from _pydev_bundle import pydev_log from _pydevd_bundle import pydevd_traceproperty, pydevd_dont_trace, pydevd_utils import pydevd_tracing import pydevd_file_utils -from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint -from _pydevd_bundle.pydevd_comm import CMD_RUN, CMD_VERSION, CMD_LIST_THREADS, CMD_THREAD_KILL, InternalTerminateThread, \ - CMD_THREAD_SUSPEND, pydevd_find_thread_by_id, CMD_THREAD_RUN, InternalRunThread, CMD_STEP_INTO, CMD_STEP_OVER, \ - CMD_STEP_RETURN, CMD_STEP_INTO_MY_CODE, InternalStepThread, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT, \ - CMD_SMART_STEP_INTO, InternalSetNextStatementThread, CMD_RELOAD_CODE, ReloadCodeCommand, CMD_CHANGE_VARIABLE, \ - InternalChangeVariable, CMD_GET_VARIABLE, InternalGetVariable, CMD_GET_ARRAY, InternalGetArray, CMD_GET_COMPLETIONS, \ - InternalGetCompletions, CMD_GET_FRAME, InternalGetFrame, CMD_SET_BREAK, file_system_encoding, CMD_REMOVE_BREAK, \ - CMD_EVALUATE_EXPRESSION, CMD_EXEC_EXPRESSION, InternalEvaluateExpression, CMD_CONSOLE_EXEC, InternalConsoleExec, \ - CMD_SET_PY_EXCEPTION, CMD_GET_FILE_CONTENTS, CMD_SET_PROPERTY_TRACE, CMD_ADD_EXCEPTION_BREAK, \ - CMD_REMOVE_EXCEPTION_BREAK, CMD_LOAD_SOURCE, CMD_ADD_DJANGO_EXCEPTION_BREAK, CMD_REMOVE_DJANGO_EXCEPTION_BREAK, \ - CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression, InternalConsoleGetCompletions, \ - CMD_RUN_CUSTOM_OPERATION, InternalRunCustomOperation, CMD_IGNORE_THROWN_EXCEPTION_AT, CMD_ENABLE_DONT_TRACE, \ - CMD_SHOW_RETURN_VALUES, ID_TO_MEANING, CMD_GET_DESCRIPTION, InternalGetDescription, InternalLoadFullValue, \ - CMD_LOAD_FULL_VALUE, CMD_REDIRECT_OUTPUT, CMD_GET_NEXT_STATEMENT_TARGETS, InternalGetNextStatementTargets, CMD_SET_PROJECT_ROOTS, \ - CMD_GET_THREAD_STACK, CMD_THREAD_DUMP_TO_STDERR, CMD_STOP_ON_START, CMD_GET_EXCEPTION_DETAILS, NetCommand,\ - CMD_SET_PROTOCOL -from _pydevd_bundle.pydevd_constants import get_thread_id, IS_PY3K, DebugInfoHolder, dict_keys, STATE_RUN, \ - NEXT_VALUE_SEPARATOR, IS_WINDOWS +from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint, get_exception_class +from _pydevd_bundle.pydevd_comm import (CMD_RUN, CMD_VERSION, CMD_LIST_THREADS, CMD_THREAD_KILL, InternalTerminateThread, + CMD_THREAD_SUSPEND, pydevd_find_thread_by_id, CMD_THREAD_RUN, InternalRunThread, CMD_STEP_INTO, CMD_STEP_OVER, + CMD_STEP_RETURN, CMD_STEP_INTO_MY_CODE, InternalStepThread, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT, + CMD_SMART_STEP_INTO, InternalSetNextStatementThread, CMD_RELOAD_CODE, ReloadCodeCommand, CMD_CHANGE_VARIABLE, + InternalChangeVariable, CMD_GET_VARIABLE, InternalGetVariable, CMD_GET_ARRAY, InternalGetArray, CMD_GET_COMPLETIONS, + InternalGetCompletions, CMD_GET_FRAME, InternalGetFrame, CMD_SET_BREAK, file_system_encoding, CMD_REMOVE_BREAK, + CMD_EVALUATE_EXPRESSION, CMD_EXEC_EXPRESSION, InternalEvaluateExpression, CMD_CONSOLE_EXEC, InternalConsoleExec, + CMD_SET_PY_EXCEPTION, CMD_GET_FILE_CONTENTS, CMD_SET_PROPERTY_TRACE, CMD_ADD_EXCEPTION_BREAK, + CMD_REMOVE_EXCEPTION_BREAK, CMD_LOAD_SOURCE, CMD_ADD_DJANGO_EXCEPTION_BREAK, CMD_REMOVE_DJANGO_EXCEPTION_BREAK, + CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression, InternalConsoleGetCompletions, + CMD_RUN_CUSTOM_OPERATION, InternalRunCustomOperation, CMD_IGNORE_THROWN_EXCEPTION_AT, CMD_ENABLE_DONT_TRACE, + CMD_SHOW_RETURN_VALUES, ID_TO_MEANING, CMD_GET_DESCRIPTION, InternalGetDescription, InternalLoadFullValue, + CMD_LOAD_FULL_VALUE, CMD_REDIRECT_OUTPUT, CMD_GET_NEXT_STATEMENT_TARGETS, InternalGetNextStatementTargets, CMD_SET_PROJECT_ROOTS, + CMD_GET_THREAD_STACK, CMD_THREAD_DUMP_TO_STDERR, CMD_STOP_ON_START, CMD_GET_EXCEPTION_DETAILS, NetCommand, + CMD_SET_PROTOCOL, CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION) +from _pydevd_bundle.pydevd_constants import (get_thread_id, IS_PY3K, DebugInfoHolder, dict_keys, STATE_RUN, + NEXT_VALUE_SEPARATOR, IS_WINDOWS) from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info from _pydev_imps._pydev_saved_modules import threading +import json def process_net_command(py_db, cmd_id, seq, text): '''Processes a command received from the Java side @@ -819,6 +820,19 @@ def process_net_command(py_db, cmd_id, seq, text): elif cmd_id == CMD_STOP_ON_START: py_db.stop_on_start = text.strip() in ('True', 'true', '1') + + elif cmd_id == CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION: + # Expected to receive a json string as: + # { + # 'skip_suspend_on_breakpoint_exception': [] + # 'skip_print_breakpoint_exception': [] + # } + msg = json.loads(text.strip()) + py_db.skip_suspend_on_breakpoint_exception = tuple( + get_exception_class(x) for x in msg.get('skip_suspend_on_breakpoint_exception', ())) + + py_db.skip_print_breakpoint_exception = tuple( + get_exception_class(x) for x in msg.get('skip_print_breakpoint_exception', ())) elif cmd_id == CMD_GET_EXCEPTION_DETAILS: thread_id = text diff --git a/ptvsd/_vendored/pydevd/pydevd.py b/ptvsd/_vendored/pydevd/pydevd.py index a8257512..9333a2df 100644 --- a/ptvsd/_vendored/pydevd/pydevd.py +++ b/ptvsd/_vendored/pydevd/pydevd.py @@ -256,9 +256,10 @@ class PyDB: self.skip_on_exceptions_thrown_in_same_context = False self.ignore_exceptions_thrown_in_lines_with_ignore_exception = True - # Suspend debugger even if breakpoint condition raises an exception - SUSPEND_ON_BREAKPOINT_EXCEPTION = True - self.suspend_on_breakpoint_exception = SUSPEND_ON_BREAKPOINT_EXCEPTION + # Suspend debugger even if breakpoint condition raises an exception. + # May be changed with CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION. + self.skip_suspend_on_breakpoint_exception = () # By default suspend on any Exception. + self.skip_print_breakpoint_exception = () # By default print on any Exception. # By default user can step into properties getter/setter/deleter methods self.disable_property_trace = False @@ -711,22 +712,21 @@ class PyDB: thread.stop_reason = stop_reason # If conditional breakpoint raises any exception during evaluation send details to Java - if stop_reason == CMD_SET_BREAK and self.suspend_on_breakpoint_exception: - self._send_breakpoint_condition_exception(thread) + if stop_reason == CMD_SET_BREAK and info.conditional_breakpoint_exception is not None: + conditional_breakpoint_exception_tuple = info.conditional_breakpoint_exception + info.conditional_breakpoint_exception = None + self._send_breakpoint_condition_exception(thread, conditional_breakpoint_exception_tuple) - def _send_breakpoint_condition_exception(self, thread): + def _send_breakpoint_condition_exception(self, thread, conditional_breakpoint_exception_tuple): """If conditional breakpoint raises an exception during evaluation send exception details to java """ thread_id = get_thread_id(thread) - conditional_breakpoint_exception_tuple = thread.additional_info.conditional_breakpoint_exception # conditional_breakpoint_exception_tuple - should contain 2 values (exception_type, stacktrace) if conditional_breakpoint_exception_tuple and len(conditional_breakpoint_exception_tuple) == 2: exc_type, stacktrace = conditional_breakpoint_exception_tuple int_cmd = InternalGetBreakpointException(thread_id, exc_type, stacktrace) - # Reset the conditional_breakpoint_exception details to None - thread.additional_info.conditional_breakpoint_exception = None self.post_internal_command(int_cmd, thread_id) diff --git a/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py b/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py index 6bf33410..13399a52 100644 --- a/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py +++ b/ptvsd/_vendored/pydevd/tests_python/debugger_unittest.py @@ -1,5 +1,6 @@ from collections import namedtuple from contextlib import contextmanager +import json try: from urllib import quote, quote_plus, unquote_plus except ImportError: @@ -74,6 +75,7 @@ CMD_GET_THREAD_STACK = 152 CMD_THREAD_DUMP_TO_STDERR = 153 # This is mostly for unit-tests to diagnose errors on ci. CMD_STOP_ON_START = 154 CMD_GET_EXCEPTION_DETAILS = 155 +CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION = 156 CMD_REDIRECT_OUTPUT = 200 CMD_GET_NEXT_STATEMENT_TARGETS = 201 @@ -736,29 +738,37 @@ class AbstractWriterThread(threading.Thread): def write_version(self): from _pydevd_bundle.pydevd_constants import IS_WINDOWS - self.write("501\t%s\t1.0\t%s\tID" % (self.next_seq(), 'WINDOWS' if IS_WINDOWS else 'UNIX')) + self.write("%s\t%s\t1.0\t%s\tID" % (CMD_VERSION, self.next_seq(), 'WINDOWS' if IS_WINDOWS else 'UNIX')) def get_main_filename(self): return self.TEST_FILE - def write_add_breakpoint(self, line, func, filename=None, hit_condition=None, is_logpoint=False, suspend_policy=None): + def write_add_breakpoint(self, line, func, filename=None, hit_condition=None, is_logpoint=False, suspend_policy=None, condition=None): ''' @param line: starts at 1 ''' if filename is None: filename = self.get_main_filename() breakpoint_id = self.next_breakpoint_id() - if hit_condition is None and not is_logpoint and suspend_policy is None: + if hit_condition is None and not is_logpoint and suspend_policy is None and condition is None: # Format kept for backward compatibility tests self.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % ( CMD_SET_BREAK, self.next_seq(), breakpoint_id, 'python-line', filename, line, func)) else: # Format: breakpoint_id, type, file, line, func_name, condition, expression, hit_condition, is_logpoint, suspend_policy - self.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone\t%s\t%s\t%s" % ( - CMD_SET_BREAK, self.next_seq(), breakpoint_id, 'python-line', filename, line, func, hit_condition, is_logpoint, suspend_policy)) + self.write("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\tNone\t%s\t%s\t%s" % ( + CMD_SET_BREAK, self.next_seq(), breakpoint_id, 'python-line', filename, line, func, condition, hit_condition, is_logpoint, suspend_policy)) self.log.append('write_add_breakpoint: %s line: %s func: %s' % (breakpoint_id, line, func)) return breakpoint_id + def write_suspend_on_breakpoint_exception(self, skip_suspend_on_breakpoint_exception=('all',), skip_print_breakpoint_exception=('all',)): + self.write("%s\t%s\t%s" % (CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, self.next_seq(), + json.dumps(dict( + skip_suspend_on_breakpoint_exception=skip_suspend_on_breakpoint_exception, + skip_print_breakpoint_exception=skip_print_breakpoint_exception + )) + )) + def write_stop_on_start(self, stop=True): self.write("%s\t%s\t%s" % (CMD_STOP_ON_START, self.next_seq(), stop)) diff --git a/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_breakpoint_condition_exc.py b/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_breakpoint_condition_exc.py new file mode 100644 index 00000000..acc2fd45 --- /dev/null +++ b/ptvsd/_vendored/pydevd/tests_python/resources/_debugger_case_breakpoint_condition_exc.py @@ -0,0 +1,8 @@ +def Call(): + for i in range(10): # break here + last_i = i + + +if __name__ == '__main__': + Call() + print('TEST SUCEEDED!') diff --git a/ptvsd/_vendored/pydevd/tests_python/test_debugger.py b/ptvsd/_vendored/pydevd/tests_python/test_debugger.py index 5f4e774f..12a76dee 100644 --- a/ptvsd/_vendored/pydevd/tests_python/test_debugger.py +++ b/ptvsd/_vendored/pydevd/tests_python/test_debugger.py @@ -16,7 +16,8 @@ from tests_python import debugger_unittest from tests_python.debugger_unittest import (CMD_SET_PROPERTY_TRACE, REASON_CAUGHT_EXCEPTION, REASON_UNCAUGHT_EXCEPTION, REASON_STOP_ON_BREAKPOINT, REASON_THREAD_SUSPEND, overrides, CMD_THREAD_CREATE, CMD_GET_THREAD_STACK, REASON_STEP_INTO_MY_CODE, CMD_GET_EXCEPTION_DETAILS, IS_IRONPYTHON, IS_JYTHON, IS_CPYTHON, - IS_APPVEYOR, wait_for_condition) + IS_APPVEYOR, wait_for_condition, CMD_GET_FRAME, CMD_GET_BREAKPOINT_EXCEPTION, + CMD_THREAD_SUSPEND) from _pydevd_bundle.pydevd_constants import IS_WINDOWS try: from urllib import unquote @@ -127,6 +128,89 @@ def test_case_2(case_setup): writer.finished_ok = True +@pytest.mark.parametrize( + 'skip_suspend_on_breakpoint_exception, skip_print_breakpoint_exception', + ( + [['NameError'], []], + [['NameError'], ['NameError']], + [[], []], # Empty means it'll suspend/print in any exception + [[], ['NameError']], + [['ValueError'], ['Exception']], + [['Exception'], ['ValueError']], # ValueError will also suspend/print since we're dealing with a NameError + ) + ) +def test_case_breakpoint_condition_exc(case_setup, skip_suspend_on_breakpoint_exception, skip_print_breakpoint_exception): + + msgs_in_stderr = ( + 'Error while evaluating expression: i > 5', + "NameError: name 'i' is not defined", + 'Traceback (most recent call last):', + 'File "", line 1, in ', + ) + + def _ignore_stderr_line(line): + if original_ignore_stderr_line(line): + return True + + for msg in msgs_in_stderr: + if msg in line: + return True + + return False + + def additional_output_checks(stdout, stderr): + original_additional_output_checks(stdout, stderr) + if skip_print_breakpoint_exception in ([], ['ValueError']): + for msg in msgs_in_stderr: + assert msg in stderr + else: + for msg in msgs_in_stderr: + assert msg not in stderr + + with case_setup.test_file('_debugger_case_breakpoint_condition_exc.py') as writer: + + original_ignore_stderr_line = writer._ignore_stderr_line + writer._ignore_stderr_line = _ignore_stderr_line + + original_additional_output_checks = writer.additional_output_checks + writer.additional_output_checks = additional_output_checks + + writer.write_suspend_on_breakpoint_exception(skip_suspend_on_breakpoint_exception, skip_print_breakpoint_exception) + breakpoint_id = writer.write_add_breakpoint( + writer.get_line_index_with_content('break here'), 'Call', condition='i > 5') + + writer.write_make_initial_run() + + if skip_suspend_on_breakpoint_exception in ([], ['ValueError']): + writer.wait_for_message(lambda msg:msg.startswith('%s\t' % (CMD_GET_BREAKPOINT_EXCEPTION,))) + hit = writer.wait_for_breakpoint_hit() + writer.write_run_thread(hit.thread_id) + + if IS_JYTHON: + # Jython will break twice. + if skip_suspend_on_breakpoint_exception in ([], ['ValueError']): + writer.wait_for_message(lambda msg:msg.startswith('%s\t' % (CMD_GET_BREAKPOINT_EXCEPTION,))) + hit = writer.wait_for_breakpoint_hit() + writer.write_run_thread(hit.thread_id) + + hit = writer.wait_for_breakpoint_hit() + thread_id = hit.thread_id + frame_id = hit.frame_id + + writer.write_get_frame(thread_id, frame_id) + msg = writer.wait_for_message(lambda msg:msg.startswith('%s\t' % (CMD_GET_FRAME,))) + name_to_value = {} + for var in msg.var: + name_to_value[var['name']] = var['value'] + assert name_to_value == {'i': 'int: 6', 'last_i': 'int: 6'} + + writer.write_remove_breakpoint(breakpoint_id) + + writer.write_run_thread(thread_id) + + writer.finished_ok = True + + @pytest.mark.skipif(IS_IRONPYTHON, reason='This test fails once in a while due to timing issues on IronPython, so, skipping it.') def test_case_3(case_setup): with case_setup.test_file('_debugger_case3.py') as writer: @@ -2182,7 +2266,7 @@ def test_top_level_exceptions_on_attach(case_setup_remote, check_scenario): def check_test_suceeded_msg(writer, stdout, stderr): return 'TEST SUCEEDED' in ''.join(stderr) - + def additional_output_checks(writer, stdout, stderr): # Don't call super as we have an expected exception assert 'ValueError: TEST SUCEEDED' in stderr