Port stopped and continued events to pydevd. Fixes #1340, #1341 (#1373)

* Port stopped and continued events to pydevd. Fixes #1340, #1341

* Use 'ptvsd.log.exception' instead of traceback.print_exc.
This commit is contained in:
Fabio Zadrozny 2019-04-19 09:09:52 -03:00 committed by GitHub
parent 5dbba4ac28
commit 60da1fee41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 363 additions and 289 deletions

View file

@ -1079,98 +1079,106 @@ def internal_get_description(dbg, seq, thread_id, frame_id, expression):
dbg.writer.add_command(cmd)
def build_exception_info_response(dbg, thread_id, request_seq, set_additional_thread_info, iter_visible_frames_info, max_frames):
'''
:return ExceptionInfoResponse
'''
thread = pydevd_find_thread_by_id(thread_id)
additional_info = set_additional_thread_info(thread)
topmost_frame = additional_info.get_topmost_frame(thread)
frames = []
exc_type = None
exc_desc = None
if topmost_frame is not None:
frame_id_to_lineno = {}
try:
trace_obj = None
frame = topmost_frame
while frame is not None:
if frame.f_code.co_name == 'do_wait_suspend' and frame.f_code.co_filename.endswith('pydevd.py'):
arg = frame.f_locals.get('arg', None)
if arg is not None:
exc_type, exc_desc, trace_obj = arg
break
frame = frame.f_back
while trace_obj.tb_next is not None:
trace_obj = trace_obj.tb_next
info = dbg.suspended_frames_manager.get_topmost_frame_and_frame_id_to_line(thread_id)
if info is not None:
topmost_frame, frame_id_to_lineno = info
if trace_obj is not None:
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno in iter_visible_frames_info(
dbg, trace_obj.tb_frame, frame_id_to_lineno):
line_text = linecache.getline(original_filename, lineno)
# Never filter out plugin frames!
if not getattr(frame, 'IS_PLUGIN_FRAME', False):
if not dbg.in_project_scope(original_filename):
if not dbg.get_use_libraries_filter():
continue
frames.append((filename_in_utf8, lineno, method_name, line_text))
finally:
topmost_frame = None
name = 'exception: type unknown'
if exc_type is not None:
try:
name = exc_type.__qualname__
except:
try:
name = exc_type.__name__
except:
try:
name = str(exc_type)
except:
pass
description = 'exception: no description'
if exc_desc is not None:
try:
description = str(exc_desc)
except:
pass
stack_str = ''.join(traceback.format_list(frames[-max_frames:]))
# This is an extra bit of data used by Visual Studio
source_path = frames[0][0] if frames else ''
if thread.stop_reason == CMD_STEP_CAUGHT_EXCEPTION:
break_mode = pydevd_schema.ExceptionBreakMode.ALWAYS
else:
break_mode = pydevd_schema.ExceptionBreakMode.UNHANDLED
response = pydevd_schema.ExceptionInfoResponse(
request_seq=request_seq,
success=True,
command='exceptionInfo',
body=pydevd_schema.ExceptionInfoResponseBody(
exceptionId=name,
description=description,
breakMode=break_mode,
details=pydevd_schema.ExceptionDetails(
message=description,
typeName=name,
stackTrace=stack_str,
source=source_path
)
)
)
return response
def internal_get_exception_details_json(dbg, request, thread_id, max_frames, set_additional_thread_info=None, iter_visible_frames_info=None):
''' Fetch exception details
'''
try:
thread = pydevd_find_thread_by_id(thread_id)
additional_info = set_additional_thread_info(thread)
topmost_frame = additional_info.get_topmost_frame(thread)
frames = []
exc_type = None
exc_desc = None
if topmost_frame is not None:
frame_id_to_lineno = {}
try:
trace_obj = None
frame = topmost_frame
while frame is not None:
if frame.f_code.co_name == 'do_wait_suspend' and frame.f_code.co_filename.endswith('pydevd.py'):
arg = frame.f_locals.get('arg', None)
if arg is not None:
exc_type, exc_desc, trace_obj = arg
break
frame = frame.f_back
while trace_obj.tb_next is not None:
trace_obj = trace_obj.tb_next
info = dbg.suspended_frames_manager.get_topmost_frame_and_frame_id_to_line(thread_id)
if info is not None:
topmost_frame, frame_id_to_lineno = info
if trace_obj is not None:
for frame_id, frame, method_name, original_filename, filename_in_utf8, lineno in iter_visible_frames_info(
dbg, trace_obj.tb_frame, frame_id_to_lineno):
line_text = linecache.getline(original_filename, lineno)
# Never filter out plugin frames!
if not getattr(frame, 'IS_PLUGIN_FRAME', False):
if not dbg.in_project_scope(original_filename):
if not dbg.get_use_libraries_filter():
continue
frames.append((filename_in_utf8, lineno, method_name, line_text))
finally:
topmost_frame = None
name = 'exception: type unknown'
if exc_type is not None:
try:
name = exc_type.__qualname__
except:
try:
name = exc_type.__name__
except:
try:
name = str(exc_type)
except:
pass
description = 'exception: no description'
if exc_desc is not None:
try:
description = str(exc_desc)
except:
pass
stack_str = ''.join(traceback.format_list(frames[-max_frames:]))
# This is an extra bit of data used by Visual Studio
source_path = frames[0][0] if frames else ''
if thread.stop_reason == CMD_STEP_CAUGHT_EXCEPTION:
break_mode = pydevd_schema.ExceptionBreakMode.ALWAYS
else:
break_mode = pydevd_schema.ExceptionBreakMode.UNHANDLED
response = pydevd_schema.ExceptionInfoResponse(
request_seq=request.seq,
success=True,
command='exceptionInfo',
body=pydevd_schema.ExceptionInfoResponseBody(
exceptionId=name,
description=description,
breakMode=break_mode,
details=pydevd_schema.ExceptionDetails(
message=description,
typeName=name,
stackTrace=stack_str,
source=source_path
)
)
)
response = build_exception_info_response(dbg, thread_id, request.seq, set_additional_thread_info, iter_visible_frames_info, max_frames)
except:
exc = get_exception_traceback_str()
response = pydevd_base_schema.build_response(request, kwargs={

View file

@ -1,22 +1,27 @@
from functools import partial
import itertools
import os
from _pydev_bundle._pydev_imports_tipper import TYPE_IMPORT, TYPE_CLASS, TYPE_FUNCTION, TYPE_ATTR, \
TYPE_BUILTIN, TYPE_PARAM
from _pydev_bundle.pydev_is_thread_alive import is_thread_alive
from _pydev_bundle.pydev_override import overrides
from _pydev_imps._pydev_saved_modules import threading
from _pydevd_bundle._debug_adapter import pydevd_schema
from _pydevd_bundle._debug_adapter.pydevd_schema import ModuleEvent, ModuleEventBody, Module, \
OutputEventBody, OutputEvent, ContinuedEventBody
from _pydevd_bundle.pydevd_comm_constants import CMD_THREAD_CREATE, CMD_RETURN, CMD_MODULE_EVENT, \
CMD_WRITE_TO_CONSOLE
CMD_WRITE_TO_CONSOLE, CMD_STEP_INTO, CMD_STEP_INTO_MY_CODE, CMD_STEP_OVER, CMD_STEP_OVER_MY_CODE, \
CMD_STEP_RETURN, CMD_STEP_CAUGHT_EXCEPTION, CMD_ADD_EXCEPTION_BREAK, CMD_SET_BREAK, \
CMD_SET_NEXT_STATEMENT, CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION, \
CMD_THREAD_RESUME_SINGLE_NOTIFICATION
from _pydevd_bundle.pydevd_constants import get_thread_id, dict_values
from _pydevd_bundle.pydevd_net_command import NetCommand
from _pydevd_bundle.pydevd_net_command_factory_xml import NetCommandFactory
from _pydevd_bundle.pydevd_utils import get_non_pydevd_threads
from _pydev_imps._pydev_saved_modules import threading
from _pydevd_bundle._debug_adapter.pydevd_schema import ModuleEvent, ModuleEventBody, Module, \
OutputEventBody, OutputEvent
from functools import partial
import itertools
import pydevd_file_utils
from _pydevd_bundle.pydevd_comm import pydevd_find_thread_by_id, build_exception_info_response
from _pydevd_bundle.pydevd_additional_thread_info_regular import set_additional_thread_info
class ModulesManager(object):
@ -224,3 +229,56 @@ class NetCommandFactoryJson(NetCommandFactory):
body = OutputEventBody(v, category)
event = OutputEvent(body)
return NetCommand(CMD_WRITE_TO_CONSOLE, 0, event, is_json=True)
_STEP_REASONS = set([
CMD_STEP_INTO,
CMD_STEP_INTO_MY_CODE,
CMD_STEP_OVER,
CMD_STEP_OVER_MY_CODE,
CMD_STEP_RETURN,
CMD_STEP_INTO_MY_CODE,
])
_EXCEPTION_REASONS = set([
CMD_STEP_CAUGHT_EXCEPTION,
CMD_ADD_EXCEPTION_BREAK,
])
@overrides(NetCommandFactory.make_thread_suspend_single_notification)
def make_thread_suspend_single_notification(self, py_db, thread_id, stop_reason):
exc_desc = None
exc_name = None
if stop_reason in self._STEP_REASONS:
stop_reason = 'step'
elif stop_reason in self._EXCEPTION_REASONS:
stop_reason = 'exception'
elif stop_reason == CMD_SET_BREAK:
stop_reason = 'breakpoint'
elif stop_reason == CMD_SET_NEXT_STATEMENT:
stop_reason = 'goto'
else:
stop_reason = 'pause'
if stop_reason == 'exception':
exception_info_response = build_exception_info_response(
py_db, thread_id, -1, set_additional_thread_info, self._iter_visible_frames_info, max_frames=-1)
exception_info_response
exc_name = exception_info_response.body.exceptionId
exc_desc = exception_info_response.body.description
body = pydevd_schema.StoppedEventBody(
reason=stop_reason,
description=exc_desc,
threadId=thread_id,
text=exc_name,
allThreadsStopped=True,
preserveFocusHint=stop_reason not in ['step', 'exception', 'breakpoint'],
)
event = pydevd_schema.StoppedEvent(body)
return NetCommand(CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION, 0, event, is_json=True)
@overrides(NetCommandFactory.make_thread_resume_single_notification)
def make_thread_resume_single_notification(self, thread_id):
body = ContinuedEventBody(threadId=thread_id, allThreadsContinued=True)
event = pydevd_schema.ContinuedEvent(body)
return NetCommand(CMD_THREAD_RESUME_SINGLE_NOTIFICATION, 0, event, is_json=True)

View file

@ -273,7 +273,7 @@ class NetCommandFactory(object):
except:
return self.make_error_message(0, get_exception_traceback_str())
def make_thread_suspend_single_notification(self, thread_id, stop_reason):
def make_thread_suspend_single_notification(self, py_db, thread_id, stop_reason):
try:
return NetCommand(CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION, 0, json.dumps(
{'thread_id': thread_id, 'stop_reason':stop_reason}))

View file

@ -759,10 +759,12 @@ class _PyDevJsonCommandProcessor(object):
start_patterns = tuple(args['dontTraceStartPatterns'])
end_patterns = tuple(args['dontTraceEndPatterns'])
if self._can_set_dont_trace_pattern(py_db, start_patterns, end_patterns):
def dont_trace_files_property_request(abs_path):
result = abs_path.startswith(start_patterns) or \
abs_path.endswith(end_patterns)
return result
dont_trace_files_property_request.start_patterns = start_patterns
dont_trace_files_property_request.end_patterns = end_patterns
py_db.dont_trace_external_files = dont_trace_files_property_request
@ -788,4 +790,5 @@ class _PyDevJsonCommandProcessor(object):
response = pydevd_base_schema.build_response(request, kwargs={'body': {}})
return NetCommand(CMD_RETURN, 0, response, is_json=True)
process_net_command_json = _PyDevJsonCommandProcessor(pydevd_base_schema.from_json).process_net_command_json

View file

@ -139,6 +139,7 @@ file_system_encoding = getfilesystemencoding()
_CACHE_FILE_TYPE = {}
#=======================================================================================================================
# PyDBCommandThread
#=======================================================================================================================
@ -338,7 +339,7 @@ class ThreadsSuspendedSingleNotification(AbstractSingleNotificationBehavior):
def send_suspend_notification(self, thread_id, stop_reason):
py_db = self._py_db()
if py_db is not None:
py_db.writer.add_command(py_db.cmd_factory.make_thread_suspend_single_notification(thread_id, stop_reason))
py_db.writer.add_command(py_db.cmd_factory.make_thread_suspend_single_notification(py_db, thread_id, stop_reason))
@overrides(AbstractSingleNotificationBehavior.notify_thread_suspended)
@contextmanager
@ -598,7 +599,7 @@ class PyDB(object):
return file_type
def is_cache_file_type_empty(self):
return bool(_CACHE_FILE_TYPE)
return not _CACHE_FILE_TYPE
def get_thread_local_trace_func(self):
try:

View file

@ -1,7 +1,17 @@
from _debugger_case_dont_trace import call_me_back
import sys
import os
try:
from _debugger_case_dont_trace import call_me_back
except ImportError:
sys.path.append(os.path.dirname(__file__))
from _debugger_case_dont_trace import call_me_back
def my_callback():
print('trace me') # Break here
if __name__ == '__main__':
call_me_back(my_callback)
print('TEST SUCEEDED!')
print('TEST SUCEEDED!')

View file

@ -4,22 +4,24 @@ import pytest
from _pydevd_bundle._debug_adapter import pydevd_schema, pydevd_base_schema
from _pydevd_bundle._debug_adapter.pydevd_base_schema import from_json
from tests_python.debugger_unittest import IS_JYTHON, REASON_STEP_INTO, REASON_STEP_OVER, \
REASON_CAUGHT_EXCEPTION, REASON_THREAD_SUSPEND, REASON_STEP_RETURN, IS_APPVEYOR, overrides, \
REASON_UNCAUGHT_EXCEPTION
REASON_CAUGHT_EXCEPTION, REASON_STEP_RETURN, IS_APPVEYOR, overrides
from _pydevd_bundle._debug_adapter.pydevd_schema import ThreadEvent, ModuleEvent, OutputEvent, \
ExceptionOptions, Response
ExceptionOptions, Response, StoppedEvent, ContinuedEvent
from tests_python import debugger_unittest
import json
from collections import namedtuple
from _pydevd_bundle.pydevd_constants import int_types
from tests_python.debug_constants import * # noqa
import time
from os.path import normcase
pytest_plugins = [
str('tests_python.debugger_fixtures'),
]
_JsonHit = namedtuple('_JsonHit', 'frameId, stack_trace_response')
_JsonHit = namedtuple('_JsonHit', 'thread_id, frame_id, stack_trace_response')
pytestmark = pytest.mark.skipif(IS_JYTHON, reason='Single notification is not OK in Jython (investigate).')
# Note: in reality must be < int32, but as it's created sequentially this should be
# a reasonable number for tests.
@ -30,6 +32,8 @@ class JsonFacade(object):
def __init__(self, writer):
self.writer = writer
writer.write_set_protocol('http_json')
writer.write_multi_threads_single_notification(True)
def wait_for_json_message(self, expected_class, accept_message=lambda obj:True):
@ -75,6 +79,14 @@ class JsonFacade(object):
def write_list_threads(self):
return self.wait_for_response(self.write_request(pydevd_schema.ThreadsRequest()))
def wait_for_thread_stopped(self, reason='breakpoint', line=None):
stopped_event = self.wait_for_json_message(StoppedEvent)
assert stopped_event.body.reason == reason
json_hit = self.get_stack_as_json_hit(stopped_event.body.threadId)
if line is not None:
assert json_hit.stack_trace_response.body.stackFrames[0]['line'] == line
return json_hit
def write_set_breakpoints(self, lines, filename=None, line_to_info=None):
'''
Adds a breakpoint.
@ -162,7 +174,8 @@ class JsonFacade(object):
stack_frame = next(iter(stack_trace_response_body.stackFrames))
return _JsonHit(frameId=stack_frame['id'], stack_trace_response=stack_trace_response)
return _JsonHit(
thread_id=thread_id, frame_id=stack_frame['id'], stack_trace_response=stack_trace_response)
def get_variables_response(self, variables_reference):
assert variables_reference < MAX_EXPECTED_ID
@ -192,12 +205,31 @@ class JsonFacade(object):
references.append(reference)
return references
def write_continue(self):
continue_request = self.write_request(
pydevd_schema.ContinueRequest(pydevd_schema.ContinueArguments('*')))
# The continued event is received before the response.
assert self.wait_for_json_message(ContinuedEvent).body.allThreadsContinued
continue_response = self.wait_for_response(continue_request)
assert continue_response.body.allThreadsContinued
def write_pause(self):
pause_request = self.write_request(
pydevd_schema.PauseRequest(pydevd_schema.PauseArguments('*')))
pause_response = self.wait_for_response(pause_request)
assert pause_response.success
def write_step_in(self, thread_id):
arguments = pydevd_schema.StepInArguments(threadId=thread_id)
self.wait_for_response(self.write_request(pydevd_schema.StepInRequest(arguments)))
def test_case_json_logpoints(case_setup):
with case_setup.test_file('_debugger_case_change_breaks.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
json_facade.write_launch()
break_2 = writer.get_line_index_with_content('break 2')
break_3 = writer.get_line_index_with_content('break 3')
@ -224,29 +256,24 @@ def test_case_json_logpoints(case_setup):
break
# Just one hit at the end (break 3).
hit = writer.wait_for_breakpoint_hit()
writer.write_run_thread(hit.thread_id)
json_facade.wait_for_thread_stopped(line=break_3)
json_facade.write_continue()
writer.finished_ok = True
@pytest.mark.skipif(IS_JYTHON, reason='Must check why it is failing in Jython.')
def test_case_json_change_breaks(case_setup):
with case_setup.test_file('_debugger_case_change_breaks.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
json_facade.write_launch()
json_facade.write_set_breakpoints(writer.get_line_index_with_content('break 1'))
break1_line = writer.get_line_index_with_content('break 1')
json_facade.write_set_breakpoints(break1_line)
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit()
writer.write_run_thread(hit.thread_id)
hit = writer.wait_for_breakpoint_hit()
writer.write_run_thread(hit.thread_id)
json_facade.wait_for_thread_stopped(line=break1_line)
json_facade.write_set_breakpoints([])
writer.write_run_thread(hit.thread_id)
json_facade.write_continue()
writer.finished_ok = True
@ -255,21 +282,20 @@ def test_case_handled_exception_breaks(case_setup):
with case_setup.test_file('_debugger_case_exceptions.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
json_facade.write_launch()
json_facade.write_set_exception_breakpoints(['raised'])
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit(
reason=REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('raise indexerror line'))
writer.write_run_thread(hit.thread_id)
json_facade.wait_for_thread_stopped(
reason='exception', line=writer.get_line_index_with_content('raise indexerror line'))
json_facade.write_continue()
hit = writer.wait_for_breakpoint_hit(
reason=REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('reraise on method2'))
json_facade.wait_for_thread_stopped(
reason='exception', line=writer.get_line_index_with_content('reraise on method2'))
# Clear so that the last one is not hit.
json_facade.write_set_exception_breakpoints([])
writer.write_run_thread(hit.thread_id)
json_facade.write_continue()
writer.finished_ok = True
@ -278,7 +304,6 @@ def test_case_handled_exception_breaks_by_type(case_setup):
with case_setup.test_file('_debugger_case_exceptions.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
json_facade.write_launch()
json_facade.write_set_exception_breakpoints(exception_options=[
ExceptionOptions(breakMode='always', path=[
@ -288,8 +313,8 @@ def test_case_handled_exception_breaks_by_type(case_setup):
])
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit(
reason=REASON_CAUGHT_EXCEPTION, line=writer.get_line_index_with_content('raise indexerror line'))
json_facade.wait_for_thread_stopped(
reason='exception', line=writer.get_line_index_with_content('raise indexerror line'))
# Deal only with RuntimeErorr now.
json_facade.write_set_exception_breakpoints(exception_options=[
@ -299,7 +324,7 @@ def test_case_handled_exception_breaks_by_type(case_setup):
])
])
writer.write_run_thread(hit.thread_id)
json_facade.write_continue()
writer.finished_ok = True
@ -309,16 +334,14 @@ def test_case_json_protocol(case_setup):
with case_setup.test_file('_debugger_case_print.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
json_facade.write_launch()
json_facade.write_set_breakpoints(writer.get_line_index_with_content('Break here'))
break_line = writer.get_line_index_with_content('Break here')
json_facade.write_set_breakpoints(break_line)
json_facade.write_make_initial_run()
json_facade.wait_for_json_message(ThreadEvent, lambda event: event.body.reason == 'started')
hit = writer.wait_for_breakpoint_hit()
thread_id = hit.thread_id
frame_id = hit.frame_id
json_facade.wait_for_thread_stopped(line=break_line)
# : :type response: ThreadsResponse
response = json_facade.write_list_threads()
@ -360,8 +383,6 @@ def test_case_path_translation_not_skipped(case_setup):
with case_setup.test_file('my_code/my_code.py', get_environ=get_environ) as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
bp_line = writer.get_line_index_with_content('break here')
json_facade.write_set_breakpoints(
bp_line,
@ -369,11 +390,11 @@ def test_case_path_translation_not_skipped(case_setup):
)
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit(line=bp_line)
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
json_hit = json_facade.wait_for_thread_stopped(line=bp_line)
assert json_hit.stack_trace_response.body.stackFrames[-1]['source']['path'] == \
os.path.join(sys_folder, 'my_code.py')
writer.write_run_thread(hit.thread_id)
json_facade.write_continue()
writer.finished_ok = True
@ -390,7 +411,6 @@ def test_case_skipping_filters(case_setup, custom_setup):
with case_setup.test_file('my_code/my_code.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
if custom_setup == 'set_exclude_launch_path_match_filename':
json_facade.write_launch(
debugOptions=['DebugStdLib'],
@ -440,12 +460,13 @@ def test_case_skipping_filters(case_setup, custom_setup):
else:
raise AssertionError('Unhandled: %s' % (custom_setup,))
json_facade.write_set_breakpoints(writer.get_line_index_with_content('break here'))
break_line = writer.get_line_index_with_content('break here')
json_facade.write_set_breakpoints(break_line)
json_facade.write_make_initial_run()
json_facade.wait_for_json_message(ThreadEvent, lambda event: event.body.reason == 'started')
hit = writer.wait_for_breakpoint_hit()
hit = writer.wait_for_breakpoint_hit(line=break_line)
writer.write_step_in(hit.thread_id)
hit = writer.wait_for_breakpoint_hit(reason=REASON_STEP_INTO)
@ -481,7 +502,6 @@ def test_case_completions_json(case_setup):
with case_setup.test_file('_debugger_case_completions.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()
@ -495,7 +515,7 @@ def test_case_completions_json(case_setup):
first_hit = json_hit
completions_arguments = pydevd_schema.CompletionsArguments(
'dict.', 6, frameId=json_hit.frameId, line=0)
'dict.', 6, frameId=json_hit.frame_id, line=0)
completions_request = json_facade.write_request(
pydevd_schema.CompletionsRequest(completions_arguments))
@ -505,7 +525,7 @@ def test_case_completions_json(case_setup):
assert set(labels).issuperset(set(['__contains__', 'items', 'keys', 'values']))
completions_arguments = pydevd_schema.CompletionsArguments(
'dict.item', 10, frameId=json_hit.frameId)
'dict.item', 10, frameId=json_hit.frame_id)
completions_request = json_facade.write_request(
pydevd_schema.CompletionsRequest(completions_arguments))
@ -520,9 +540,9 @@ def test_case_completions_json(case_setup):
if i == 1:
# Check with a previously existing frameId.
assert first_hit.frameId != json_hit.frameId
assert first_hit.frame_id != json_hit.frame_id
completions_arguments = pydevd_schema.CompletionsArguments(
'dict.item', 10, frameId=first_hit.frameId)
'dict.item', 10, frameId=first_hit.frame_id)
completions_request = json_facade.write_request(
pydevd_schema.CompletionsRequest(completions_arguments))
@ -549,8 +569,6 @@ def test_modules(case_setup):
with case_setup.test_file('_debugger_case_local_variables.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
writer.write_add_breakpoint(writer.get_line_index_with_content('Break 2 here'))
json_facade.write_make_initial_run()
@ -580,14 +598,12 @@ def test_stack_and_variables_dict(case_setup):
with case_setup.test_file('_debugger_case_local_variables.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
writer.write_add_breakpoint(writer.get_line_index_with_content('Break 2 here'))
json_facade.write_make_initial_run()
hit = writer.wait_for_breakpoint_hit()
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
variables_response = json_facade.get_variables_response(json_hit.frameId)
variables_response = json_facade.get_variables_response(json_hit.frame_id)
variables_references = json_facade.pop_variables_reference(variables_response.body.variables)
dict_variable_reference = variables_references[2]
@ -632,7 +648,6 @@ def test_stack_and_variables_dict(case_setup):
def test_return_value(case_setup):
with case_setup.test_file('_debugger_case_return_value.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
break_line = writer.get_line_index_with_content('break here')
writer.write_add_breakpoint(break_line)
@ -644,7 +659,7 @@ def test_return_value(case_setup):
hit = writer.wait_for_breakpoint_hit(REASON_STEP_OVER, name='<module>', line=break_line + 1)
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
variables_response = json_facade.get_variables_response(json_hit.frameId)
variables_response = json_facade.get_variables_response(json_hit.frame_id)
return_variables = json_facade.filter_return_variables(variables_response.body.variables)
assert return_variables == [{
'name': '(return) method1',
@ -662,14 +677,12 @@ def test_stack_and_variables_set_and_list(case_setup):
with case_setup.test_file('_debugger_case_local_variables2.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()
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
variables_response = json_facade.get_variables_response(json_hit.frameId)
variables_response = json_facade.get_variables_response(json_hit.frame_id)
variables_references = json_facade.pop_variables_reference(variables_response.body.variables)
if IS_PY2:
@ -714,8 +727,6 @@ def test_evaluate_unicode(case_setup):
with case_setup.test_file('_debugger_case_local_variables.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
writer.write_add_breakpoint(writer.get_line_index_with_content('Break 2 here'))
json_facade.write_make_initial_run()
@ -723,7 +734,7 @@ def test_evaluate_unicode(case_setup):
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
evaluate_response = json_facade.wait_for_response(
json_facade.write_request(EvaluateRequest(EvaluateArguments(u'\u16A0', json_hit.frameId))))
json_facade.write_request(EvaluateRequest(EvaluateArguments(u'\u16A0', json_hit.frame_id))))
evaluate_response_body = evaluate_response.body.to_dict()
@ -769,8 +780,6 @@ def test_evaluate_variable_references(case_setup):
with case_setup.test_file('_debugger_case_local_variables2.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()
@ -778,7 +787,7 @@ def test_evaluate_variable_references(case_setup):
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
evaluate_response = json_facade.wait_for_response(
json_facade.write_request(EvaluateRequest(EvaluateArguments('variable_for_test_2', json_hit.frameId))))
json_facade.write_request(EvaluateRequest(EvaluateArguments('variable_for_test_2', json_hit.frame_id))))
evaluate_response_body = evaluate_response.body.to_dict()
@ -821,8 +830,6 @@ def test_set_expression(case_setup):
with case_setup.test_file('_debugger_case_local_variables2.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()
@ -831,11 +838,11 @@ def test_set_expression(case_setup):
set_expression_response = json_facade.wait_for_response(
json_facade.write_request(SetExpressionRequest(
SetExpressionArguments('bb', '20', frameId=json_hit.frameId))))
SetExpressionArguments('bb', '20', frameId=json_hit.frame_id))))
assert set_expression_response.to_dict()['body'] == {
'value': '20', 'type': 'int', 'presentationHint': {}, 'variablesReference': 0}
variables_response = json_facade.get_variables_response(json_hit.frameId)
variables_response = json_facade.get_variables_response(json_hit.frame_id)
assert {'name': 'bb', 'value': '20', 'type': 'int', 'evaluateName': 'bb'} in \
variables_response.to_dict()['body']['variables']
@ -849,7 +856,6 @@ def test_stack_and_variables(case_setup):
with case_setup.test_file('_debugger_case_local_variables.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()
@ -975,7 +981,6 @@ def test_hex_variables(case_setup):
with case_setup.test_file('_debugger_case_local_variables_hex.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()
@ -1051,33 +1056,40 @@ def test_hex_variables(case_setup):
writer.finished_ok = True
@pytest.mark.skipif(IS_JYTHON, reason='Flaky on Jython.')
def test_pause_and_continue(case_setup):
with case_setup.test_file('_debugger_case_pause_continue.py') as writer:
def test_stopped_event(case_setup):
with case_setup.test_file('_debugger_case_print.py') as writer:
json_facade = JsonFacade(writer)
writer.write_multi_threads_single_notification(True)
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()
json_hit = json_facade.wait_for_thread_stopped()
assert json_hit.thread_id
continue_request = json_facade.write_request(
pydevd_schema.ContinueRequest(pydevd_schema.ContinueArguments('*')))
continue_response = json_facade.wait_for_response(continue_request)
assert continue_response.body.allThreadsContinued
json_facade.write_continue()
pause_request = json_facade.write_request(
pydevd_schema.PauseRequest(pydevd_schema.PauseArguments('*')))
pause_response = json_facade.wait_for_response(pause_request)
hit = writer.wait_for_breakpoint_hit(reason=REASON_THREAD_SUSPEND)
writer.finished_ok = True
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)
stack_frame = next(iter(stack_trace_response.body.stackFrames))
@pytest.mark.skipif(IS_JYTHON, reason='Not Jython compatible (fails on set variable).')
def test_pause_and_continue(case_setup):
with case_setup.test_file('_debugger_case_pause_continue.py') as writer:
json_facade = JsonFacade(writer)
json_facade.write_set_breakpoints(writer.get_line_index_with_content('Break here'))
json_facade.write_make_initial_run()
json_facade.wait_for_thread_stopped()
json_facade.write_continue()
json_facade.write_pause()
json_hit = json_facade.wait_for_thread_stopped(reason="pause")
stack_frame = next(iter(json_hit.stack_trace_response.body.stackFrames))
scopes_request = json_facade.write_request(pydevd_schema.ScopesRequest(
pydevd_schema.ScopesArguments(stack_frame['id'])))
@ -1093,10 +1105,7 @@ def test_pause_and_continue(case_setup):
set_variable_response_as_dict = set_variable_response.to_dict()['body']
assert set_variable_response_as_dict == {'value': "False", 'type': 'bool'}
continue_request = json_facade.write_request(
pydevd_schema.ContinueRequest(pydevd_schema.ContinueArguments('*')))
continue_response = json_facade.wait_for_response(continue_request)
assert continue_response.body.allThreadsContinued
json_facade.write_continue()
writer.finished_ok = True
@ -1105,7 +1114,6 @@ def test_stepping(case_setup):
with case_setup.test_file('_debugger_case_stepping.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 1'))
writer.write_add_breakpoint(writer.get_line_index_with_content('Break here 2'))
@ -1176,7 +1184,6 @@ def test_evaluate(case_setup):
with case_setup.test_file('_debugger_case_evaluate.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()
@ -1220,7 +1227,6 @@ def test_exception_details(case_setup, max_frames):
with case_setup.test_file('_debugger_case_large_exception_stack.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
if max_frames == 'all':
json_facade.write_launch(maxExceptionStackFrames=0)
# trace back compresses repeated text
@ -1248,7 +1254,7 @@ def test_exception_details(case_setup, max_frames):
body = exc_info_response.body
assert body.exceptionId.endswith('IndexError')
assert body.description == 'foo'
assert body.details.kwargs['source'] == writer.TEST_FILE
assert normcase(body.details.kwargs['source']) == normcase(writer.TEST_FILE)
stack_line_count = len(body.details.stackTrace.split('\n'))
assert min_expected_lines <= stack_line_count <= max_expected_lines
@ -1262,7 +1268,6 @@ 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()
@ -1331,7 +1336,6 @@ def test_goto(case_setup):
with case_setup.test_file('_debugger_case_set_next_statement.py') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
break_line = writer.get_line_index_with_content('Break here')
step_line = writer.get_line_index_with_content('Step here')
writer.write_add_breakpoint(break_line)
@ -1382,12 +1386,12 @@ def test_goto(case_setup):
writer.finished_ok = True
@pytest.mark.parametrize('dbg_property', ['dont_trace', 'trace', 'change_pattern', 'dont_trace_after_start'])
def test_set_debugger_property(case_setup, dbg_property):
with case_setup.test_file('_debugger_case_dont_trace_test.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'))
if dbg_property in ('dont_trace', 'change_pattern', 'dont_trace_after_start'):
@ -1473,7 +1477,6 @@ def test_path_translation_and_source_reference(case_setup):
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
bp_line = writer.get_line_index_with_content('break here')
writer.write_add_breakpoint(
bp_line, 'call_this', filename=file_in_client)
@ -1530,7 +1533,7 @@ def test_source_reference_no_file(case_setup, tmpdir):
with case_setup.test_file('_debugger_case_source_reference.py', get_environ=get_environ) as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
writer.write_add_breakpoint(writer.get_line_index_with_content('breakpoint'))
json_facade.write_make_initial_run()
@ -1591,7 +1594,6 @@ def test_case_django_no_attribute_exception_breakpoint(case_setup_django, jmc):
with case_setup_django.test_file(EXPECTED_RETURNCODE='any') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
if jmc:
writer.write_set_project_roots([debugger_unittest._get_debugger_test_file('my_code')])
@ -1627,7 +1629,7 @@ def test_case_django_no_attribute_exception_breakpoint(case_setup_django, jmc):
assert stack_frame['source']['path'].endswith('template_error.html')
json_hit = json_facade.get_stack_as_json_hit(hit.thread_id)
variables_response = json_facade.get_variables_response(json_hit.frameId)
variables_response = json_facade.get_variables_response(json_hit.frame_id)
entries = [x for x in variables_response.to_dict()['body']['variables'] if x['name'] == 'entry']
assert len(entries) == 1
variables_response = json_facade.get_variables_response(entries[0]['variablesReference'])
@ -1645,7 +1647,6 @@ def test_case_django_no_attribute_exception_breakpoint(case_setup_django, jmc):
def test_case_flask_exceptions(case_setup_flask, jmc):
with case_setup_flask.test_file(EXPECTED_RETURNCODE='any') as writer:
json_facade = JsonFacade(writer)
writer.write_set_protocol('http_json')
if jmc:
writer.write_set_project_roots([debugger_unittest._get_debugger_test_file('my_code')])
@ -1684,7 +1685,7 @@ def test_redirect_output(case_setup):
with case_setup.test_file('_debugger_case_redirect.py', get_environ=get_environ) as writer:
original_ignore_stderr_line = writer._ignore_stderr_line
writer.write_set_protocol('http_json')
json_facade = JsonFacade(writer)
@overrides(writer._ignore_stderr_line)

View file

@ -7,7 +7,18 @@ from tests_python.debug_constants import IS_PY26, IS_PY3K
def test_is_main_thread():
from _pydevd_bundle.pydevd_utils import is_current_thread_main_thread
assert is_current_thread_main_thread()
if not is_current_thread_main_thread():
error_msg = 'Current thread does not seem to be a main thread. Details:\n'
current_thread = threading.current_thread()
error_msg += 'Current thread: %s\n' % (current_thread,)
if hasattr(threading, 'main_thread'):
error_msg += 'Main thread found: %s\n' % (threading.main_thread(),)
else:
error_msg += 'Current main thread not instance of: %s (%s)' % (
threading._MainThread, current_thread.__class__.__mro__,)
raise AssertionError(error_msg)
class NonMainThread(threading.Thread):

View file

@ -24,7 +24,7 @@ class DebugSession(Startable, Closeable):
return raw
if not is_socket(raw):
# TODO: Create a new client socket from a remote address?
#addr = Address.from_raw(raw)
# addr = Address.from_raw(raw)
raise NotImplementedError
client = raw
return cls(client, **kwargs)
@ -42,9 +42,11 @@ class DebugSession(Startable, Closeable):
super(DebugSession, self).__init__()
if notify_closing is not None:
def handle_closing(before):
if before:
notify_closing(self)
self.add_close_handler(handle_closing)
if notify_disconnecting is None:
@ -54,6 +56,7 @@ class DebugSession(Startable, Closeable):
self._sock = sock
self._pre_socket_close = None
if ownsock:
# Close the socket *after* calling sys.exit() (via notify_closing).
def handle_closing(before):
if before:
@ -68,6 +71,7 @@ class DebugSession(Startable, Closeable):
except TimeoutError:
ptvsd.log.exception('timed out waiting for disconnect', category='D')
close_socket(self._sock)
self.add_close_handler(handle_closing)
self._msgprocessor = None
@ -171,6 +175,7 @@ class PyDevdDebugSession(DebugSession):
self._notified_debugger_ready = True
if _notify is not None:
_notify(session)
self._notified_debugger_ready = False
self._notify_debugger_ready = notify_debugger_ready
@ -178,7 +183,11 @@ class PyDevdDebugSession(DebugSession):
if self._msgprocessor is None:
# TODO: Do more than ignore?
return
return self._msgprocessor.on_pydevd_event(cmdid, seq, text)
try:
return self._msgprocessor.on_pydevd_event(cmdid, seq, text)
except:
ptvsd.log.exception('Error handling pydevd message: {0}', text)
raise
# internal methods

View file

@ -51,19 +51,6 @@ from ptvsd.socket import TimeoutError # noqa
WAIT_FOR_THREAD_FINISH_TIMEOUT = 1 # seconds
STEP_REASONS = {
pydevd_comm.CMD_STEP_INTO,
pydevd_comm.CMD_STEP_INTO_MY_CODE,
pydevd_comm.CMD_STEP_OVER,
pydevd_comm.CMD_STEP_OVER_MY_CODE,
pydevd_comm.CMD_STEP_RETURN,
pydevd_comm.CMD_STEP_INTO_MY_CODE,
}
EXCEPTION_REASONS = {
pydevd_comm.CMD_STEP_CAUGHT_EXCEPTION,
pydevd_comm.CMD_ADD_EXCEPTION_BREAK
}
debugger_attached = threading.Event()
@ -78,12 +65,14 @@ def path_to_unicode(s):
PTVSD_DIR_PATH = os.path.dirname(os.path.abspath(get_abs_path_real_path_and_base_from_file(__file__)[0])) + os.path.sep
NORM_PTVSD_DIR_PATH = os.path.normcase(PTVSD_DIR_PATH)
def dont_trace_ptvsd_files(py_db, file_path):
"""
Returns true if the file should not be traced.
"""
return file_path.startswith(PTVSD_DIR_PATH) or file_path.endswith('ptvsd_launcher.py')
pydevd.PyDB.dont_trace_external_files = dont_trace_ptvsd_files
@ -1333,11 +1322,11 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
# Don't trace files under ptvsd, and ptvsd_launcher.py files
# TODO: un-comment this code after fixing https://github.com/Microsoft/ptvsd/issues/1355
#dont_trace_request = self._get_new_setDebuggerProperty_request(
# dont_trace_request = self._get_new_setDebuggerProperty_request(
# dontTraceStartPatterns=[PTVSD_DIR_PATH],
# dontTraceEndPatterns=['ptvsd_launcher.py']
#)
#yield self.pydevd_request(-1, dont_trace_request, is_json=True)
# )
# yield self.pydevd_request(-1, dont_trace_request, is_json=True)
def _handle_detach(self):
ptvsd.log.info('Detaching ...')
@ -1745,84 +1734,50 @@ class VSCodeMessageProcessor(VSCLifecycleMsgProcessor):
pass # We only care about the thread suspend single notification.
@pydevd_events.handler(pydevd_comm_constants.CMD_THREAD_SUSPEND_SINGLE_NOTIFICATION)
@async_handler
def on_pydevd_thread_suspend_single_notification(self, seq, args):
# NOTE: We should add the thread to VSC thread map only if the
# thread is seen here for the first time in 'attach' scenario.
# If we are here in 'launch' scenario and we get KeyError then
# there is an issue in reporting of thread creation.
suspend_info = json.loads(args)
pyd_tid = suspend_info['thread_id']
reason = suspend_info['stop_reason']
body = args.get('body', {})
pyd_tid = body['threadId']
autogen = self.start_reason == 'attach'
vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=autogen)
exc_desc = None
exc_name = None
extra = {}
if reason in STEP_REASONS:
reason = 'step'
elif reason in EXCEPTION_REASONS:
reason = 'exception'
elif reason == pydevd_comm.CMD_SET_BREAK:
reason = 'breakpoint'
elif reason == pydevd_comm.CMD_SET_NEXT_STATEMENT:
reason = 'goto'
else:
reason = 'pause'
extra['preserveFocusHint'] = \
reason not in ['step', 'exception', 'breakpoint']
reason = body['reason']
if reason == 'exception':
pydevd_request = {
'type': 'request',
'command': 'exceptionInfo',
'arguments': {
'threadId': pyd_tid
},
}
exc_name = body['text']
exc_desc = body['description']
_, _, resp_args = yield self.pydevd_request(
pydevd_comm.CMD_GET_EXCEPTION_DETAILS,
pydevd_request,
is_json=True)
exc_name = resp_args['body']['exceptionId']
exc_desc = resp_args['body']['description']
if not self.debug_options.get('BREAK_SYSTEMEXIT_ZERO', False) and exc_name == 'SystemExit':
ptvsd.log.info('{0}({1!r})', exc_name, exc_desc)
try:
exit_code = int(exc_desc)
except ValueError:
# It is legal to invoke exit() with a non-integer argument, and SystemExit will
# pass that through. It's considered an error exit, same as non-zero integer.
ptvsd.log.info('Exit code {0!r} cannot be converted to int, treating as failure', exc_desc)
ignore = False
else:
ignore = exit_code in self._success_exitcodes
ptvsd.log.info(
'Process exiting with {0} exit code {1}',
'success' if ignore else 'failure',
exc_desc,
)
if ignore:
self._resume_all_threads()
return
if not self.debug_options.get('BREAK_SYSTEMEXIT_ZERO', False) and exc_name == 'SystemExit':
ptvsd.log.info('{0}({1!r})', exc_name, exc_desc)
try:
exit_code = int(exc_desc)
except ValueError:
# It is legal to invoke exit() with a non-integer argument, and SystemExit will
# pass that through. It's considered an error exit, same as non-zero integer.
ptvsd.log.info('Exit code {0!r} cannot be converted to int, treating as failure', exc_desc)
ignore = False
else:
ignore = exit_code in self._success_exitcodes
ptvsd.log.info(
'Process exiting with {0} exit code {1}',
'success' if ignore else 'failure',
exc_desc,
)
if ignore:
self._resume_all_threads()
return
extra['allThreadsStopped'] = True
self.send_event(
'stopped',
reason=reason,
threadId=vsc_tid,
text=exc_name,
description=exc_desc,
**extra)
body = body.copy()
body['threadId'] = vsc_tid
self.send_event('stopped', **body)
@pydevd_events.handler(pydevd_comm_constants.CMD_THREAD_RESUME_SINGLE_NOTIFICATION)
def on_pydevd_thread_resume_single_notification(self, seq, args):
resumed_info = json.loads(args)
pyd_tid = resumed_info['thread_id']
body = args.get('body', {})
pyd_tid = body['threadId']
try:
vsc_tid = self.thread_map.to_vscode(pyd_tid, autogen=False)

View file

@ -56,7 +56,11 @@ def test_vsc_exception_options_raise_with_except(pyfile, run_as, start_method, r
})
if raised == 'raisedOn':
hit = session.wait_for_thread_stopped(reason='exception')
hit = session.wait_for_thread_stopped(
reason='exception',
text=ANY.such_that(lambda s: s.endswith('ArithmeticError')),
description='bad code',
)
frames = hit.stacktrace.body['stackFrames']
assert ex_line == frames[0]['line']
@ -313,6 +317,7 @@ def test_raise_exception_options(pyfile, run_as, start_method, exceptions, break
@pytest.mark.parametrize('exit_code', [0, 3])
def test_success_exitcodes(pyfile, run_as, start_method, exit_code):
@pyfile
def code_to_debug():
from dbgimporter import import_and_enable_debugger
@ -344,17 +349,19 @@ def test_success_exitcodes(pyfile, run_as, start_method, exit_code):
@pytest.mark.parametrize('max_frames', ['default', 'all', 10])
def test_exception_stack(pyfile, run_as, start_method, max_frames):
@pyfile
def code_to_debug():
from dbgimporter import import_and_enable_debugger
import_and_enable_debugger()
def do_something(n):
if n <= 0:
raise ArithmeticError('bad code') # @unhandled
raise ArithmeticError('bad code') # @unhandled
do_something2(n - 1)
def do_something2(n):
do_something(n-1)
do_something(n - 1)
do_something(100)

View file

@ -636,10 +636,21 @@ class DebugSession(object):
'breakpoints': [{'line': bp_line} for bp_line in lines],
}).wait_for_response().body.get('breakpoints', None)
def wait_for_thread_stopped(self, reason=ANY):
def wait_for_thread_stopped(self, reason=ANY, text=None, description=None):
thread_stopped = self.wait_for_next(Event('stopped', ANY.dict_with({'reason': reason})))
if text is not None:
assert text == thread_stopped.body['text']
if description is not None:
assert description == thread_stopped.body['description']
tid = thread_stopped.body['threadId']
assert thread_stopped.body['allThreadsStopped']
assert thread_stopped.body['preserveFocusHint'] == \
(thread_stopped.body['reason'] not in ['step', 'exception', 'breakpoint'])
assert tid is not None
resp_stacktrace = self.send_request('stackTrace', arguments={