Move stack levels to pydevd (#1351)

This commit is contained in:
Karthik Nadig 2019-04-12 19:32:51 -07:00 committed by GitHub
parent 69a76244bf
commit 46d0c7edf7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 80 additions and 37 deletions

View file

@ -117,10 +117,10 @@ class PyDevdAPI(object):
py_db.post_method_as_internal_command(
thread_id, internal_get_completions, seq, thread_id, frame_id, act_tok, line=line, column=column)
def request_stack(self, py_db, seq, thread_id, fmt=None, timeout=.5):
def request_stack(self, py_db, seq, thread_id, fmt=None, timeout=.5, start_frame=0, levels=0):
# If it's already suspended, get it right away.
internal_get_thread_stack = InternalGetThreadStack(
seq, thread_id, py_db, set_additional_thread_info, fmt=fmt, timeout=timeout)
seq, thread_id, py_db, set_additional_thread_info, fmt=fmt, timeout=timeout, start_frame=start_frame, levels=levels)
if internal_get_thread_stack.can_be_executed_by(get_current_thread_id(threading.current_thread())):
internal_get_thread_stack.do_it(py_db)
else:

View file

@ -550,13 +550,15 @@ class InternalGetThreadStack(InternalThreadCommand):
stopped in a breakpoint).
'''
def __init__(self, seq, thread_id, py_db, set_additional_thread_info, fmt, timeout=.5):
def __init__(self, seq, thread_id, py_db, set_additional_thread_info, fmt, timeout=.5, start_frame=0, levels=0):
InternalThreadCommand.__init__(self, thread_id)
self._py_db = weakref.ref(py_db)
self._timeout = time.time() + timeout
self.seq = seq
self._cmd = None
self._fmt = fmt
self._start_frame = start_frame
self._levels = levels
# Note: receives set_additional_thread_info to avoid a circular import
# in this module.
@ -574,7 +576,7 @@ class InternalGetThreadStack(InternalThreadCommand):
frame = additional_info.get_topmost_frame(t)
try:
self._cmd = py_db.cmd_factory.make_get_thread_stack_message(
py_db, self.seq, self.thread_id, frame, self._fmt, must_be_suspended=not timed_out)
py_db, self.seq, self.thread_id, frame, self._fmt, must_be_suspended=not timed_out, start_frame=self._start_frame, levels=self._levels)
finally:
frame = None
t = None

View file

@ -158,7 +158,7 @@ class NetCommandFactoryJson(NetCommandFactory):
return frame_name
@overrides(NetCommandFactory.make_get_thread_stack_message)
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False):
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False, start_frame=0, levels=0):
frames = []
module_events = []
if topmost_frame is not None:
@ -204,11 +204,18 @@ class NetCommandFactoryJson(NetCommandFactory):
for module_event in module_events:
py_db.writer.add_command(module_event)
total_frames = len(frames)
stack_frames = frames
if bool(levels):
start = start_frame
end = min(start + levels, total_frames)
stack_frames = frames[start:end]
response = pydevd_schema.StackTraceResponse(
request_seq=seq,
success=True,
command='stackTrace',
body=pydevd_schema.StackTraceResponseBody(stackFrames=frames, totalFrames=len(frames)))
body=pydevd_schema.StackTraceResponseBody(stackFrames=stack_frames, totalFrames=total_frames))
return NetCommand(CMD_RETURN, 0, response, is_json=True)
@overrides(NetCommandFactory.make_io_message)

View file

@ -87,7 +87,7 @@ class NetCommandFactory(object):
except:
return self.make_error_message(seq, get_exception_traceback_str())
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False):
def make_get_thread_stack_message(self, py_db, seq, thread_id, topmost_frame, fmt, must_be_suspended=False, start_frame=0, levels=0):
"""
Returns thread stack as XML.

View file

@ -481,11 +481,13 @@ class _PyDevJsonCommandProcessor(object):
# : :type stack_trace_arguments: StackTraceArguments
stack_trace_arguments = request.arguments
thread_id = stack_trace_arguments.threadId
start_frame = stack_trace_arguments.startFrame
levels = stack_trace_arguments.levels
fmt = stack_trace_arguments.format
if hasattr(fmt, 'to_dict'):
fmt = fmt.to_dict()
self.api.request_stack(py_db, request.seq, thread_id, fmt)
self.api.request_stack(py_db, request.seq, thread_id, fmt=fmt, start_frame=start_frame, levels=levels)
def on_exceptioninfo_request(self, py_db, request):
'''

View file

@ -0,0 +1,16 @@
def method1(n):
if n <= 0:
return 0 # Break here
method2(n - 1)
def method2(n):
method1(n - 1)
if __name__ == '__main__':
try:
method1(100)
except:
pass # Don't let it print the exception (just deal with caught exceptions).
print('TEST SUCEEDED!')

View file

@ -1182,6 +1182,41 @@ def test_exception_details(case_setup, max_frames):
writer.finished_ok = True
def test_stack_levels(case_setup):
with case_setup.test_file('_debugger_case_deep_stacks.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here'))
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit()
# get full stack
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)
full_stack_frames = stack_trace_response.body.stackFrames
total_frames = stack_trace_response.body.totalFrames
startFrame = 0
levels = 20
received_frames = []
while startFrame < total_frames:
stack_trace_request = json_facade.write_request(
pydevd_schema.StackTraceRequest(pydevd_schema.StackTraceArguments(
threadId=hit.thread_id,
startFrame=startFrame,
levels=20)))
stack_trace_response = json_facade.wait_for_response(stack_trace_request)
received_frames += stack_trace_response.body.stackFrames
startFrame += levels
assert full_stack_frames == received_frames
writer.write_run_thread(hit.thread_id)
writer.finished_ok = True
@pytest.mark.skipif(IS_JYTHON, reason='No goto on Jython.')
def test_goto(case_setup):

View file

@ -1412,7 +1412,15 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
def _forward_request_to_pydevd(self, request, args):
translate_thread_id = args.get('threadId') is not None
if translate_thread_id:
pyd_tid = self.thread_map.to_pydevd(int(args['threadId']))
try:
vsc_tid = int(args['threadId'])
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
except KeyError:
# Unknown thread, nothing much we can do about it here
self.send_error_response(
request,
'Thread {} not found'.format(vsc_tid))
return
pydevd_request = copy.deepcopy(request)
del pydevd_request['seq'] # A new seq should be created for pydevd.
@ -1480,35 +1488,8 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
else:
self.send_error_response(request, resp_args.get('message', 'Error retrieving source'))
@async_handler
def on_stackTrace(self, request, args):
vsc_tid = int(args['threadId'])
try:
pyd_tid = self.thread_map.to_pydevd(vsc_tid)
except KeyError:
# Unknown thread, nothing much we can do about it here
self.send_error_response(
request,
'Thread {} not found'.format(vsc_tid))
return
pydevd_request = copy.deepcopy(request)
del pydevd_request['seq'] # A new seq should be created for pydevd.
# Translate threadId for pydevd.
pydevd_request['arguments']['threadId'] = pyd_tid
_, _, resp_args = yield self.pydevd_request(
pydevd_comm.CMD_GET_THREAD_STACK,
pydevd_request,
is_json=True)
levels = int(args.get('levels', 0))
stackFrames = resp_args['body']['stackFrames']
if levels > 0:
start = int(args.get('startFrame', 0))
end = min(start + levels, len(stackFrames))
stackFrames = resp_args['body']['stackFrames'][start:end]
totalFrames = resp_args['body']['totalFrames']
self.send_response(request, stackFrames=stackFrames, totalFrames=totalFrames)
self._forward_request_to_pydevd(request, args)
def on_scopes(self, request, args):
self._forward_request_to_pydevd(request, args)