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_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_set_variable(pyfile, run_as, start_method): @pyfile def code_to_debug(): import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import ptvsd a = 1 ptvsd.break_into_debugger() backchannel.write_json(a) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], use_backchannel=True, ) 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'] == 'a') assert len(variables) == 1 assert variables[0] == { 'type': 'int', 'value': '1', 'name': 'a', 'evaluateName': "a" } resp_set_variable = session.send_request( 'setVariable', arguments={ 'variablesReference': scopes[0]['variablesReference'], 'name': 'a', 'value': '1000' }).wait_for_response() assert resp_set_variable.body == ANY.dict_with({ 'type': 'int', 'value': '1000' }) session.send_request('continue').wait_for_response(freeze=False) assert session.read_json() == 1000 session.wait_for_exit()
def test_add_multi_thread(self): sink = ModulesEventSink() self.mgr = ModulesManager(sink) orig_module = sys.modules['ptvsd.untangle'] expected_module = ANY.dict_with({ 'id': ANY.int, 'name': orig_module.__name__, 'package': orig_module.__package__, 'path': orig_module.__file__, 'version': orig_module.__version__, }) self.path = orig_module.__file__ def thread_worker(test, expected): time.sleep(0.01) assert expected_module == test.mgr.add_or_get_from_path(test.path) threads = [] for _ in range(10): thread = threading.Thread(target=thread_worker, args=(self, expected_module)) threads.append(thread) thread.start() for thread in threads: thread.join() assert 1 == len(sink.event_data) assert [expected_module] == self.mgr.get_all()
def test_attaching_by_pid_no_threading(pyfile, run_as, start_method): @pyfile def code_to_debug(): # import_and_enable_debugger import time wait = True i = 0 while wait: i += 1 print('in loop') time.sleep(0.1) if i > 100: raise AssertionError( 'Debugger did not change wait to False as expected within the timeout.' ) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.start_debugging() # Wait for the first print to make sure we're in the proper state to pause. session.wait_for_next( Event('output', ANY.dict_with({ 'category': 'stdout', 'output': 'in loop' }))) _change_wait_to_false_and_exit(session)
def test_valid_new_module(self): sink = ModulesEventSink() mgr = ModulesManager(sink) orig_module = sys.modules['ptvsd.untangle'] expected_module = ANY.dict_with({ 'id': ANY.int, 'name': orig_module.__name__, 'package': orig_module.__package__, 'path': orig_module.__file__, 'version': orig_module.__version__, }) assert expected_module == mgr.add_or_get_from_path( ptvsd.untangle.__file__) assert 1 == len(sink.event_data) assert [expected_module] == mgr.get_all() assert sink.event_data == [ { 'event': 'module', 'args': { 'reason': 'new', 'module': expected_module, }, }, ]
def test_with_tab_in_output(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = '\t'.join(('Hello', 'World')) print(a) # Break here so we are sure to get the output event. a = 1 # @bp1 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, ) session.set_breakpoints(code_to_debug, [line_numbers['bp1']]) session.start_debugging() # Breakpoint at the end just to make sure we get all output events. session.wait_for_thread_stopped() session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit() output = session.all_occurrences_of(Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) assert output_str.startswith('Hello\tWorld')
def test_redirect_output(pyfile, run_as, start_method, redirect): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() for i in [111, 222, 333, 444]: print(i) print() # @bp1 line_numbers = get_marked_line_numbers(code_to_debug) with DebugSession() as session: # By default 'RedirectOutput' is always set. So using this way # to override the default in session. session.debug_options = [redirect] if bool(redirect) else [] session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.set_breakpoints(code_to_debug, [line_numbers['bp1']]) session.start_debugging() # Breakpoint at the end just to make sure we get all output events. session.wait_for_thread_stopped() session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit() output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) expected = ['111', '222', '333', '444'] if bool(redirect) else [] assert expected == list(o.body['output'] for o in output if len(o.body['output']) == 3)
def test_attaching_by_pid(pyfile, run_as, start_method): @pyfile def code_to_debug(): # import_and_enable_debugger() import time def do_something(i): time.sleep(0.1) print(i) for i in range(100): do_something(i) bp_line = 5 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] # remove breakpoint and continue session.set_breakpoints(code_to_debug, []) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_next(Event('output', ANY.dict_with({'category': 'stdout'}))) session.wait_for_exit()
def test_nodebug(pyfile, run_as): @pyfile def code_to_debug(): # import_and_enable_debugger import backchannel backchannel.read_json() print('ok') with DebugSession() as session: session.no_debug = True session.initialize(target=(run_as, code_to_debug), start_method='launch', use_backchannel=True) session.set_breakpoints(code_to_debug, [3]) session.start_debugging() session.write_json(None) # Breakpoint shouldn't be hit. session.wait_for_exit() session.expect_realized( Event('output', ANY.dict_with({ 'category': 'stdout', 'output': 'ok', })))
def test_conditional_breakpoint(pyfile, run_as, start_method, condition_key): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() for i in range(0, 10): print(i) expected = { 'condition_var': ('condition', 'i==5', '5', 1), 'hitCondition_#': ('hitCondition', '5', '4', 1), 'hitCondition_eq': ('hitCondition', '==5', '4', 1), 'hitCondition_gt': ('hitCondition', '>5', '5', 5), 'hitCondition_ge': ('hitCondition', '>=5', '4', 6), 'hitCondition_lt': ('hitCondition', '<5', '0', 4), 'hitCondition_le': ('hitCondition', '<=5', '0', 5), 'hitCondition_mod': ('hitCondition', '%3', '2', 3), } condition_type, condition, value, hits = expected[condition_key] bp_line = 4 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setBreakpoints', arguments={ 'source': {'path': code_to_debug}, 'breakpoints': [{'line': bp_line, condition_type: condition}], }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] 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'] == 'i') assert variables == [ ANY.dict_with({'name': 'i', 'type': 'int', 'value': value, 'evaluateName': 'i'}) ] session.send_request('continue').wait_for_response(freeze=False) for i in range(1, hits): session.wait_for_thread_stopped() session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_add_and_remove_breakpoint(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() for i in range(0, 10): print(i) import backchannel backchannel.read_json() bp_line = 4 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, use_backchannel=True, ) session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] # remove breakpoints in file session.set_breakpoints(code_to_debug, []) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_next( Event('output', ANY.dict_with({ 'category': 'stdout', 'output': '9' }))) session.write_json('done') session.wait_for_exit() output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output = sorted( int(o.body['output'].strip()) for o in output if len(o.body['output'].strip()) > 0) assert list(range(0, 10)) == output
def test_completions_scope(pyfile, bp_line, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() class SomeClass(): def __init__(self, someVar): self.some_var = someVar def do_someting(self): someVariable = self.some_var return someVariable def someFunction(someVar): someVariable = someVar return SomeClass(someVariable).do_someting() someFunction('value') print('done') expected = expected_at_line[bp_line] with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('stopped'), Event('continued')], ) session.set_breakpoints(code_to_debug, [bp_line]) session.start_debugging() 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 len(frames) > 0 fid = frames[0]['id'] resp_completions = session.send_request('completions', arguments={ 'text': 'some', 'frameId': fid, 'column': 5, }).wait_for_response() targets = resp_completions.body['targets'] session.send_request('continue').wait_for_response(freeze=False) targets.sort(key=lambda t: t['label']) expected.sort(key=lambda t: t['label']) assert targets == expected session.wait_for_exit()
def test_log_point(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 10 for i in range(1, a): print('value: %d' % i) # Break at end too so that we're sure we get all output # events before the break. a = 10 bp_line = 5 end_bp_line = 8 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.send_request('setBreakpoints', arguments={ 'source': { 'path': code_to_debug }, 'breakpoints': [{ 'line': bp_line, 'logMessage': 'log: {a + i}' }, { 'line': end_bp_line }], }).wait_for_response() session.start_debugging() # Breakpoint at the end just to make sure we get all output events. hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert end_bp_line == frames[0]['line'] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit() assert session.get_stderr_as_string() == b'' output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) logged = sorted( int(i) for i in re.findall(r"log:\s([0-9]*)", output_str)) values = sorted( int(i) for i in re.findall(r"value:\s([0-9]*)", output_str)) assert logged == list(range(11, 20)) assert values == list(range(1, 10))
def test_unicode(pyfile, run_as, start_method): # On Python 3, variable names can contain Unicode characters. # On Python 2, they must be ASCII, but using a Unicode character in an expression should not crash debugger. @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import ptvsd # Since Unicode variable name is a SyntaxError at parse time in Python 2, # this needs to do a roundabout way of setting it to avoid parse issues. globals()[u'\u16A0'] = 123 ptvsd.break_into_debugger() print('break') with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.start_debugging() hit = session.wait_for_thread_stopped() resp_eval = session.send_request('evaluate', arguments={ 'expression': '\u16A0', 'frameId': hit.frame_id, }).wait_for_response() if sys.version_info >= (3,): assert resp_eval.body == ANY.dict_with({ 'type': 'int', 'result': '123' }) else: assert resp_eval.body == ANY.dict_with({ 'type': 'SyntaxError' }) session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_with_tab_in_output(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() print('Hello\tWorld') with DebugSession() as session: session.initialize(target=(run_as, code_to_debug), start_method=start_method) session.start_debugging() session.wait_for_exit() output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) assert output_str.startswith('Hello\tWorld')
def test_run_submodule(): cwd = get_test_root('testpkgs') with DebugSession() as session: session.initialize( target=('module', 'pkg1.sub'), start_method='launch', cwd=cwd, ) session.start_debugging() session.wait_for_next( Event('output', ANY.dict_with({ 'category': 'stdout', 'output': 'three' }))) session.wait_for_exit()
def test_get_only_module(self): sink = ModulesEventSink() mgr = ModulesManager(sink) expected_module = ANY.dict_with({ 'id': 1, 'name': 'abc.xyz', 'package': 'abc', 'path': '/abc/xyz.py', 'version': '1.2.3.4a1', }) mgr.path_to_module_id['/abc/xyz.py'] = 1 mgr.module_id_to_details[1] = expected_module assert expected_module == mgr.add_or_get_from_path('/abc/xyz.py') assert 0 == len(sink.event_data) assert [expected_module] == mgr.get_all()
def test_log_point(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 10 for i in range(1, a): print('value: %d' % i) bp_line = 5 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setBreakpoints', arguments={ 'source': { 'path': code_to_debug }, 'breakpoints': [{ 'line': bp_line, 'logMessage': 'log: {a + i}' }], }).wait_for_response() session.start_debugging() session.wait_for_exit() assert session.get_stderr_as_string() == b'' output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) logged = sorted( int(i) for i in re.findall(r"log:\s([0-9]*)", output_str)) values = sorted( int(i) for i in re.findall(r"value:\s([0-9]*)", output_str)) # NOTE: Due to https://github.com/Microsoft/ptvsd/issues/1028 we may not get # all output events. Once that is fixed we should check for the exact output # and log value assert len(logged) > 0 assert len(values) > 0
def test_targets(target_kind, client, wait, nodebug, multiproc, extra): args = ['--host', 'localhost', '--port', '8888'] if client: args += ['--client'] if wait: args += ['--wait'] if nodebug: args += ['--nodebug'] if multiproc: args += ['--multiprocess'] if target_kind == 'file': target = 'spam.py' args += [target] elif target_kind == 'module': target = 'spam' args += ['-m', target] elif target_kind == 'code': target = '123' args += ['-c', target] if extra: extra = ['ham', '--client', '--wait', '-y', 'spam', '--', '--nodebug', '--host', '--port', '-c', '--something', '-m'] args += extra else: extra = [] print(args) reload(ptvsd.options) rest = parse(args) assert list(rest) == extra assert vars(ptvsd.options) == ANY.dict_with({ 'target_kind': target_kind, 'target': target, 'host': 'localhost', 'port': 8888, 'no_debug': bool(nodebug), 'wait': bool(wait), 'multiprocess': bool(multiproc), })
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_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': '-c' if run_as == 'code' else Regex(re.escape(code_to_debug) + r'(c|o)?$') })) session.write_json('continue') ptvsd_path = session.read_json() expected_ptvsd_path = os.path.abspath(ptvsd.__file__) assert re.match( re.escape(expected_ptvsd_path) + r'(c|o)?$', ptvsd_path) session.wait_for_exit()
def _generate_system_info(): def version_str(v): return '%d.%d.%d%s%d' % (v.major, v.minor, v.micro, v.releaselevel, v.serial) try: impl_name = sys.implementation.name except AttributeError: impl_name = '' try: impl_version = version_str(sys.implementation.version) except AttributeError: impl_version = '' return ANY.dict_with({ 'ptvsd': { 'version': ptvsd.__version__, }, 'python': { 'version': version_str(sys.version_info), 'implementation': { 'name': impl_name, 'version': impl_version, 'description': ANY.str, }, }, 'platform': { 'name': sys.platform, }, 'process': { 'pid': ANY.int, 'executable': sys.executable, 'bitness': 64 if sys.maxsize > 2**32 else 32, }, })
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_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_condition_with_log_point(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 10 for i in range(1, a): print('value: %d' % i) bp_line = 5 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.send_request('setBreakpoints', arguments={ 'source': { 'path': code_to_debug }, 'breakpoints': [{ 'line': bp_line, 'logMessage': 'log: {a + i}', 'condition': 'i==5' }], }).wait_for_response() session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert bp_line == frames[0]['line'] 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'] == 'i') assert variables == [ ANY.dict_with({ 'name': 'i', 'type': 'int', 'value': '5', 'evaluateName': 'i' }) ] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit() assert session.get_stderr_as_string() == b'' output = session.all_occurrences_of( Event('output', ANY.dict_with({'category': 'stdout'}))) output_str = ''.join(o.body['output'] for o in output) logged = sorted( int(i) for i in re.findall(r"log:\s([0-9]*)", output_str)) values = sorted( int(i) for i in re.findall(r"value:\s([0-9]*)", output_str)) assert 15 in logged assert 5 in values
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_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_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()