mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
Make it possible to evaluate without a frameId. Fixes #1716
This commit is contained in:
parent
2a367b2f4b
commit
f45444f9f9
3 changed files with 87 additions and 46 deletions
|
|
@ -897,10 +897,14 @@ def _evaluate_response(py_db, request, result, error_message=''):
|
|||
py_db.writer.add_command(NetCommand(CMD_RETURN, 0, variables_response, is_json=True))
|
||||
|
||||
|
||||
_global_frame = None
|
||||
|
||||
|
||||
def internal_evaluate_expression_json(py_db, request, thread_id):
|
||||
'''
|
||||
:param EvaluateRequest request:
|
||||
'''
|
||||
global _global_frame
|
||||
# : :type arguments: EvaluateArguments
|
||||
|
||||
arguments = request.arguments
|
||||
|
|
@ -918,28 +922,43 @@ def internal_evaluate_expression_json(py_db, request, thread_id):
|
|||
_evaluate_response(py_db, request, '', error_message='Expression is not valid utf-8.')
|
||||
raise
|
||||
|
||||
frame = py_db.find_frame(thread_id, frame_id)
|
||||
result = pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=False)
|
||||
is_error = isinstance(result, ExceptionOnEvaluate)
|
||||
try_exec = False
|
||||
if frame_id is None:
|
||||
if _global_frame is None:
|
||||
# Lazily create a frame to be used for evaluation with no frame id.
|
||||
|
||||
if is_error:
|
||||
if context == 'hover':
|
||||
_evaluate_response(py_db, request, result='')
|
||||
return
|
||||
def __create_frame():
|
||||
yield sys._getframe()
|
||||
|
||||
elif context == 'repl':
|
||||
try:
|
||||
pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=True)
|
||||
except Exception as ex:
|
||||
err = ''.join(traceback.format_exception_only(type(ex), ex))
|
||||
# Currently there is an issue in VSC where returning success=false for an
|
||||
# eval request, in repl context, VSC does not show the error response in
|
||||
# the debug console. So return the error message in result as well.
|
||||
_evaluate_response(py_db, request, result=err, error_message=err)
|
||||
_global_frame = next(__create_frame())
|
||||
|
||||
frame = _global_frame
|
||||
try_exec = True # Always exec in this case
|
||||
eval_result = None
|
||||
else:
|
||||
frame = py_db.find_frame(thread_id, frame_id)
|
||||
eval_result = pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=False)
|
||||
is_error = isinstance(eval_result, ExceptionOnEvaluate)
|
||||
if is_error:
|
||||
if context == 'hover': # In a hover it doesn't make sense to do an exec.
|
||||
_evaluate_response(py_db, request, result='')
|
||||
return
|
||||
# No result on exec.
|
||||
_evaluate_response(py_db, request, result='')
|
||||
else:
|
||||
try_exec = context == 'repl'
|
||||
|
||||
if try_exec:
|
||||
try:
|
||||
pydevd_vars.evaluate_expression(py_db, frame, expression, is_exec=True)
|
||||
except Exception as ex:
|
||||
err = ''.join(traceback.format_exception_only(type(ex), ex))
|
||||
# Currently there is an issue in VSC where returning success=false for an
|
||||
# eval request, in repl context, VSC does not show the error response in
|
||||
# the debug console. So return the error message in result as well.
|
||||
_evaluate_response(py_db, request, result=err, error_message=err)
|
||||
return
|
||||
# No result on exec.
|
||||
_evaluate_response(py_db, request, result='')
|
||||
return
|
||||
|
||||
# Ok, we have the result (could be an error), let's put it into the saved variables.
|
||||
frame_tracker = py_db.suspended_frames_manager.get_frame_tracker(thread_id)
|
||||
|
|
@ -948,7 +967,7 @@ def internal_evaluate_expression_json(py_db, request, thread_id):
|
|||
_evaluate_response(py_db, request, result='', error_message='Thread id: %s is not current thread id.' % (thread_id,))
|
||||
return
|
||||
|
||||
variable = frame_tracker.obtain_as_variable(expression, result, frame=frame)
|
||||
variable = frame_tracker.obtain_as_variable(expression, eval_result, frame=frame)
|
||||
var_data = variable.get_var_data(fmt=fmt)
|
||||
|
||||
body = pydevd_schema.EvaluateResponseBody(
|
||||
|
|
|
|||
|
|
@ -701,22 +701,25 @@ class _PyDevJsonCommandProcessor(object):
|
|||
# : :type arguments: EvaluateArguments
|
||||
arguments = request.arguments
|
||||
|
||||
thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference(
|
||||
arguments.frameId)
|
||||
|
||||
if thread_id is not None:
|
||||
self.api.request_exec_or_evaluate_json(
|
||||
py_db, request, thread_id)
|
||||
if arguments.frameId is None:
|
||||
self.api.request_exec_or_evaluate_json(py_db, request, thread_id='*')
|
||||
else:
|
||||
body = EvaluateResponseBody('', 0)
|
||||
response = pydevd_base_schema.build_response(
|
||||
request,
|
||||
kwargs={
|
||||
'body': body,
|
||||
'success': False,
|
||||
'message': 'Unable to find thread for evaluation.'
|
||||
})
|
||||
return NetCommand(CMD_RETURN, 0, response, is_json=True)
|
||||
thread_id = py_db.suspended_frames_manager.get_thread_id_for_variable_reference(
|
||||
arguments.frameId)
|
||||
|
||||
if thread_id is not None:
|
||||
self.api.request_exec_or_evaluate_json(
|
||||
py_db, request, thread_id)
|
||||
else:
|
||||
body = EvaluateResponseBody('', 0)
|
||||
response = pydevd_base_schema.build_response(
|
||||
request,
|
||||
kwargs={
|
||||
'body': body,
|
||||
'success': False,
|
||||
'message': 'Unable to find thread for evaluation.'
|
||||
})
|
||||
return NetCommand(CMD_RETURN, 0, response, is_json=True)
|
||||
|
||||
def on_setexpression_request(self, py_db, request):
|
||||
# : :type arguments: SetExpressionArguments
|
||||
|
|
|
|||
|
|
@ -416,6 +416,14 @@ class JsonFacade(object):
|
|||
assert isinstance(process_id, int)
|
||||
return response
|
||||
|
||||
def evaluate(self, expression, frameId=None, context=None, fmt=None, success=True):
|
||||
eval_request = self.write_request(
|
||||
pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments(
|
||||
expression, frameId=frameId, context=context, format=fmt)))
|
||||
eval_response = self.wait_for_response(eval_request)
|
||||
assert eval_response.success == success
|
||||
return eval_response
|
||||
|
||||
|
||||
def test_case_json_logpoints(case_setup):
|
||||
with case_setup.test_file('_debugger_case_change_breaks.py') as writer:
|
||||
|
|
@ -1803,25 +1811,36 @@ def test_evaluate(case_setup):
|
|||
stack_frame_id = stack_frame['id']
|
||||
|
||||
# Test evaluate request that results in 'eval'
|
||||
eval_request = json_facade.write_request(
|
||||
pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1', frameId=stack_frame_id, context='repl')))
|
||||
eval_response = json_facade.wait_for_response(eval_request)
|
||||
eval_response = json_facade.evaluate('var_1', frameId=stack_frame_id, context='repl')
|
||||
assert eval_response.body.result == '5'
|
||||
assert eval_response.body.type == 'int'
|
||||
|
||||
# Test evaluate request that results in 'exec'
|
||||
exec_request = json_facade.write_request(
|
||||
pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1 = 6', frameId=stack_frame_id, context='repl')))
|
||||
exec_response = json_facade.wait_for_response(exec_request)
|
||||
exec_response = json_facade.evaluate('var_1 = 6', frameId=stack_frame_id, context='repl')
|
||||
assert exec_response.body.result == ''
|
||||
|
||||
# Test evaluate request that results in 'exec' but fails
|
||||
exec_request = json_facade.write_request(
|
||||
pydevd_schema.EvaluateRequest(pydevd_schema.EvaluateArguments('var_1 = "abc"/6', frameId=stack_frame_id, context='repl')))
|
||||
exec_response = json_facade.wait_for_response(exec_request)
|
||||
assert exec_response.success == False
|
||||
assert exec_response.body.result.find('TypeError') > -1
|
||||
assert exec_response.message.find('TypeError') > -1
|
||||
exec_response = json_facade.evaluate(
|
||||
'var_1 = "abc"/6', frameId=stack_frame_id, context='repl', success=False)
|
||||
assert 'TypeError' in exec_response.body.result
|
||||
assert 'TypeError' in exec_response.message
|
||||
|
||||
# Evaluate without a frameId.
|
||||
|
||||
# Error because 'foo_value' is not set in 'sys'.
|
||||
exec_response = json_facade.evaluate('import email;email.foo_value', success=False)
|
||||
assert 'AttributeError' in exec_response.body.result
|
||||
assert 'AttributeError' in exec_response.message
|
||||
|
||||
# Reading foo_value didn't work, but 'email' should be in the namespace now.
|
||||
json_facade.evaluate('email.foo_value=True')
|
||||
|
||||
# Ok, 'foo_value' is now set in 'email' module.
|
||||
exec_response = json_facade.evaluate('email.foo_value')
|
||||
|
||||
# We don't actually get variables without a frameId, we can just evaluate and observe side effects
|
||||
# (so, the result is always empty -- or an error).
|
||||
assert exec_response.body.result == ''
|
||||
|
||||
json_facade.write_continue(wait_for_response=False)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue