diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py index bdfce727f..a77e69b0d 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py @@ -129,7 +129,8 @@ def set_gui_event_loop(self, py_db, gui_event_loop): py_db._gui_event_loop = gui_event_loop def send_error_message(self, py_db, msg): - sys.stderr.write('pydevd: %s\n' % (msg,)) + cmd = py_db.cmd_factory.make_warning_message('pydevd: %s\n' % (msg,)) + py_db.writer.add_command(cmd) def set_show_return_values(self, py_db, show_return_values): if show_return_values: diff --git a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py index 86fa3c188..7922f45c1 100644 --- a/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py +++ b/src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_process_net_command_json.py @@ -15,7 +15,7 @@ ProcessEvent, Scope, ScopesResponseBody, SetExpressionResponseBody, SetVariableResponseBody, SourceBreakpoint, SourceResponseBody, VariablesResponseBody, SetBreakpointsResponseBody, Response, - Capabilities, PydevdAuthorizeRequest, Request, StepInTargetsResponse, StepInTarget, + Capabilities, PydevdAuthorizeRequest, Request, StepInTargetsResponseBody, SetFunctionBreakpointsResponseBody, BreakpointEvent, BreakpointEventBody) from _pydevd_bundle.pydevd_api import PyDevdAPI @@ -35,7 +35,6 @@ from _pydevd_bundle.pydevd_comm import internal_get_step_in_targets_json from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info from _pydevd_bundle.pydevd_thread_lifecycle import pydevd_find_thread_by_id -from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding def _convert_rules_to_exclude_filters(rules, on_error): diff --git a/src/debugpy/_vendored/pydevd/pydevd.py b/src/debugpy/_vendored/pydevd/pydevd.py index 4a8d45d77..367b6d84d 100644 --- a/src/debugpy/_vendored/pydevd/pydevd.py +++ b/src/debugpy/_vendored/pydevd/pydevd.py @@ -23,6 +23,7 @@ import atexit import dis +import io from collections import defaultdict from contextlib import contextmanager from functools import partial @@ -881,10 +882,14 @@ def handle_breakpoint_condition(self, info, pybreakpoint, new_frame): return eval(condition, new_frame.f_globals, new_frame.f_locals) except Exception as e: if not isinstance(e, self.skip_print_breakpoint_exception): - sys.stderr.write('Error while evaluating expression: %s\n' % (condition,)) - + stack_trace = io.StringIO() etype, value, tb = sys.exc_info() - traceback.print_exception(etype, value, tb.tb_next) + traceback.print_exception(etype, value, tb.tb_next, file=stack_trace) + + msg = 'Error while evaluating expression in conditional breakpoint: %s\n%s' % ( + condition, stack_trace.getvalue()) + api = PyDevdAPI() + api.send_error_message(self, msg) if not isinstance(e, self.skip_suspend_on_breakpoint_exception): try: diff --git a/src/debugpy/_vendored/pydevd/tests_python/debugger_unittest.py b/src/debugpy/_vendored/pydevd/tests_python/debugger_unittest.py index 4432cf354..f8f8425e9 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/debugger_unittest.py +++ b/src/debugpy/_vendored/pydevd/tests_python/debugger_unittest.py @@ -199,6 +199,7 @@ def __init__(self, sock): self._queue = Queue() self._kill = False self.accept_xml_messages = True + self.on_message_found = lambda msg: None def set_messages_timeout(self, timeout): self.MESSAGES_TIMEOUT = timeout @@ -208,6 +209,7 @@ def get_next_message(self, context_message, timeout=None): timeout = self.MESSAGES_TIMEOUT try: msg = self._queue.get(block=True, timeout=timeout) + self.on_message_found(msg) except: raise TimeoutError('No message was written in %s seconds. Error message:\n%s' % (timeout, context_message,)) else: diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_debugger.py b/src/debugpy/_vendored/pydevd/tests_python/test_debugger.py index cf1806dcd..693abf300 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_debugger.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_debugger.py @@ -119,7 +119,7 @@ def test_case_2(case_setup): 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', + 'Error while evaluating expression in conditional breakpoint: i > 5', 'Traceback (most recent call last):', 'File "", line 1, in ', ) @@ -140,44 +140,53 @@ def _ignore_stderr_line(line): 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 - - for msg in msgs_one_in_stderr: - if msg in stderr: - break - else: - raise AssertionError('Did not find any of: %s in stderr: %s' % ( - msgs_one_in_stderr, stderr)) - else: - for msg in msgs_in_stderr + msgs_one_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) + + expect_print = skip_print_breakpoint_exception in ([], ['ValueError']) + expect_suspend = skip_suspend_on_breakpoint_exception in ([], ['ValueError']) + breakpoint_id = writer.write_add_breakpoint( writer.get_line_index_with_content('break here'), 'Call', condition='i > 5') + if not expect_print: + _original = writer.reader_thread.on_message_found + + def on_message_found(found_msg): + for msg in msgs_in_stderr + msgs_one_in_stderr: + assert msg not in found_msg + + writer.reader_thread.on_message_found = on_message_found + writer.write_make_initial_run() - if skip_suspend_on_breakpoint_exception in ([], ['ValueError']): + def check_error_msg(stderr): + for msg in msgs_in_stderr: + assert msg in stderr + + for msg in msgs_one_in_stderr: + if msg in stderr: + break + else: + raise AssertionError('Did not find any of: %s in stderr: %s' % ( + msgs_one_in_stderr, stderr)) + + if expect_print: + msg, ctx = writer.wait_for_output() + check_error_msg(msg.replace('>', '>')) + + if expect_suspend: writer.wait_for_message(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']): + if expect_suspend: writer.wait_for_message(CMD_GET_BREAKPOINT_EXCEPTION) hit = writer.wait_for_breakpoint_hit() writer.write_run_thread(hit.thread_id) diff --git a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py index 6ba9eb336..07e51152c 100644 --- a/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py +++ b/src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py @@ -616,6 +616,33 @@ def test_case_json_hit_count_and_step(case_setup): writer.finished_ok = True +def test_case_json_hit_condition_error(case_setup): + with case_setup.test_file('_debugger_case_hit_count.py') as writer: + json_facade = JsonFacade(writer) + + json_facade.write_launch() + bp = writer.get_line_index_with_content('before loop line') + json_facade.write_set_breakpoints( + [bp], + line_to_info={ + bp: {'condition': 'range.range.range'} + }) + json_facade.write_make_initial_run() + + def accept_message(msg): + if msg.body.category == 'important': + if 'Error while evaluating expression in conditional breakpoint' in msg.body.output: + return True + return False + + json_facade.wait_for_json_message(OutputEvent, accept_message=accept_message) + + json_facade.wait_for_thread_stopped(line=bp) + json_facade.write_continue() + + writer.finished_ok = True + + def test_case_process_event(case_setup): with case_setup.test_file('_debugger_case_change_breaks.py') as writer: json_facade = JsonFacade(writer)