diff --git a/src/ptvsd/_vendored/pydevd/pydevd.py b/src/ptvsd/_vendored/pydevd/pydevd.py index 6cbda586..208e9f50 100644 --- a/src/ptvsd/_vendored/pydevd/pydevd.py +++ b/src/ptvsd/_vendored/pydevd/pydevd.py @@ -1265,10 +1265,16 @@ class PyDB(object): from_this_thread.append(frame_custom_thread_id) with self._threads_suspended_single_notification.notify_thread_suspended(thread_id, stop_reason): - self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker) + keep_suspended = self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker) + + if keep_suspended: + # This means that we should pause again after a set next statement. + self._threads_suspended_single_notification.increment_suspend_time() + self.do_wait_suspend(thread, frame, event, arg, is_unhandled_exception) def _do_wait_suspend(self, thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker): info = thread.additional_info + keep_suspended = False if info.pydev_state == STATE_SUSPEND and not self._finish_debugging_session: in_main_thread = is_current_thread_main_thread() @@ -1324,8 +1330,8 @@ class PyDB(object): self.writer.add_command(cmd) info.pydev_state = STATE_SUSPEND thread.stop_reason = CMD_SET_NEXT_STATEMENT - self.do_wait_suspend(thread, frame, event, arg) - return + keep_suspended = True + else: # Set next did not work... info.pydev_step_cmd = -1 @@ -1333,8 +1339,7 @@ class PyDB(object): thread.stop_reason = CMD_THREAD_SUSPEND # return to the suspend state and wait for other command (without sending any # additional notification to the client). - self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker) - return + return self._do_wait_suspend(thread, frame, event, arg, suspend_type, from_this_thread, frames_tracker) elif info.pydev_step_cmd in (CMD_STEP_RETURN, CMD_STEP_RETURN_MY_CODE): back_frame = frame.f_back @@ -1370,6 +1375,8 @@ class PyDB(object): # print('Removing created frame: %s' % (frame_id,)) self.writer.add_command(self.cmd_factory.make_thread_killed_message(frame_id)) + return keep_suspended + def do_stop_on_unhandled_exception(self, thread, frame, frames_byid, arg): pydev_log.debug("We are stopping in post-mortem\n") try: diff --git a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py index f23d821b..2ac51a49 100644 --- a/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py +++ b/src/ptvsd/_vendored/pydevd/tests_python/test_debugger_json.py @@ -820,6 +820,7 @@ def test_hex_variables(case_setup): writer.finished_ok = True +@pytest.mark.skipif(IS_JYTHON, reason='Flaky on Jython.') def test_pause_and_continue(case_setup): with case_setup.test_file('_debugger_case_pause_continue.py') as writer: json_facade = JsonFacade(writer) @@ -852,14 +853,13 @@ def test_pause_and_continue(case_setup): scope = pydevd_schema.Scope(**next(iter(scopes_response.body.scopes))) frame_variables_reference = scope.variablesReference - if not IS_JYTHON: - set_variable_request = json_facade.write_request( - pydevd_schema.SetVariableRequest(pydevd_schema.SetVariableArguments( - frame_variables_reference, 'loop', 'False' - ))) - set_variable_response = json_facade.wait_for_response(set_variable_request) - set_variable_response_as_dict = set_variable_response.to_dict()['body'] - assert set_variable_response_as_dict == {'value': "False", 'type': 'bool'} + set_variable_request = json_facade.write_request( + pydevd_schema.SetVariableRequest(pydevd_schema.SetVariableArguments( + frame_variables_reference, 'loop', 'False' + ))) + set_variable_response = json_facade.wait_for_response(set_variable_request) + set_variable_response_as_dict = set_variable_response.to_dict()['body'] + assert set_variable_response_as_dict == {'value': "False", 'type': 'bool'} continue_request = json_facade.write_request( pydevd_schema.ContinueRequest(pydevd_schema.ContinueArguments('*'))) diff --git a/src/ptvsd/wrapper.py b/src/ptvsd/wrapper.py index 7d077a35..a7015b30 100644 --- a/src/ptvsd/wrapper.py +++ b/src/ptvsd/wrapper.py @@ -1000,7 +1000,7 @@ INITIALIZE_RESPONSE = dict( supportsSetVariable=True, supportsValueFormattingOptions=True, supportTerminateDebuggee=True, - supportsGotoTargetsRequest=False, # https://github.com/Microsoft/ptvsd/issues/1163 + supportsGotoTargetsRequest=True, exceptionBreakpointFilters=[ { 'filter': 'raised', @@ -1244,6 +1244,10 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor): self.internals_filter = InternalsFilter() self.new_thread_lock = threading.Lock() + # goto + self.goto_target_map = IDMap() + self.current_goto_request = None + # adapter state self._detached = False self._path_mappings_received = False @@ -2085,30 +2089,11 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor): @pydevd_events.handler(pydevd_comm.CMD_THREAD_SUSPEND) @async_handler def on_pydevd_thread_suspend(self, seq, args): - xml = self.parse_xml_response(args) - reason = int(xml.thread['stop_reason']) - - # Normally, we rely on CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION instead, - # but we only get this one in response to CMD_SET_NEXT_STATEMENT. - if reason == pydevd_comm.CMD_SET_NEXT_STATEMENT: - pyd_tid = xml.thread['id'] - vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) - self.send_event( - 'stopped', - reason='pause', - threadId=vsc_tid, - allThreadsStopped=True) + pass # We only care about the thread suspend single notification. @pydevd_events.handler(pydevd_comm.CMD_THREAD_RUN) def on_pydevd_thread_run(self, seq, args): - pyd_tid, reason = args.split('\t', 2) - reason = int(reason) - vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False) - - # Normally, we rely on CMD_THREAD_RESUME_SINGLE_NOTIFICATION instead, - # but we only get this one in response to CMD_SET_NEXT_STATEMENT. - if reason == pydevd_comm.CMD_SET_NEXT_STATEMENT: - self.send_event('continued', threadId=vsc_tid) + pass # We only care about the thread suspend single notification. @pydevd_events.handler(pydevd_comm_constants.CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION) @async_handler diff --git a/tests/func/test_step.py b/tests/func/test_step.py index 24ba4fe2..54ce2cee 100644 --- a/tests/func/test_step.py +++ b/tests/func/test_step.py @@ -12,17 +12,18 @@ from tests.helpers.timeline import Event from tests.helpers.pattern import ANY -@pytest.mark.skip(reason='https://github.com/Microsoft/ptvsd/issues/1163') def test_set_next_statement(pyfile, run_as, start_method): + @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def func(): - print(1) #@inner1 - print(2) #@inner2 - print(3) #@outer3 + print(1) # @inner1 + print(2) # @inner2 + + print(3) # @outer3 func() line_numbers = get_marked_line_numbers(code_to_debug) diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py index ca0323bf..3ebbea74 100644 --- a/tests/helpers/__init__.py +++ b/tests/helpers/__init__.py @@ -11,15 +11,14 @@ import threading import time import traceback - if sys.version_info >= (3, 5): clock = time.monotonic else: clock = time.clock - timestamp_zero = clock() + def timestamp(): return clock() - timestamp_zero @@ -83,7 +82,7 @@ def get_marked_line_numbers(path): with open(path) as f: lines = {} for i, line in enumerate(f): - match = re.search(r'#@\s*(.*?)\s*$', line) + match = re.search(r'#\s*@\s*(.*?)\s*$', line) if match: marker = match.group(1) lines[marker] = i + 1