Show return values (#761)

* Add show return value feature

* Add show return value tests

* Fix linter

* Check presentation hints in tests
This commit is contained in:
Karthik Nadig 2018-08-27 12:58:45 -07:00 committed by GitHub
parent 634926b6a8
commit c68cd7be64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 169 additions and 13 deletions

View file

@ -704,6 +704,7 @@ DEBUG_OPTIONS_PARSER = {
'CLIENT_OS_TYPE': unquote,
'DEBUG_STDLIB': bool_parser,
'STOP_ON_ENTRY': bool_parser,
'SHOW_RETURN_VALUE': bool_parser,
}
@ -719,6 +720,7 @@ DEBUG_OPTIONS_BY_FLAG = {
'WindowsClient': 'CLIENT_OS_TYPE=WINDOWS',
'UnixClient': 'CLIENT_OS_TYPE=UNIX',
'StopOnEntry': 'STOP_ON_ENTRY=True',
'ShowReturnValue': 'SHOW_RETURN_VALUE=True',
}
@ -1362,6 +1364,9 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
if opts.get('STOP_ON_ENTRY', False) and self.start_reason == 'launch':
self.pydevd_request(pydevd_comm.CMD_STOP_ON_START, '1')
if opts.get('SHOW_RETURN_VALUE', False):
self.pydevd_request(pydevd_comm.CMD_SHOW_RETURN_VALUES, '1\t1')
self._apply_code_stepping_settings()
def _is_just_my_code_stepping_enabled(self):
@ -1708,6 +1713,7 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
variables = VariablesSorter()
for xvar in xvars:
attributes = []
var_name = unquote(xvar['name'])
var_type = unquote(xvar['type'])
var_value = unquote(xvar['value'])
@ -1718,16 +1724,24 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
}
if self._is_raw_string(var_type):
var['presentationHint'] = {'attributes': ['rawString']}
attributes.append('rawString')
if bool(xvar['isContainer']):
pyd_child = pyd_var + (var_name,)
var['variablesReference'] = self.var_map.to_vscode(
pyd_child, autogen=True)
if bool(xvar['isRetVal']):
attributes.append('readOnly')
var['name'] = '(return) %s' % var_name
else:
if bool(xvar['isContainer']):
pyd_child = pyd_var + (var_name,)
var['variablesReference'] = self.var_map.to_vscode(
pyd_child, autogen=True)
eval_name = self._get_variable_evaluate_name(pyd_var, var_name)
if eval_name:
var['evaluateName'] = eval_name
eval_name = self._get_variable_evaluate_name(
pyd_var, var_name)
if eval_name:
var['evaluateName'] = eval_name
if len(attributes) > 0:
var['presentationHint'] = {'attributes': attributes}
variables.append(var)
@ -1779,18 +1793,23 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
@async_handler
def on_setVariable(self, request, args):
"""Handles DAP SetVariableRequest."""
var_name = args['name']
var_value = args['value']
vsc_var = int(args['variablesReference'])
fmt = args.get('format', {})
if var_name.startswith('(return) '):
self.send_error_response(
request,
'Cannot change return value.')
return
try:
pyd_var = self.var_map.to_pydevd(vsc_var)
except KeyError:
self.send_error_response(request)
return
var_name = args['name']
var_value = args['value']
fmt = args.get('format', {})
lhs_expr = self._get_variable_evaluate_name(pyd_var, var_name)
if not lhs_expr:
lhs_expr = var_name

View file

@ -0,0 +1,12 @@
class MyClass(object):
def do_something(self):
return 'did something'
def my_func():
return 'did more things'
MyClass().do_something()
my_func()
print('done')

View file

@ -198,3 +198,128 @@ class StopOnEntryLaunchPackageTests(StopOnEntryTests):
modulename='mymod',
cwd=cwd,
env=env))
class ShowReturnValueTests(LifecycleTestsBase):
def _debugger_step_next(self, session, thread_id):
stopped = session.get_awaiter_for_event('stopped')
req_next = session.send_request('next', threadId=thread_id)
req_next.wait(timeout=2.0)
stopped.wait(timeout=2.0)
def _get_variables(self, session, thread_id):
req_stacktrace = session.send_request(
'stackTrace',
threadId=thread_id,
)
req_stacktrace.wait(timeout=2.0)
frames = req_stacktrace.resp.body['stackFrames']
frame_id = frames[0]['id']
req_scopes = session.send_request(
'scopes',
frameId=frame_id,
)
req_scopes.wait(timeout=2.0)
scopes = req_scopes.resp.body['scopes']
variables_reference = scopes[0]['variablesReference']
req_variables = session.send_request(
'variables',
variablesReference=variables_reference,
)
req_variables.wait(timeout=2.0)
return req_variables.resp.body['variables']
def run_test_return_values(self, debug_info, is_enabled):
if is_enabled:
options = {'debugOptions': ['RedirectOutput', 'ShowReturnValue']}
else:
options = {'debugOptions': ['RedirectOutput']}
breakpoints = [{
'source': {
'path': debug_info.filename
},
'breakpoints': [{'line': 10}, {'line': 11}, {'line': 12}],
'lines': [10, 11, 12]
}]
with self.start_debugging(debug_info) as dbg:
session = dbg.session
stopped = session.get_awaiter_for_event('stopped')
(_, req_launch_attach, _, _, _, _
) = lifecycle_handshake(session, debug_info.starttype,
options=options,
breakpoints=breakpoints)
req_launch_attach.wait(timeout=3.0)
stopped.wait()
thread_id = stopped.event.body['threadId']
self._debugger_step_next(session, thread_id)
variables = self._get_variables(session, thread_id)
return_values = list(v for v in variables
if v['name'].startswith('(return) '))
if is_enabled:
self.assertEqual(len(return_values), 1)
self.assertEqual(return_values[0]['name'],
'(return) MyClass.do_something')
self.assertEqual(return_values[0]['value'],
"'did something'")
self.assertEqual(return_values[0]['type'],
'str')
attributes = return_values[0]['presentationHint']['attributes']
self.assertTrue('readOnly' in attributes)
else:
self.assertEqual(len(return_values), 0)
self._debugger_step_next(session, thread_id)
variables = self._get_variables(session, thread_id)
return_values = list(v for v in variables
if v['name'].startswith('(return) '))
if is_enabled:
self.assertEqual(len(return_values), 2)
self.assertEqual(return_values[0]['name'],
'(return) MyClass.do_something')
self.assertEqual(return_values[0]['value'],
"'did something'")
self.assertEqual(return_values[0]['type'],
'str')
attributes = return_values[0]['presentationHint']['attributes']
self.assertTrue('readOnly' in attributes)
self.assertEqual(return_values[1]['name'],
'(return) my_func')
self.assertEqual(return_values[1]['value'],
"'did more things'")
self.assertEqual(return_values[1]['type'],
'str')
attributes = return_values[1]['presentationHint']['attributes']
self.assertTrue('readOnly' in attributes)
else:
self.assertEqual(len(return_values), 0)
exited = session.get_awaiter_for_event('exited')
session.send_request('continue').wait(timeout=2.0)
exited.wait(timeout=3.0)
def test_return_values_enabled(self):
filename = TEST_FILES.resolve('returnvalues.py')
cwd = os.path.dirname(filename)
self.run_test_return_values(
DebugInfo(filename=filename, cwd=cwd),
is_enabled=True)
def test_return_values_disabled(self):
filename = TEST_FILES.resolve('returnvalues.py')
cwd = os.path.dirname(filename)
self.run_test_return_values(
DebugInfo(filename=filename, cwd=cwd),
is_enabled=False)