def test_justmycode_frames(pyfile, run_as, start_method, jmc): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('break here') #@bp line_numbers = get_marked_line_numbers(code_to_debug) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], debug_options=[] if jmc == 'jmcOn' else ['DebugStdLib']) bp_line = line_numbers['bp'] actual_bps = session.set_breakpoints(code_to_debug, [bp_line]) actual_bps = [bp['line'] for bp in actual_bps] session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert frames[0] == ANY.dict_with({ 'line': bp_line, 'source': ANY.dict_with({'path': Path(code_to_debug)}) }) if jmc == 'jmcOn': assert len(frames) == 1 session.send_request('stepIn', { 'threadId': hit.thread_id }).wait_for_response() # 'step' should terminate the debuggee else: assert len(frames) >= 1 session.send_request('stepIn', { 'threadId': hit.thread_id }).wait_for_response() # 'step' should enter stdlib hit2 = session.wait_for_thread_stopped() frames2 = hit2.stacktrace.body['stackFrames'] assert frames2[0]['source']['path'] != Path(code_to_debug) # 'continue' should terminate the debuggee session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_stop_on_entry(pyfile, run_as, start_method): @pyfile def code_to_debug(): import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() backchannel.write_json('done') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, debug_options=['StopOnEntry'], use_backchannel=True, ) session.start_debugging() thread_stopped, resp_stacktrace, tid, _ = session.wait_for_thread_stopped(reason='entry') frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 1 assert frames[0]['source']['path'] == Path(code_to_debug) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_termination() assert session.read_json() == 'done' session.wait_for_exit()
def test_with_dot_remote_root(pyfile, tmpdir, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import os import backchannel backchannel.write_json(os.path.abspath(__file__)) print('done') bp_line = 6 path_local = tmpdir.mkdir('local').join('code_to_debug.py').strpath path_remote = tmpdir.mkdir('remote').join('code_to_debug.py').strpath dir_local = os.path.dirname(path_local) dir_remote = os.path.dirname(path_remote) copyfile(code_to_debug, path_local) copyfile(code_to_debug, path_remote) with DebugSession() as session: session.initialize( target=(run_as, path_remote), start_method=start_method, ignore_unobserved=[Event('continued')], use_backchannel=True, path_mappings=[{ 'localRoot': dir_local, 'remoteRoot': '.', }], cwd=dir_remote, ) session.set_breakpoints(path_remote, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] print('Local Path: ' + path_local) print('Frames: ' + str(frames)) assert frames[0]['source']['path'] == Path(path_local) remote_code_path = session.read_json() assert path_remote == Path(remote_code_path) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_stop_on_entry(pyfile, run_as, start_method, with_bp): @pyfile def code_to_debug(): import backchannel # @bp # import_and_enable_debugger() backchannel.write_json('done') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, debug_options=['StopOnEntry'], use_backchannel=True, ) if bool(with_bp): line_numbers = get_marked_line_numbers(code_to_debug) bp_line = line_numbers['bp'] session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() if bool(with_bp): thread_stopped, resp_stacktrace, thread_id, _ = session.wait_for_thread_stopped(reason='breakpoint') frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 1 assert frames[0]['source']['path'] == Path(code_to_debug) session.send_request('next', {'threadId': thread_id}).wait_for_response() thread_stopped, resp_stacktrace, thread_id, _ = session.wait_for_thread_stopped(reason='step') frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 3 assert frames[0]['source']['path'] == Path(code_to_debug) else: thread_stopped, resp_stacktrace, tid, _ = session.wait_for_thread_stopped(reason='entry') frames = resp_stacktrace.body['stackFrames'] assert frames[0]['line'] == 1 assert frames[0]['source']['path'] == Path(code_to_debug) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_termination() assert session.read_json() == 'done' session.wait_for_exit()
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_module_events(pyfile, run_as, start_method): @pyfile def module2(): # import_and_enable_debugger() def do_more_things(): print('done') @pyfile def module1(): # import_and_enable_debugger() import module2 def do_something(): module2.do_more_things() @pyfile def test_code(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() from module1 import do_something do_something() bp_line = 3 with DebugSession() as session: session.initialize( target=(run_as, test_code), start_method=start_method, ignore_unobserved=[Event('stopped')], ) session.set_breakpoints(module2, [bp_line]) session.start_debugging() session.wait_for_thread_stopped() modules = session.all_occurrences_of(Event('module')) modules = [(m.body['module']['name'], m.body['module']['path']) for m in modules] assert modules[:3] == [ ('module2', Path(module2)), ('module1', Path(module1)), ('__main__', Path(test_code)), ] session.send_request('continue').wait_for_response(freeze=False) 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_crossfile_breakpoint(pyfile, run_as, start_method): @pyfile def script1(): from dbgimporter import import_and_enable_debugger # noqa def do_something(): print('do something') @pyfile def script2(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import script1 script1.do_something() print('Done') bp_script1_line = 3 bp_script2_line = 4 with DebugSession() as session: session.initialize( target=(run_as, script2), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.set_breakpoints(script1, lines=[bp_script1_line]) session.set_breakpoints(script2, lines=[bp_script2_line]) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_script2_line == frames[0]['line'] assert frames[0]['source']['path'] == Path(script2) session.send_request('continue').wait_for_response(freeze=False) hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_script1_line == frames[0]['line'] assert frames[0]['source']['path'] == Path(script1) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_path_with_ampersand(run_as, start_method): bp_line = 4 testfile = os.path.join(BP_TEST_ROOT, 'a&b', 'test.py') with DebugSession() as session: session.initialize( target=(run_as, testfile), start_method=start_method, ) session.set_breakpoints(testfile, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'] == Path(testfile) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_path_with_unicode(run_as, start_method): bp_line = 6 testfile = os.path.join(BP_TEST_ROOT, u'ನನ್ನ_ಸ್ಕ್ರಿಪ್ಟ್.py') with DebugSession() as session: session.initialize( target=(run_as, testfile), start_method=start_method, ) session.set_breakpoints(testfile, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'] == Path(testfile) assert u'ಏನಾದರೂ_ಮಾಡು' == frames[0]['name'] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_stop_on_entry(run_as, start_method, tmpdir): testfile = tmpdir.join('test.py') with testfile.open('w') as stream: stream.write(''' stop_here = 1 print('done') ''') with DebugSession() as session: session.initialize( target=(run_as, str(testfile)), start_method=start_method, debug_options=['StopOnEntry'], ) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'] == Path(str(testfile)) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_flask_breakpoint_no_multiproc(bp_target, start_method): bp_file, bp_line, bp_name = { 'code': (FLASK1_APP, 11, 'home'), 'template': (FLASK1_TEMPLATE, 8, 'template') }[bp_target] with DebugSession() as session: _initialize_flask_session_no_multiproc(session, start_method) bp_var_content = 'Flask-Jinja-Test' session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() # wait for Flask web server to start wait_for_connection(FLASK_PORT) link = FLASK_LINK web_request = get_web_content(link, {}) thread_stopped = session.wait_for_next( Event('stopped'), ANY.dict_with({'reason': 'breakpoint'})) assert thread_stopped.body['threadId'] is not None tid = thread_stopped.body['threadId'] 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': bp_name, 'source': { 'sourceReference': ANY.dap_id, 'path': Path(bp_file), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = session.send_request('scopes', arguments={ 'frameId': fid }).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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] session.send_request('continue').wait_for_response(freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = FLASK_LINK + 'exit' get_web_content(link).wait_for_response() session.wait_for_exit()
def test_flask_breakpoint_multiproc(start_method): env = { 'FLASK_APP': 'app', 'FLASK_ENV': 'development', 'FLASK_DEBUG': '1', } if platform.system() != 'Windows': locale = 'en_US.utf8' if platform.system( ) == 'Linux' else 'en_US.UTF-8' env.update({ 'LC_ALL': locale, 'LANG': locale, }) with DebugSession() as parent_session: parent_session.initialize( start_method=start_method, target=('module', 'flask'), multiprocess=True, program_args=['run', '--port', str(FLASK_PORT)], ignore_unobserved=[Event('stopped'), Event('continued')], debug_options=['Jinja'], cwd=FLASK1_ROOT, env=env, expected_returncode=ANY.int, # No clean way to kill Flask server ) bp_line = 11 bp_var_content = 'Flask-Jinja-Test' parent_session.set_breakpoints(FLASK1_APP, [bp_line]) parent_session.start_debugging() with parent_session.connect_to_next_child_session() as child_session: child_session.send_request('setBreakpoints', arguments={ 'source': { 'path': FLASK1_APP }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() child_session.start_debugging() # wait for Flask server to start wait_for_connection(FLASK_PORT) web_request = get_web_content(FLASK_LINK, {}) thread_stopped = child_session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'breakpoint'}))) assert thread_stopped.body['threadId'] is not None tid = thread_stopped.body['threadId'] resp_stacktrace = child_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': 'home', 'source': { 'sourceReference': ANY.dap_id, 'path': Path(FLASK1_APP), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = child_session.send_request('scopes', arguments={ 'frameId': fid }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = child_session.send_request( 'variables', arguments={ 'variablesReference': scopes[0]['variablesReference'] }).wait_for_response() variables = [ v for v in resp_variables.body['variables'] if v['name'] == 'content' ] assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] child_session.send_request('continue').wait_for_response( freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = FLASK_LINK + 'exit' get_web_content(link).wait_for_response() child_session.wait_for_termination() parent_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_breakpoint_multiproc(start_method): with DebugSession() as parent_session: parent_session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), multiprocess=True, program_args=['runserver'], debug_options=['Django'], cwd=DJANGO1_ROOT, ignore_unobserved=[Event('stopped')], expected_returncode=ANY.int, # No clean way to kill Django server ) bp_line = 40 bp_var_content = 'Django-Django-Test' parent_session.set_breakpoints(DJANGO1_MANAGE, [bp_line]) parent_session.start_debugging() with parent_session.connect_to_next_child_session() as child_session: child_session.send_request('setBreakpoints', arguments={ 'source': { 'path': DJANGO1_MANAGE }, 'breakpoints': [ { 'line': bp_line }, ], }).wait_for_response() child_session.start_debugging() # wait for Django server to start while True: child_session.proceed() o = child_session.wait_for_next(Event('output')) if get_url_from_str(o.body['output']) is not None: break web_request = get_web_content(DJANGO_LINK + 'home', {}) thread_stopped = child_session.wait_for_next( Event('stopped', ANY.dict_with({'reason': 'breakpoint'}))) assert thread_stopped.body['threadId'] is not None tid = thread_stopped.body['threadId'] resp_stacktrace = child_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': 'home', 'source': { 'sourceReference': ANY.dap_id, 'path': Path(DJANGO1_MANAGE), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = child_session.send_request('scopes', arguments={ 'frameId': fid }).wait_for_response() scopes = resp_scopes.body['scopes'] assert len(scopes) > 0 resp_variables = child_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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] child_session.send_request('continue').wait_for_response( freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response() child_session.wait_for_termination() parent_session.wait_for_exit()
def test_with_path_mappings(pyfile, tmpdir, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import os import sys import backchannel json = backchannel.read_json() call_me_back_dir = json['call_me_back_dir'] sys.path.append(call_me_back_dir) import call_me_back def call_func(): print('break here') backchannel.write_json(os.path.abspath(__file__)) call_me_back.call_me_back(call_func) print('done') bp_line = 13 path_local = tmpdir.mkdir('local').join('code_to_debug.py').strpath path_remote = tmpdir.mkdir('remote').join('code_to_debug.py').strpath dir_local = os.path.dirname(path_local) dir_remote = os.path.dirname(path_remote) copyfile(code_to_debug, path_local) copyfile(code_to_debug, path_remote) call_me_back_dir = get_test_root('call_me_back') with DebugSession() as session: session.initialize( target=(run_as, path_remote), start_method=start_method, use_backchannel=True, path_mappings=[{ 'localRoot': dir_local, 'remoteRoot': dir_remote, }], ) session.set_breakpoints(path_remote, [bp_line]) session.start_debugging() session.write_json({'call_me_back_dir': call_me_back_dir}) hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'] == Path(path_local) source_reference = frames[0]['source']['sourceReference'] assert source_reference == 0 # Mapped files should be found locally. assert frames[1]['source']['path'].endswith('call_me_back.py') source_reference = frames[1]['source']['sourceReference'] assert source_reference > 0 # Unmapped file should have a source reference. resp_variables = session.send_request('source', arguments={ 'sourceReference': source_reference }).wait_for_response() assert "def call_me_back(callback):" in ( resp_variables.body['content']) remote_code_path = session.read_json() assert path_remote == Path(remote_code_path) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
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 do_something2(n - 1) def do_something2(n): do_something(n - 1) do_something(100) if max_frames == 'all': # trace back compresses repeated text min_expected_lines = 100 max_expected_lines = 220 args = {'maxExceptionStackFrames': 0} elif max_frames == 'default': # default is all frames min_expected_lines = 100 max_expected_lines = 220 args = {} else: min_expected_lines = 10 max_expected_lines = 21 args = {'maxExceptionStackFrames': 10} line_numbers = get_marked_line_numbers(code_to_debug) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], expected_returncode=ANY.int, args=args, ) session.send_request('setExceptionBreakpoints', { 'filters': ['uncaught'] }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == line_numbers['unhandled'] resp_exc_info = session.send_request('exceptionInfo', { 'threadId': hit.thread_id }).wait_for_response() expected = ANY.dict_with({ 'exceptionId': Regex('ArithmeticError'), 'description': 'bad code', 'breakMode': 'unhandled', 'details': ANY.dict_with({ 'typeName': Regex('ArithmeticError'), 'message': 'bad code', 'source': Path(code_to_debug), }), }) assert resp_exc_info.body == expected stack_str = resp_exc_info.body['details']['stackTrace'] stack_line_count = len(stack_str.split('\n')) assert min_expected_lines <= stack_line_count <= max_expected_lines session.send_request('continue').wait_for_response(freeze=False) 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_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_with_path_mappings(pyfile, tmpdir, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def full_function(): # Note that this function is not called, it's there just to make the mapping explicit. print('cell1 line 2') # @map_to_cell1_line_2 print('cell1 line 3') # @map_to_cell1_line_3 print('cell2 line 2') # @map_to_cell2_line_2 print('cell2 line 3') # @map_to_cell2_line_3 def strip_lines(s): return '\n'.join([line.strip() for line in s.splitlines()]) def create_code(): cell1_code = compile( strip_lines(''' # line 1 a = 1 # line 2 b = 2 # line 3 '''), '<cell1>', 'exec') cell2_code = compile( strip_lines('''# line 1 c = 3 # line 2 d = 4 # line 3 '''), '<cell2>', 'exec') return {'cell1': cell1_code, 'cell2': cell2_code} code = create_code() exec(code['cell1'], {}) exec(code['cell1'], {}) exec(code['cell2'], {}) exec(code['cell2'], {}) print('ok') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) marked_line_numbers = get_marked_line_numbers(code_to_debug) map_to_cell_1_line2 = marked_line_numbers['map_to_cell1_line_2'] map_to_cell_2_line2 = marked_line_numbers['map_to_cell2_line_2'] source_entry = code_to_debug if sys.platform == 'win32': # Check if it matches even not normalized. source_entry = code_to_debug[0].lower() + code_to_debug[1:].upper() source_entry = source_entry.replace('\\', '/') # Set breakpoints first and the map afterwards to make sure that it's reapplied. session.set_breakpoints(code_to_debug, [map_to_cell_1_line2]) session.send_request('setPydevdSourceMap', arguments={ 'source': { 'path': source_entry }, 'pydevdSourceMaps': [ { 'line': map_to_cell_1_line2, 'endLine': map_to_cell_1_line2 + 1, 'runtimeSource': { 'path': '<cell1>' }, 'runtimeLine': 2, }, { 'line': map_to_cell_2_line2, 'endLine': map_to_cell_2_line2 + 1, 'runtimeSource': { 'path': '<cell2>' }, 'runtimeLine': 2, }, ], }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'] == Path(code_to_debug) session.set_breakpoints(code_to_debug, [map_to_cell_2_line2]) # Leave only the cell2 mapping. session.send_request('setPydevdSourceMap', arguments={ 'source': { 'path': source_entry }, 'pydevdSourceMaps': [ { 'line': map_to_cell_2_line2, 'endLine': map_to_cell_2_line2 + 1, 'runtimeSource': { 'path': '<cell2>' }, 'runtimeLine': 2, }, ], }).wait_for_response() session.send_request('continue').wait_for_response() hit = session.wait_for_thread_stopped('breakpoint') # Remove the cell2 mapping so that it doesn't stop again. session.send_request('setPydevdSourceMap', arguments={ 'source': { 'path': source_entry }, 'pydevdSourceMaps': [ { 'line': map_to_cell_1_line2, 'endLine': map_to_cell_1_line2 + 1, 'runtimeSource': { 'path': '<cell1>' }, 'runtimeLine': 2, }, ], }).wait_for_response() session.send_request('continue').wait_for_response(freeze=False) 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_exceptions_and_partial_exclude_rules(pyfile, run_as, start_method, scenario): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import backchannel import sys json = backchannel.read_json() call_me_back_dir = json['call_me_back_dir'] sys.path.append(call_me_back_dir) import call_me_back def call_func(): raise RuntimeError('unhandled error') # @raise_line call_me_back.call_me_back(call_func) # @call_me_back_line print('done') line_numbers = get_marked_line_numbers(code_to_debug) call_me_back_dir = get_test_root('call_me_back') if scenario == 'exclude_code_to_debug': rules = [{ 'path': '**/' + os.path.basename(code_to_debug), 'include': False }] elif scenario == 'exclude_callback_dir': rules = [{'path': call_me_back_dir, 'include': False}] else: raise AssertionError('Unexpected scenario: %s' % (scenario, )) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, use_backchannel=True, rules=rules, ) # TODO: The process returncode doesn't match the one returned from the DAP. # See: https://github.com/Microsoft/ptvsd/issues/1278 session.expected_returncode = ANY.int filters = ['raised', 'uncaught'] session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() if scenario == 'exclude_code_to_debug': breakpoints = session.set_breakpoints(code_to_debug, [1]) assert breakpoints == [{ 'verified': False, 'message': ('Breakpoint in file excluded by filters.\n' 'Note: may be excluded because of "justMyCode" option (default == true).' ), 'source': ANY.dict_with({'path': Path(code_to_debug)}), 'line': 1 }] session.start_debugging() session.write_json({'call_me_back_dir': call_me_back_dir}) if scenario == 'exclude_code_to_debug': # Stop at handled hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] # We don't stop at the raise line but rather at the callback module which is # not excluded. assert len(frames) == 1 assert frames[0] == ANY.dict_with({ 'line': 2, 'source': ANY.dict_with({ 'path': Path(os.path.join(call_me_back_dir, 'call_me_back.py')) }) }) # assert frames[1] == ANY.dict_with({ -- filtered out # 'line': line_numbers['call_me_back_line'], # 'source': ANY.dict_with({ # 'path': Path(code_to_debug) # }) # }) # 'continue' should terminate the debuggee session.send_request('continue').wait_for_response(freeze=False) # Note: does not stop at unhandled exception because raise was in excluded file. elif scenario == 'exclude_callback_dir': # Stop at handled raise_line hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert [(frame['name'], basename(frame['source']['path'])) for frame in frames] == [ ('call_func', 'code_to_debug.py'), # ('call_me_back', 'call_me_back.py'), -- filtered out ('<module>', 'code_to_debug.py'), ] assert frames[0] == ANY.dict_with({ 'line': line_numbers['raise_line'], 'source': ANY.dict_with({'path': Path(code_to_debug)}) }) session.send_request('continue').wait_for_response() # Stop at handled call_me_back_line hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert [(frame['name'], basename(frame['source']['path'])) for frame in frames] == [ ('<module>', 'code_to_debug.py'), ] assert frames[0] == ANY.dict_with({ 'line': line_numbers['call_me_back_line'], 'source': ANY.dict_with({'path': Path(code_to_debug)}) }) session.send_request('continue').wait_for_response() # Stop at unhandled hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert [(frame['name'], basename(frame['source']['path'])) for frame in frames] == [ ('call_func', 'code_to_debug.py'), # ('call_me_back', 'call_me_back.py'), -- filtered out ('<module>', 'code_to_debug.py'), ] assert frames[0] == ANY.dict_with({ 'line': line_numbers['raise_line'], 'source': ANY.dict_with({'path': Path(code_to_debug)}) }) session.send_request('continue').wait_for_response(freeze=False) else: raise AssertionError('Unexpected scenario: %s' % (scenario, )) session.wait_for_exit()
def test_django_breakpoint_no_multiproc(bp_target, start_method): bp_file, bp_line, bp_name = { 'code': (DJANGO1_MANAGE, 40, 'home'), 'template': (DJANGO1_TEMPLATE, 8, 'Django Template'), }[bp_target] with DebugSession() as session: session.initialize( start_method=start_method, target=('file', DJANGO1_MANAGE), program_args=['runserver', '--noreload', '--', str(DJANGO_PORT)], debug_options=['Django'], cwd=DJANGO1_ROOT, expected_returncode=ANY.int, # No clean way to kill Django server ) bp_var_content = 'Django-Django-Test' session.set_breakpoints(bp_file, [bp_line]) session.start_debugging() # wait for Django server to start wait_for_connection(DJANGO_PORT) web_request = get_web_content(DJANGO_LINK + 'home', {}) hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert frames[0] == { 'id': ANY.dap_id, 'name': bp_name, 'source': { 'sourceReference': ANY, 'path': Path(bp_file), }, 'line': bp_line, 'column': 1, } fid = frames[0]['id'] resp_scopes = session.send_request('scopes', arguments={ 'frameId': fid }).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'] == 'content') assert variables == [{ 'name': 'content', 'type': 'str', 'value': repr(bp_var_content), 'presentationHint': { 'attributes': ['rawString'] }, 'evaluateName': 'content' }] session.send_request('continue').wait_for_response(freeze=False) web_content = web_request.wait_for_response() assert web_content.find(bp_var_content) != -1 # shutdown to web server link = DJANGO_LINK + 'exit' get_web_content(link).wait_for_response() session.wait_for_exit()