Use json for goto and gotoTargets request. Fixes #1221 (#1270)

* Use json for goto and gotoTargets request

* Add pydevd tests for goto and gotoTargets

* Add id maps to manage goto targets
This commit is contained in:
Karthik Nadig 2019-03-28 12:57:45 -07:00 committed by GitHub
parent b65e15a0f3
commit 35be1abb32
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 21 deletions

View file

@ -6,11 +6,12 @@ import os
from _pydevd_bundle._debug_adapter import pydevd_base_schema
from _pydevd_bundle._debug_adapter.pydevd_schema import (SourceBreakpoint, ScopesResponseBody, Scope,
VariablesResponseBody, SetVariableResponseBody, ModulesResponseBody, SourceResponseBody)
VariablesResponseBody, SetVariableResponseBody, ModulesResponseBody, SourceResponseBody,
GotoTargetsResponseBody)
from _pydevd_bundle.pydevd_api import PyDevdAPI
from _pydevd_bundle.pydevd_comm_constants import (
CMD_RETURN, CMD_STEP_OVER_MY_CODE, CMD_STEP_OVER, CMD_STEP_INTO_MY_CODE,
CMD_STEP_INTO, CMD_STEP_RETURN_MY_CODE, CMD_STEP_RETURN)
CMD_STEP_INTO, CMD_STEP_RETURN_MY_CODE, CMD_STEP_RETURN, CMD_SET_NEXT_STATEMENT)
from _pydevd_bundle.pydevd_filtering import ExcludeFilter
from _pydevd_bundle.pydevd_json_debug_options import _extract_debug_options
from _pydevd_bundle.pydevd_net_command import NetCommand
@ -78,6 +79,25 @@ def _convert_rules_to_exclude_filters(rules, filename_to_server, on_error):
return exclude_filters
class IDMap(object):
def __init__(self):
self._value_to_key = {}
self._key_to_value = {}
self._next_id = partial(next, itertools.count(0))
def obtain_value(self, key):
return self._key_to_value[key]
def obtain_key(self, value):
try:
key = self._value_to_key[value]
except KeyError:
key = self._next_id()
self._key_to_value[key] = value
self._value_to_key[value] = key
return key
class _PyDevJsonCommandProcessor(object):
def __init__(self, from_json):
@ -85,6 +105,7 @@ class _PyDevJsonCommandProcessor(object):
self.api = PyDevdAPI()
self._debug_options = {}
self._next_breakpoint_id = partial(next, itertools.count(0))
self._goto_targets_map = IDMap()
def process_net_command_json(self, py_db, json_contents):
'''
@ -514,5 +535,36 @@ class _PyDevJsonCommandProcessor(object):
response = pydevd_base_schema.build_response(request, kwargs=response_args)
return NetCommand(CMD_RETURN, 0, response, is_json=True)
def on_gototargets_request(self, py_db, request):
path = request.arguments.source.path
line = request.arguments.line
target_id = self._goto_targets_map.obtain_key((path, line))
target = {
'id': target_id,
'label': '{}:{}'.format(path, line),
'line': line
}
body = GotoTargetsResponseBody(targets=[target])
response_args = {'body': body}
response = pydevd_base_schema.build_response(request, kwargs=response_args)
return NetCommand(CMD_RETURN, 0, response, is_json=True)
def on_goto_request(self, py_db, request):
target_id = int(request.arguments.targetId)
thread_id = request.arguments.threadId
try:
_, line = self._goto_targets_map.obtain_value(target_id)
except KeyError:
response = pydevd_base_schema.build_response(request,
kwargs={
'body': {},
'success': False,
'message': 'Unknown goto target id: %d' % (target_id,),
})
return NetCommand(CMD_RETURN, 0, response, is_json=True)
self.api.request_set_next(py_db, thread_id, CMD_SET_NEXT_STATEMENT, line, '*')
response = pydevd_base_schema.build_response(request, kwargs={'body': {}})
return NetCommand(CMD_RETURN, 0, response, is_json=True)
process_net_command_json = _PyDevJsonCommandProcessor(pydevd_base_schema.from_json).process_net_command_json

View file

@ -1,9 +1,9 @@
def method():
a = 1
a = 1 # Step here
print('call %s' % (a,))
a = 2
print('call %s' % (a,))
a = 3
a = 3 # Break here
if __name__ == '__main__':
method()

View file

@ -1021,6 +1021,63 @@ def test_exception_details(case_setup):
writer.finished_ok = True
def test_goto(case_setup):
with case_setup.test_file('_debugger_case_set_next_statement.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
break_line = writer.get_line_index_with_content('Break here')
step_line = writer.get_line_index_with_content('Step here')
writer.write_add_breakpoint(break_line)
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit()
stack_trace_request = json_facade.write_request(
pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(threadId=hit.thread_id)))
stack_trace_response = json_facade.wait_for_response(stack_trace_request)
stack_frame = next(iter(stack_trace_response.body.stackFrames))
assert stack_frame['line'] == break_line
goto_targets_request = json_facade.write_request(
pydevd_schema.GotoTargetsRequest(pydevd_schema.GotoTargetsArguments(
source=pydevd_schema.Source(path=writer.TEST_FILE, sourceReference=0),
line=step_line)))
goto_targets_response = json_facade.wait_for_response(goto_targets_request)
target_id = goto_targets_response.body.targets[0]['id']
goto_request = json_facade.write_request(
pydevd_schema.GotoRequest(pydevd_schema.GotoArguments(
threadId=hit.thread_id,
targetId=12345)))
goto_response = json_facade.wait_for_response(goto_request)
assert not goto_response.success
goto_request = json_facade.write_request(
pydevd_schema.GotoRequest(pydevd_schema.GotoArguments(
threadId=hit.thread_id,
targetId=target_id)))
goto_response = json_facade.wait_for_response(goto_request)
hit = writer.wait_for_breakpoint_hit(reason='127')
stack_trace_request = json_facade.write_request(
pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(threadId=hit.thread_id)))
stack_trace_response = json_facade.wait_for_response(stack_trace_request)
stack_frame = next(iter(stack_trace_response.body.stackFrames))
assert stack_frame['line'] == step_line
writer.write_run_thread(hit.thread_id)
# we hit the breakpoint again. Since we moved back
hit = writer.wait_for_breakpoint_hit()
writer.write_run_thread(hit.thread_id)
writer.finished_ok = True
@pytest.mark.skipif(IS_JYTHON, reason='Flaky on Jython.')
def test_path_translation_and_source_reference(case_setup):

View file

@ -1237,7 +1237,6 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
self.new_thread_lock = threading.Lock()
# goto
self.goto_target_map = IDMap()
self.current_goto_request = None
# adapter state
@ -1761,14 +1760,12 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
@async_handler
def on_gotoTargets(self, request, args):
path = args['source']['path']
line = args['line']
target_id = self.goto_target_map.to_vscode((path, line), autogen=True)
self.send_response(request, targets=[{
'id': target_id,
'label': '{}:{}'.format(path, line),
'line': line,
}])
pydevd_request = copy.deepcopy(request)
del pydevd_request['seq'] # A new seq should be created for pydevd.
cmd_id = pydevd_comm.CMD_GET_NEXT_STATEMENT_TARGETS
_, _, resp_args = yield self.pydevd_request(cmd_id, pydevd_request, is_json=True)
self.send_response(request, targets=resp_args['body']['targets'])
@async_handler
def on_goto(self, request, args):
@ -1776,16 +1773,16 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
self.send_error_response(request, 'Already processing a "goto" request.')
return
vsc_tid = args['threadId']
target_id = args['targetId']
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
path, line = self.goto_target_map.to_pydevd(target_id)
pyd_tid = self.thread_map.to_pydevd(int(args['threadId']))
pydevd_request = copy.deepcopy(request)
del pydevd_request['seq'] # A new seq should be created for pydevd.
pydevd_request['arguments']['threadId'] = pyd_tid
self.current_goto_request = request
self.pydevd_notify(
pydevd_comm.CMD_SET_NEXT_STATEMENT,
'{}\t{}\t*'.format(pyd_tid, line))
cmd_id = pydevd_comm.CMD_SET_NEXT_STATEMENT
yield self.pydevd_request(cmd_id, pydevd_request, is_json=True)
# response for this is received via set_next_statement event
# see on_pydevd_set_next_statement below
@async_handler
def on_setBreakpoints(self, request, args):