def test_vsc_exception_options_raise_with_except(pyfile, run_as, start_method, raised, uncaught): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def raise_with_except(): try: raise ArithmeticError('bad code') # @exception_line except Exception: pass raise_with_except() line_numbers = get_marked_line_numbers(code_to_debug) ex_line = line_numbers['exception_line'] filters = [] filters += ['raised'] if raised == 'raisedOn' else [] filters += ['uncaught'] if uncaught == 'uncaughtOn' else [] with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() session.start_debugging() expected = ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'bad code', 'breakMode': 'always' if raised == 'raisedOn' else 'unhandled', 'details': ANY.dict_with({ 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'message': 'bad code', 'source': Path(code_to_debug), }), }) if raised == 'raisedOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) # uncaught should not 'stop' matter since the exception is caught session.wait_for_exit()
def test_flask_template_exception_no_multiproc(start_method): with DebugSession() as session: _initialize_flask_session_no_multiproc(session, start_method) session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() session.start_debugging() # wait for Flask web server to start wait_for_connection(FLASK_PORT) base_link = FLASK_LINK part = 'badtemplate' link = base_link + part if base_link.endswith('/') else ('/' + part) web_request = get_web_content(link, {}) hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert frames[0] == ANY.dict_with({ 'id': ANY.dap_id, 'name': 'template' if sys.version_info[0] >= 3 else 'Jinja2 TemplateSyntaxError', 'source': ANY.dict_with({ 'sourceReference': ANY.dap_id, 'path': Path(FLASK1_BAD_TEMPLATE), }), 'line': 8, 'column': 1, }) resp_exception_info = session.send_request( 'exceptionInfo', arguments={'threadId': hit.thread_id, } ).wait_for_response() exception = resp_exception_info.body assert exception == ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('TemplateSyntaxError')), 'breakMode': 'always', 'description': ANY.such_that(lambda s: s.find('doesnotexist') > -1), 'details': ANY.dict_with({ 'message': ANY.such_that(lambda s: s.find('doesnotexist') > -1), 'typeName': ANY.such_that(lambda s: s.endswith('TemplateSyntaxError')), }) }) session.send_request('continue').wait_for_response(freeze=False) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = base_link + 'exit' if base_link.endswith('/') else '/exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_run(pyfile, run_as, start_method): @pyfile def code_to_debug(): import os import sys import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('begin') assert backchannel.read_json() == 'continue' backchannel.write_json(os.path.abspath(sys.modules['ptvsd'].__file__)) print('end') with DebugSession() as session: session.initialize(target=(run_as, code_to_debug), start_method=start_method, use_backchannel=True) session.start_debugging() assert session.timeline.is_frozen process_event, = session.all_occurrences_of(Event('process')) assert process_event == Event( 'process', ANY.dict_with({ 'name': ANY if run_as == 'code' else ANY.such_that(lambda name: ( # There can be a difference in file extension (.py/.pyc/.pyo) on clean runs. name == code_to_debug or name == code_to_debug + 'c' or name == code_to_debug + 'o')), })) session.write_json('continue') ptvsd_path = session.read_json() expected_ptvsd_path = os.path.abspath(ptvsd.__file__) assert (ptvsd_path == expected_ptvsd_path or ptvsd_path == expected_ptvsd_path + 'c' or ptvsd_path == expected_ptvsd_path + 'o') session.wait_for_exit()
def test_vsc_exception_options_raise_without_except(pyfile, run_as, start_method, raised, uncaught): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def raise_without_except(): raise ArithmeticError('bad code') # @exception_line raise_without_except() line_numbers = get_marked_line_numbers(code_to_debug) ex_line = line_numbers['exception_line'] filters = [] filters += ['raised'] if raised == 'raisedOn' else [] filters += ['uncaught'] if uncaught == 'uncaughtOn' else [] with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued'), Event('stopped')], expected_returncode=ANY.int, ) session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() session.start_debugging() expected = ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'bad code', 'breakMode': 'always' if raised == 'raisedOn' else 'unhandled', 'details': ANY.dict_with({ 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'message': 'bad code', 'source': Path(code_to_debug), }), }) if raised == 'raisedOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) # NOTE: debugger stops at each frame if raised and is uncaught # This behavior can be changed by updating 'notify_on_handled_exceptions' # setting we send to pydevd to notify only once. In our test code, we have # two frames, hence two stops. session.wait_for_thread_stopped(reason='exception') session.send_request('continue').wait_for_response(freeze=False) if uncaught == 'uncaughtOn': hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert ex_line == frames[0]['line'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() expected = ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'bad code', 'breakMode': 'unhandled', # Only difference from previous expected is breakMode. 'details': ANY.dict_with({ 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'message': 'bad code', 'source': Path(code_to_debug), }), }) assert resp_exc_info.body == expected session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_flask_exception_no_multiproc(ex_type, start_method): ex_line = { 'handled': 21, 'unhandled': 33, }[ex_type] with DebugSession() as session: _initialize_flask_session_no_multiproc(session, start_method) session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() session.start_debugging() # wait for Flask web server to start wait_for_connection(FLASK_PORT) base_link = FLASK_LINK link = base_link + ex_type if base_link.endswith('/') else ('/' + ex_type) web_request = get_web_content(link, {}) thread_stopped = session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'exception'}))) assert thread_stopped == Event( 'stopped', ANY.dict_with({ 'reason': 'exception', 'text': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'Hello' })) tid = thread_stopped.body['threadId'] resp_exception_info = session.send_request('exceptionInfo', arguments={ 'threadId': tid, }).wait_for_response() exception = resp_exception_info.body assert exception == { 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'breakMode': 'always', 'description': 'Hello', 'details': { 'message': 'Hello', 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'source': Path(FLASK1_APP), 'stackTrace': ANY.such_that(lambda s: True) } } resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.dap_id, 'name': 'bad_route_' + ex_type, 'source': { 'sourceReference': ANY.dap_id, 'path': Path(FLASK1_APP), }, 'line': ex_line, 'column': 1, } session.send_request('continue').wait_for_response(freeze=False) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = base_link + 'exit' if base_link.endswith('/') else '/exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_django_exception_no_multiproc(ex_type, start_method): ex_line = { 'handled': 50, 'unhandled': 64, }[ex_type] with DebugSession() as session: session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), program_args=[ 'runserver', '--noreload', '--nothreading', str(DJANGO_PORT) ], debug_options=['Django'], cwd=DJANGO1_ROOT, expected_returncode=ANY.int, # No clean way to kill Django server ) session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() session.start_debugging() wait_for_connection(DJANGO_PORT) link = DJANGO_LINK + ex_type web_request = get_web_content(link, {}) thread_stopped = session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'exception'}))) assert thread_stopped == Event( 'stopped', ANY.dict_with({ 'reason': 'exception', 'text': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'description': 'Hello' })) tid = thread_stopped.body['threadId'] resp_exception_info = session.send_request('exceptionInfo', arguments={ 'threadId': tid, }).wait_for_response() exception = resp_exception_info.body assert exception == { 'exceptionId': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'breakMode': 'always', 'description': 'Hello', 'details': { 'message': 'Hello', 'typeName': ANY.such_that(lambda s: s.endswith('ArithmeticError')), 'source': Path(DJANGO1_MANAGE), 'stackTrace': ANY.such_that(lambda s: True), } } resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': tid, }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 1 frames = resp_stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.dap_id, 'name': 'bad_route_' + ex_type, 'source': { 'sourceReference': ANY.dap_id, 'path': Path(DJANGO1_MANAGE), }, 'line': ex_line, 'column': 1, } session.send_request('continue').wait_for_response(freeze=False) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_django_template_exception_no_multiproc(start_method): with DebugSession() as session: session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), program_args=[ 'runserver', '--noreload', '--nothreading', str(DJANGO_PORT) ], debug_options=['Django'], cwd=DJANGO1_ROOT, expected_returncode=ANY.int, # No clean way to kill Django server ) session.send_request('setExceptionBreakpoints', arguments={ 'filters': ['raised', 'uncaught'], }).wait_for_response() session.start_debugging() wait_for_connection(DJANGO_PORT) link = DJANGO_LINK + 'badtemplate' web_request = get_web_content(link, {}) hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0] == ANY.dict_with({ 'id': ANY.dap_id, 'name': 'Django TemplateSyntaxError', 'source': ANY.dict_with({ 'sourceReference': ANY.dap_id, 'path': Path(DJANGO1_BAD_TEMPLATE), }), 'line': 8, 'column': 1, }) # Will stop once in the plugin resp_exception_info = session.send_request('exceptionInfo', arguments={ 'threadId': hit.thread_id, }).wait_for_response() exception = resp_exception_info.body assert exception == ANY.dict_with({ 'exceptionId': ANY.such_that(lambda s: s.endswith('TemplateSyntaxError')), 'breakMode': 'always', 'description': ANY.such_that(lambda s: s.find('doesnotexist') > -1), 'details': ANY.dict_with({ 'message': ANY.such_that(lambda s: s.endswith('doesnotexist') > -1), 'typeName': ANY.such_that(lambda s: s.endswith('TemplateSyntaxError')), }) }) session.send_request('continue').wait_for_response(freeze=False) # And a second time when the exception reaches the user code. hit = session.wait_for_thread_stopped(reason='exception') session.send_request('continue').wait_for_response(freeze=False) # ignore response for exception tests web_request.wait_for_response() # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_return_values(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() class MyClass(object): def do_something(self): return 'did something' def my_func(): return 'did more things' MyClass().do_something() # @bp my_func() print('done') line_numbers = get_marked_line_numbers(code_to_debug) print(line_numbers) expected1 = ANY.dict_with({ 'name': '(return) MyClass.do_something', 'value': "'did something'", 'type': 'str', 'presentationHint': ANY.dict_with({ 'attributes': ANY.such_that(lambda x: 'readOnly' in x) }), }) expected2 = ANY.dict_with({ 'name': '(return) my_func', 'value': "'did more things'", 'type': 'str', 'presentationHint': ANY.dict_with({ 'attributes': ANY.such_that(lambda x: 'readOnly' in x) }), }) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, debug_options=['ShowReturnValue'], ) session.set_breakpoints(code_to_debug, [line_numbers['bp']]) session.start_debugging() hit = session.wait_for_thread_stopped() session.send_request('next', {'threadId': hit.thread_id}).wait_for_response() hit = session.wait_for_thread_stopped(reason='step') resp_scopes = session.send_request('scopes', arguments={ 'frameId': hit.frame_id }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = session.send_request('variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = list( v for v in resp_variables.body['variables'] if v['name'].startswith('(return)') ) assert variables == [expected1] session.send_request('next', {'threadId': hit.thread_id}).wait_for_response() hit = session.wait_for_thread_stopped(reason='step') # Scope should not have changed so use the same scope resp_variables = session.send_request('variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = list( v for v in resp_variables.body['variables'] if v['name'].startswith('(return)') ) assert variables == [expected1, expected2] session.send_request('continue').wait_for_response() session.wait_for_exit()
def test_variables_and_evaluate(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 1 b = {"one": 1, "two": 2} c = 3 print([a, b, c]) bp_line = 6 bp_file = code_to_debug with DebugSession() as session: session.initialize( target=(run_as, bp_file), start_method=start_method, ) session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped() resp_scopes = session.send_request('scopes', arguments={ 'frameId': hit.frame_id, }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = session.send_request('variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = list(v for v in resp_variables.body['variables'] if v['name'] in ['a', 'b', 'c']) assert len(variables) == 3 # variables should be sorted alphabetically assert ['a', 'b', 'c'] == list(v['name'] for v in variables) # get contents of 'b' resp_b_variables = session.send_request('variables', arguments={ 'variablesReference': variables[1]['variablesReference'] }).wait_for_response() b_variables = resp_b_variables.body['variables'] assert len(b_variables) == 3 assert b_variables[0] == { 'type': 'int', 'value': '1', 'name': ANY.such_that(lambda x: x.find('one') > 0), 'evaluateName': "b['one']", 'variablesReference': 0, } assert b_variables[1] == { 'type': 'int', 'value': '2', 'name': ANY.such_that(lambda x: x.find('two') > 0), 'evaluateName': "b['two']", 'variablesReference': 0, } assert b_variables[2] == { 'type': 'int', 'value': '2', 'name': '__len__', 'evaluateName': "len(b)", 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, } # simple variable resp_evaluate1 = session.send_request('evaluate', arguments={ 'expression': 'a', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate1.body == ANY.dict_with({ 'type': 'int', 'result': '1' }) # dict variable resp_evaluate2 = session.send_request('evaluate', arguments={ 'expression': 'b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate2.body == ANY.dict_with({ 'type': 'int', 'result': '1' }) # expression evaluate resp_evaluate3 = session.send_request('evaluate', arguments={ 'expression': 'a + b["one"]', 'frameId': hit.frame_id, }).wait_for_response() assert resp_evaluate3.body == ANY.dict_with({ 'type': 'int', 'result': '2' }) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()