diff --git a/ptvsd/wrapper.py b/ptvsd/wrapper.py index 40de683d..5ca1f3b0 100644 --- a/ptvsd/wrapper.py +++ b/ptvsd/wrapper.py @@ -7,6 +7,7 @@ from __future__ import print_function, absolute_import import contextlib import errno import io +import json import os import platform import pydevd_file_utils @@ -1403,6 +1404,12 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor): if opts.get('SHOW_RETURN_VALUE', False): self.pydevd_request(pydevd_comm.CMD_SHOW_RETURN_VALUES, '1\t1') + # Print on all but NameError, don't suspend on any. + self.pydevd_request(pydevd_comm.CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, json.dumps(dict( + skip_suspend_on_breakpoint_exception=('BaseException',), + skip_print_breakpoint_exception=('NameError',), + ))) + self._apply_code_stepping_settings() def _is_just_my_code_stepping_enabled(self): @@ -2525,3 +2532,8 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor): category = 'stdout' if ctx == '1' else 'stderr' content = unquote(xml.io['s']) self.send_event('output', category=category, output=content) + + @pydevd_events.handler(pydevd_comm.CMD_GET_BREAKPOINT_EXCEPTION) + def on_pydevd_get_breakpoint_exception(self, seq, args): + # If pydevd sends exception info from a failed breakpoint condition, just ignore. + pass diff --git a/tests/helpers/pydevd/_messages.py b/tests/helpers/pydevd/_messages.py index 703b9357..bcfa3b1f 100644 --- a/tests/helpers/pydevd/_messages.py +++ b/tests/helpers/pydevd/_messages.py @@ -117,3 +117,15 @@ class PyDevdMessages(object): info += fmt.format(*frame) info += '' return info + + def format_breakpoint_exception(self, threadid, exc_type, stacktrace): + info = '' + for filename, line, methodname, methodobj in stacktrace: + fn = pydevd_xml.make_valid_xml_value(filename) + mn = pydevd_xml.make_valid_xml_value(methodname) + obj = pydevd_xml.make_valid_xml_value(methodobj) + info += '' \ + % (threadid, fn, line, mn, obj) + info += "" + return exc_type + '\t' + info + diff --git a/tests/highlevel/__init__.py b/tests/highlevel/__init__.py index 1d2974e9..9726de6a 100644 --- a/tests/highlevel/__init__.py +++ b/tests/highlevel/__init__.py @@ -18,6 +18,7 @@ from _pydevd_bundle.pydevd_comm import ( CMD_SET_PROJECT_ROOTS, CMD_GET_THREAD_STACK, CMD_GET_EXCEPTION_DETAILS, + CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, ) from ptvsd._util import new_hidden_thread @@ -140,9 +141,10 @@ class PyDevdLifecycle(object): @contextlib.contextmanager def _wait_for_initialized(self): with self._fix.wait_for_command(CMD_REDIRECT_OUTPUT): - with self._fix.wait_for_command(CMD_SET_PROJECT_ROOTS): - with self._fix.wait_for_command(CMD_RUN): - yield + with self._fix.wait_for_command(CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION): + with self._fix.wait_for_command(CMD_SET_PROJECT_ROOTS): + with self._fix.wait_for_command(CMD_RUN): + yield def _initialize(self): version = self._fix.fake.VERSION diff --git a/tests/highlevel/test_lifecycle.py b/tests/highlevel/test_lifecycle.py index e3b37960..9ef2a4d7 100644 --- a/tests/highlevel/test_lifecycle.py +++ b/tests/highlevel/test_lifecycle.py @@ -1,3 +1,4 @@ +import json import os import ptvsd import sys @@ -8,6 +9,7 @@ from _pydevd_bundle.pydevd_comm import ( CMD_RUN, CMD_VERSION, CMD_SET_PROJECT_ROOTS, + CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, ) from . import ( @@ -112,6 +114,10 @@ class LifecycleTests(HighlevelTest, unittest.TestCase): self.debugger_msgs.new_request(CMD_VERSION, *['1.1', expected_os_id, 'ID']), self.debugger_msgs.new_request(CMD_REDIRECT_OUTPUT), + self.debugger_msgs.new_request(CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, json.dumps(dict( + skip_suspend_on_breakpoint_exception=('BaseException',), + skip_print_breakpoint_exception=('NameError',), + ))), self.debugger_msgs.new_request(CMD_SET_PROJECT_ROOTS, _get_project_dirs()), self.debugger_msgs.new_request(CMD_RUN), @@ -158,7 +164,8 @@ class LifecycleTests(HighlevelTest, unittest.TestCase): # configuration req_config = self.send_request('configurationDone') self.wait_for_pydevd('version', 'redirect-output', - 'run', 'set_project_roots') + 'run', 'suspend_on_breakpoint_exception', + 'set_project_roots') # Normal ops would go here. @@ -190,6 +197,10 @@ class LifecycleTests(HighlevelTest, unittest.TestCase): self.debugger_msgs.new_request(CMD_VERSION, *['1.1', OS_ID, 'ID']), self.debugger_msgs.new_request(CMD_REDIRECT_OUTPUT), + self.debugger_msgs.new_request(CMD_SUSPEND_ON_BREAKPOINT_EXCEPTION, json.dumps(dict( + skip_suspend_on_breakpoint_exception=('BaseException',), + skip_print_breakpoint_exception=('NameError',), + ))), self.debugger_msgs.new_request(CMD_SET_PROJECT_ROOTS, _get_project_dirs()), self.debugger_msgs.new_request(CMD_RUN), diff --git a/tests/highlevel/test_messages.py b/tests/highlevel/test_messages.py index c17b0120..0af54962 100644 --- a/tests/highlevel/test_messages.py +++ b/tests/highlevel/test_messages.py @@ -2798,22 +2798,19 @@ class GetExceptionBreakpointEventTests(PyDevdEventTest, unittest.TestCase): CMD = CMD_GET_BREAKPOINT_EXCEPTION EVENT = None - def pydevd_payload(self, tid, exc, frame): - return self.debugger_msgs.format_exception(tid, exc, frame) + def pydevd_payload(self, tid, exc_type, stacktrace): + return self.debugger_msgs.format_breakpoint_exception(tid, exc_type, stacktrace) - def test_unsupported(self): - ptid = 10 - exc = RuntimeError('something went wrong') - frame = (2, 'spam', 'abc.py', 10) # (pfid, func, file, line) + def test_basic(self): + trace = [('abc.py', 10, 'spam', 'eggs'), ] # (file, line, func, obj) with self.launched(): - with self.assertRaises(UnsupportedPyDevdCommandError): - self.send_event(ptid, exc, frame) + self.send_event(10, 'RuntimeError', trace) received = self.vsc.received + # Note: We don't send any events to client. So this should be empty. self.assert_vsc_received(received, []) self.assert_received(self.debugger, []) - class ShowConsoleEventTests(PyDevdEventTest, unittest.TestCase): CMD = CMD_SHOW_CONSOLE @@ -2853,3 +2850,20 @@ class WriteToConsoleEventTests(PyDevdEventTest, unittest.TestCase): self.assert_vsc_received(received, []) self.assert_received(self.debugger, []) + +class UnsupportedPyDevdEventTests(PyDevdEventTest, unittest.TestCase): + + CMD = 12345 + EVENT = None + + def pydevd_paylaod(self, msg): + return msg + + def test_unsupported(self): + with self.launched(): + with self.assertRaises(UnsupportedPyDevdCommandError): + self.send_event('unsupported') + received = self.vsc.received + + self.assert_vsc_received(received, []) + self.assert_received(self.debugger, [])