Skip to content

Commit

Permalink
Properly translate line in Goto Target. Fixes microsoft#150
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jan 15, 2021
1 parent aa36155 commit 90b2495
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 5 deletions.
24 changes: 23 additions & 1 deletion src/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,31 @@ def request_step(self, py_db, thread_id, step_cmd_id):
elif thread_id.startswith('__frame__:'):
sys.stderr.write("Can't make tasklet step command: %s\n" % (thread_id,))

def request_set_next(self, py_db, seq, thread_id, set_next_cmd_id, line, func_name):
def request_set_next(self, py_db, seq, thread_id, set_next_cmd_id, original_filename, line, func_name):
'''
:param Optional[str] original_filename:
If available, the filename may be source translated, otherwise no translation will take
place (the set next just needs the line afterwards as it executes locally, but for
the Jupyter integration, the source mapping may change the actual lines and not only
the filename).
'''
t = pydevd_find_thread_by_id(thread_id)
if t:
if original_filename is not None:
translated_filename = self.filename_to_server(original_filename) # Apply user path mapping.
pydev_log.debug('Set next (after path translation) in: %s line: %s', translated_filename, line)
func_name = self.to_str(func_name)

assert translated_filename.__class__ == str # i.e.: bytes on py2 and str on py3
assert func_name.__class__ == str # i.e.: bytes on py2 and str on py3

# Apply source mapping (i.e.: ipython).
_source_mapped_filename, new_line, multi_mapping_applied = py_db.source_mapping.map_to_server(
translated_filename, line)
if multi_mapping_applied:
pydev_log.debug('Set next (after source mapping) in: %s line: %s', translated_filename, line)
line = new_line

int_cmd = InternalSetNextStatementThread(thread_id, set_next_cmd_id, line, func_name, seq=seq)
py_db.post_internal_command(int_cmd, thread_id)
elif thread_id.startswith('__frame__:'):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def _cmd_step(self, py_db, cmd_id, seq, text):

def _cmd_set_next(self, py_db, cmd_id, seq, text):
thread_id, line, func_name = text.split('\t', 2)
return self.api.request_set_next(py_db, seq, thread_id, cmd_id, line, func_name)
return self.api.request_set_next(py_db, seq, thread_id, cmd_id, None, line, func_name)

cmd_run_to_line = _cmd_set_next
cmd_set_next_statement = _cmd_set_next
Expand Down Expand Up @@ -366,7 +366,6 @@ def cmd_set_path_mapping_json(self, py_db, cmd_id, seq, text):
if debug or force:
pydevd_file_utils.DEBUG_CLIENT_SERVER_TRANSLATION = debug


def cmd_set_py_exception_json(self, py_db, cmd_id, seq, text):
# This API is optional and works 'in bulk' -- it's possible
# to get finer-grained control with CMD_ADD_EXCEPTION_BREAK/CMD_REMOVE_EXCEPTION_BREAK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ 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)
path, line = self._goto_targets_map.obtain_value(target_id)
except KeyError:
response = pydevd_base_schema.build_response(
request,
Expand All @@ -1005,7 +1005,7 @@ def on_goto_request(self, py_db, request):
})
return NetCommand(CMD_RETURN, 0, response, is_json=True)

self.api.request_set_next(py_db, request.seq, thread_id, CMD_SET_NEXT_STATEMENT, line, '*')
self.api.request_set_next(py_db, request.seq, thread_id, CMD_SET_NEXT_STATEMENT, path, line, '*')
# See 'NetCommandFactoryJson.make_set_next_stmnt_status_message' for response
return None

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Some comment lines to move the function below
# Some comment lines to move the function below
# Some comment lines to move the function below
# Some comment lines to move the function below


def full_function():
# Note that this function is not called, it's there just to make the mapping explicit.
# The test case should stop at `a = 1` and then skip the `print('Skip this print')`.
# map to Cell1, line 1
a = 1 # map to Cell1, line 2
print('Skip this print') # map to Cell1, line 3
print('TEST SUCEEDED') # map to Cell1, line 4
b = 2 # map to Cell1, line 5


if __name__ == '__main__':
code = compile('''# line 1
a = 1 # line 2
print('Skip this print') # line 3
print('TEST SUCEEDED') # line 4
b = 2 # line 5
''', '<Cell1>', 'exec')
exec(code)
54 changes: 54 additions & 0 deletions src/debugpy/_vendored/pydevd/tests_python/test_debugger_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -3293,6 +3293,60 @@ def test_source_mapping_just_my_code(case_setup):
writer.finished_ok = True


def test_source_mapping_goto_target(case_setup):
from _pydevd_bundle._debug_adapter.pydevd_schema import Source
from _pydevd_bundle._debug_adapter.pydevd_schema import PydevdSourceMap

with case_setup.test_file('_debugger_case_source_map_goto_target.py') as writer:
test_file = writer.TEST_FILE
if isinstance(test_file, bytes):
# file is in the filesystem encoding (needed for launch) but protocol needs it in utf-8
test_file = test_file.decode(file_system_encoding)
test_file = test_file.encode('utf-8')

json_facade = JsonFacade(writer)
json_facade.write_launch(justMyCode=False)

map_to_cell_1_line1 = writer.get_line_index_with_content('map to Cell1, line 1')
map_to_cell_1_line2 = writer.get_line_index_with_content('map to Cell1, line 2')
map_to_cell_1_line4 = writer.get_line_index_with_content('map to Cell1, line 4')
map_to_cell_1_line5 = writer.get_line_index_with_content('map to Cell1, line 5')

cell1_map = PydevdSourceMap(map_to_cell_1_line1, map_to_cell_1_line5, Source(path='<Cell1>'), 1)
pydevd_source_maps = [cell1_map]
json_facade.write_set_pydevd_source_map(
Source(path=test_file),
pydevd_source_maps=pydevd_source_maps,
)
json_facade.write_set_breakpoints(map_to_cell_1_line2)

json_facade.write_make_initial_run()

json_hit = json_facade.wait_for_thread_stopped(line=map_to_cell_1_line2, file=os.path.basename(test_file))
for stack_frame in json_hit.stack_trace_response.body.stackFrames:
assert stack_frame['source']['sourceReference'] == 0

goto_targets_request = json_facade.write_request(
pydevd_schema.GotoTargetsRequest(pydevd_schema.GotoTargetsArguments(
source=pydevd_schema.Source(path=writer.TEST_FILE, sourceReference=0),
line=map_to_cell_1_line4)))
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=json_hit.thread_id,
targetId=target_id)))
goto_response = json_facade.wait_for_response(goto_request)
assert goto_response.success

json_hit = json_facade.wait_for_thread_stopped('goto')

json_facade.write_continue()

writer.finished_ok = True


@pytest.mark.skipif(not TEST_CHERRYPY or IS_WINDOWS, reason='No CherryPy available / not ok in Windows.')
def test_process_autoreload_cherrypy(case_setup_multiprocessing, tmpdir):
'''
Expand Down

0 comments on commit 90b2495

Please sign in to comment.