Properly translate line in Goto Target. Fixes #150

This commit is contained in:
Fabio Zadrozny 2021-01-15 08:30:05 -03:00
parent aa36155221
commit 44a07d030e
5 changed files with 108 additions and 5 deletions

View file

@ -228,9 +228,31 @@ class PyDevdAPI(object):
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__:'):

View file

@ -163,7 +163,7 @@ class _PyDevCommandProcessor(object):
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
@ -366,7 +366,6 @@ class _PyDevCommandProcessor(object):
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

View file

@ -994,7 +994,7 @@ class PyDevJsonCommandProcessor(object):
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,
@ -1005,7 +1005,7 @@ class PyDevJsonCommandProcessor(object):
})
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

View file

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

View file

@ -3293,6 +3293,64 @@ 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
def additional_output_checks(writer, stdout, stderr):
assert 'Skip this print' not in stdout
assert 'TEST SUCEEDED' in stdout
with case_setup.test_file('_debugger_case_source_map_goto_target.py', additional_output_checks=additional_output_checks) 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):
'''