Fix set next statement with DAP. Fixes #1163

This commit is contained in:
fabioz 2019-03-19 16:27:09 -03:00 committed by Fabio Zadrozny
parent 6a785aba08
commit fcf8cf6503
5 changed files with 34 additions and 42 deletions

View file

@ -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:

View file

@ -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('*')))

View file

@ -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

View file

@ -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)

View file

@ -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