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) # @break for i in range(100): do_something(i) bp_line = get_marked_line_numbers(code_to_debug)['break'] 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_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_ptvsd_systeminfo(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 'hello' # @bp1 print(a) 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() session.wait_for_thread_stopped() resp = session.send_request('ptvsd_systemInfo').wait_for_response() expected = _generate_system_info() assert resp.body == expected session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
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_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_debug_this_thread(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import platform import ptvsd import threading def foo(x): ptvsd.debug_this_thread() event.set() #@bp return 0 event = threading.Event() if platform.system() == 'Windows': from ctypes import CFUNCTYPE, c_void_p, c_size_t, c_uint32, windll thread_func_p = CFUNCTYPE(c_uint32, c_void_p) thread_func = thread_func_p( foo) # must hold a reference to wrapper during the call assert windll.kernel32.CreateThread(c_void_p(0), c_size_t(0), thread_func, c_void_p(0), c_uint32(0), c_void_p(0)) elif platform.system() == 'Linux' or platform.system() == 'Darwin': from ctypes import CDLL, CFUNCTYPE, byref, c_void_p, c_ulong from ctypes.util import find_library libpthread = CDLL(find_library('libpthread')) thread_func_p = CFUNCTYPE(c_void_p, c_void_p) thread_func = thread_func_p( foo) # must hold a reference to wrapper during the call assert not libpthread.pthread_create(byref( c_ulong(0)), c_void_p(0), thread_func, c_void_p(0)) else: assert False event.wait() 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')], ) session.set_breakpoints(code_to_debug, [line_numbers['bp']]) session.start_debugging() session.wait_for_thread_stopped() session.send_request('continue').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') # @bp @pyfile def script2(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import script1 script1.do_something() # @bp print('Done') bp_script1_line = get_marked_line_numbers(script1)['bp'] bp_script2_line = get_marked_line_numbers(script2)['bp'] with DebugSession() as session: session.initialize( target=(run_as, script2), start_method=start_method, ) 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_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_deep_stacks(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def deep_stack(level): if level <= 0: print('done') #@bp return level deep_stack(level - 1) deep_stack(100) 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, ) 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() full_frames = hit.stacktrace.body['stackFrames'] assert len(full_frames) > 100 # Construct stack from parts frames = [] start = 0 for _ in range(5): resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': hit.thread_id, 'startFrame': start, 'levels': 25 }).wait_for_response() assert resp_stacktrace.body['totalFrames'] > 0 frames += resp_stacktrace.body['stackFrames'] start = len(frames) assert full_frames == frames session.send_request('continue').wait_for_response() session.wait_for_exit()
def test_error_in_condition(pyfile, run_as, start_method, error_name): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def do_something_bad(): raise ArithmeticError() for i in range(1, 10): # @bp pass # NOTE: NameError in condition, is a special case. Pydevd is configured to skip # traceback for name errors. See https://github.com/Microsoft/ptvsd/issues/853 # for more details. For all other errors we should be printing traceback. condition = { 'NameError': ('x==5'), # 'x' does not exist in the debuggee 'OtherError': ('do_something_bad()==5') # throws some error } bp_line = get_marked_line_numbers(code_to_debug)['bp'] 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, 'condition': condition[error_name], }], }).wait_for_response() session.start_debugging() session.wait_for_exit() assert session.get_stdout_as_string() == b'' if error_name == 'NameError': assert session.get_stderr_as_string().find(b'NameError') == -1 else: assert session.get_stderr_as_string().find(b'ArithmeticError') > 0
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_reattach(pyfile, run_as, start_method): @pyfile def code_to_debug(): import time import ptvsd import backchannel from dbgimporter import import_and_enable_debugger import_and_enable_debugger() ptvsd.break_into_debugger() print('first') # @break1 backchannel.write_json('continued') for _ in range(0, 100): time.sleep(0.1) ptvsd.break_into_debugger() print('second') # @break2 with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, use_backchannel=True, kill_ptvsd=False, skip_capture=True, ) marked_line_numbers = get_marked_line_numbers(code_to_debug) session.start_debugging() hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert marked_line_numbers['break1'] == frames[0]['line'] session.send_request('disconnect').wait_for_response(freeze=False) session.wait_for_disconnect() assert session.read_json() == 'continued' # re-attach with session.connect_with_new_session( target=(run_as, code_to_debug), ) as session2: session2.start_debugging() hit = session2.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] assert marked_line_numbers['break2'] == frames[0]['line'] session2.send_request('disconnect').wait_for_response(freeze=False) session2.wait_for_disconnect()
def test_client_ide_from_path_mapping_linux_backend(pyfile, tmpdir, run_as, start_method, invalid_os_type): ''' Test simulating that the backend is on Linux and the client is on Windows (automatically detect it from the path mapping). ''' @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import backchannel import pydevd_file_utils backchannel.write_json({'ide_os': pydevd_file_utils._ide_os}) print('done') # @break_here with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], use_backchannel=True, path_mappings=[{ 'localRoot': 'C:\\TEMP\\src', 'remoteRoot': os.path.dirname(code_to_debug), }], ) if invalid_os_type: session.debug_options.append('CLIENT_OS_TYPE=INVALID') bp_line = get_marked_line_numbers(code_to_debug)['break_here'] session.set_breakpoints( 'c:\\temp\\src\\' + os.path.basename(code_to_debug), [bp_line]) session.start_debugging() hit = session.wait_for_thread_stopped('breakpoint') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source'][ 'path'] == 'C:\\TEMP\\src\\' + os.path.basename(code_to_debug) json_read = session.read_json() assert json_read == {'ide_os': 'WINDOWS'} session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_thread_count(pyfile, run_as, start_method, count): @pyfile def code_to_debug(): import threading import time import sys from dbgimporter import import_and_enable_debugger import_and_enable_debugger() stop = False def worker(tid, offset): i = 0 global stop while not stop: time.sleep(0.01) i += 1 threads = [] if sys.argv[1] != '1': for i in [111, 222]: thread = threading.Thread(target=worker, args=(i, len(threads))) threads.append(thread) thread.start() print('check here') # @bp stop = True 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, program_args=[str(count)], ) session.set_breakpoints(code_to_debug, [line_numbers['bp']]) session.start_debugging() session.wait_for_thread_stopped() resp_threads = session.send_request('threads').wait_for_response() assert len(resp_threads.body['threads']) == count 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_step_multi_threads(pyfile, run_as, start_method, stepping_resumes_all_threads): @pyfile def code_to_debug(): ''' After breaking on the thread 1, thread 2 should pause waiting for the event1 to be set, so, when we step return on thread 1, the program should finish if all threads are resumed or should keep waiting for the thread 2 to run if only thread 1 is resumed. ''' import threading from dbgimporter import import_and_enable_debugger import_and_enable_debugger() event0 = threading.Event() event1 = threading.Event() event2 = threading.Event() event3 = threading.Event() def _thread1(): while not event0.is_set(): event0.wait(timeout=.001) event1.set() # @break_thread_1 while not event2.is_set(): event2.wait(timeout=.001) # Note: we can only get here if thread 2 is also released. event3.set() def _thread2(): event0.set() while not event1.is_set(): event1.wait(timeout=.001) event2.set() while not event3.is_set(): event3.wait(timeout=.001) threads = [ threading.Thread(target=_thread1, name='thread1'), threading.Thread(target=_thread2, name='thread2'), ] for t in threads: t.start() for t in threads: t.join() line_numbers = get_marked_line_numbers(code_to_debug) with DebugSession() as session: session.stepping_resumes_all_threads = stepping_resumes_all_threads session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.set_breakpoints(code_to_debug, [line_numbers['break_thread_1']]) session.start_debugging() _thread_stopped, _resp_stacktrace, thread_id, _ = session.wait_for_thread_stopped() resp_threads = session.send_request('threads').wait_for_response() assert len(resp_threads.body['threads']) == 3 thread_name_to_id = dict((t['name'], t['id']) for t in resp_threads.body['threads']) assert thread_id == thread_name_to_id['thread1'] if stepping_resumes_all_threads or stepping_resumes_all_threads is None: # stepping_resumes_all_threads == None means we should use default (which is to # resume all threads) -- in which case stepping out will exit the program. session.send_request('stepOut', {'threadId': thread_id}).wait_for_response(freeze=False) else: session.send_request('stepOut', {'threadId': thread_id}).wait_for_response() # Wait a second and check that threads are still there. time.sleep(1) resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': thread_name_to_id['thread1'], }).wait_for_response() assert '_thread1' in [x['name'] for x in resp_stacktrace.body['stackFrames']] resp_stacktrace = session.send_request('stackTrace', arguments={ 'threadId': thread_name_to_id['thread2'], }).wait_for_response() assert '_thread2' in [x['name'] for x in resp_stacktrace.body['stackFrames']] session.send_request('continue').wait_for_response(freeze=False) 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_invalid_breakpoints(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() b = True while b: #@bp1-expected pass #@bp1-requested break print() #@bp2-expected [ #@bp2-requested 1, 2, 3, #@bp3-expected ] #@bp3-requested # Python 2.7 only. print() #@bp4-expected print(1, #@bp4-requested-1 2, 3, #@bp4-requested-2 4, 5, 6) line_numbers = get_marked_line_numbers(code_to_debug) from tests.helpers import print print(line_numbers) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) requested_bps = [ line_numbers['bp1-requested'], line_numbers['bp2-requested'], line_numbers['bp3-requested'], ] if sys.version_info < (3,): requested_bps += [ line_numbers['bp4-requested-1'], line_numbers['bp4-requested-2'], ] actual_bps = session.set_breakpoints(code_to_debug, requested_bps) actual_bps = [bp['line'] for bp in actual_bps] expected_bps = [ line_numbers['bp1-expected'], line_numbers['bp2-expected'], line_numbers['bp3-expected'], ] if sys.version_info < (3,): expected_bps += [ line_numbers['bp4-expected'], line_numbers['bp4-expected'], ] assert expected_bps == actual_bps # Now let's make sure that we hit all of the expected breakpoints, # and stop where we expect them to be. session.start_debugging() # If there's multiple breakpoints on the same line, we only stop once, # so remove duplicates first. expected_bps = sorted(set(expected_bps)) while expected_bps: hit = session.wait_for_thread_stopped() frames = hit.stacktrace.body['stackFrames'] line = frames[0]['line'] assert line == expected_bps[0] del expected_bps[0] session.send_request('continue').wait_for_response() assert not expected_bps 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_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_hex_numbers(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() a = 100 b = [1, 10, 100] c = {10: 10, 100: 100, 1000: 1000} d = {(1, 10, 100): (10000, 100000, 100000)} print((a, b, c, d)) # @bp line_numbers = get_marked_line_numbers(code_to_debug) print(line_numbers) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ) session.set_breakpoints(code_to_debug, [line_numbers['bp']]) 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'], 'format': {'hex': True} }).wait_for_response() variables = list(v for v in resp_variables.body['variables'] if v['name'] in ('a', 'b', 'c', 'd')) a, b, c, d = sorted(variables, key=lambda v: v['name']) assert a == ANY.dict_with({ 'name': 'a', 'value': "0x64", 'type': 'int', 'evaluateName': 'a', 'variablesReference': 0, }) assert b == ANY.dict_with({ 'name': 'b', 'value': "[0x1, 0xa, 0x64]", 'type': 'list', 'evaluateName': 'b', 'variablesReference': ANY.dap_id, }) resp_variables = session.send_request('variables', arguments={ 'variablesReference': b['variablesReference'], 'format': {'hex': True} }).wait_for_response() b_children = resp_variables.body['variables'] assert b_children == [ {'name': '0x0', 'value': '0x1', 'type': 'int', 'evaluateName': 'b[0]', 'variablesReference': 0, }, {'name': '0x1', 'value': '0xa', 'type': 'int', 'evaluateName': 'b[1]', 'variablesReference': 0, }, {'name': '0x2', 'value': '0x64', 'type': 'int', 'evaluateName': 'b[2]', 'variablesReference': 0, }, {'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(b)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, }, ] assert c == ANY.dict_with({ 'name': 'c', 'value': '{0xa: 0xa, 0x64: 0x64, 0x3e8: 0x3e8}', 'type': 'dict', 'evaluateName': 'c', 'variablesReference': ANY.dap_id, }) resp_variables = session.send_request('variables', arguments={ 'variablesReference': c['variablesReference'], 'format': {'hex': True} }).wait_for_response() c_children = resp_variables.body['variables'] assert c_children == [ {'name': '0x3e8', 'value': '0x3e8', 'type': 'int', 'evaluateName': 'c[1000]', 'variablesReference': 0, }, {'name': '0x64', 'value': '0x64', 'type': 'int', 'evaluateName': 'c[100]', 'variablesReference': 0, }, {'name': '0xa', 'value': '0xa', 'type': 'int', 'evaluateName': 'c[10]', 'variablesReference': 0, }, {'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(c)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, } ] assert d == ANY.dict_with({ 'name': 'd', 'value': '{(0x1, 0xa, 0x64): (0x2710, 0x186a0, 0x186a0)}', 'type': 'dict', 'evaluateName': 'd', 'variablesReference': ANY.dap_id, }) resp_variables = session.send_request('variables', arguments={ 'variablesReference': d['variablesReference'], 'format': {'hex': True} }).wait_for_response() d_children = resp_variables.body['variables'] assert d_children == [ {'name': '(0x1, 0xa, 0x64)', 'value': '(0x2710, 0x186a0, 0x186a0)', 'type': 'tuple', 'evaluateName': 'd[(1, 10, 100)]', 'variablesReference': ANY.dap_id}, {'name': '__len__', 'value': '0x1', 'type': 'int', 'evaluateName': 'len(d)', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, } ] resp_variables = session.send_request('variables', arguments={ 'variablesReference': d_children[0]['variablesReference'], 'format': {'hex': True} }).wait_for_response() d_child_of_child = resp_variables.body['variables'] assert d_child_of_child == [ {'name': '0x0', 'value': '0x2710', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][0]', 'variablesReference': 0, }, {'name': '0x1', 'value': '0x186a0', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][1]', 'variablesReference': 0, }, {'name': '0x2', 'value': '0x186a0', 'type': 'int', 'evaluateName': 'd[(1, 10, 100)][2]', 'variablesReference': 0, }, {'name': '__len__', 'value': '0x3', 'type': 'int', 'evaluateName': 'len(d[(1, 10, 100)])', 'variablesReference': 0, 'presentationHint': {'attributes': ['readOnly']}, } ] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_systemexit(pyfile, run_as, start_method, raised, uncaught, zero, exit_code): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import sys exit_code = eval(sys.argv[1]) print('sys.exit(%r)' % (exit_code, )) try: sys.exit(exit_code) # @handled except SystemExit: pass sys.exit(exit_code) # @unhandled line_numbers = get_marked_line_numbers(code_to_debug) filters = [] if raised: filters += ['raised'] if uncaught: filters += ['uncaught'] with DebugSession() as session: session.program_args = [repr(exit_code)] if zero: session.debug_options += ['BreakOnSystemExitZero'] session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], expected_returncode=ANY.int, ) session.send_request('setExceptionBreakpoints', { 'filters': filters }).wait_for_response() session.start_debugging() # When breaking on raised exceptions, we'll stop on both lines, # unless it's SystemExit(0) and we asked to ignore that. if raised and (zero or exit_code != 0): hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == line_numbers['handled'] session.send_request('continue').wait_for_response(freeze=False) hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == line_numbers['unhandled'] session.send_request('continue').wait_for_response(freeze=False) # When breaking on uncaught exceptions, we'll stop on the second line, # unless it's SystemExit(0) and we asked to ignore that. # Note that if both raised and uncaught filters are set, there will be # two stop for the second line - one for exception being raised, and one # for it unwinding the stack without finding a handler. The block above # takes care of the first stop, so here we just take care of the second. if uncaught and (zero or exit_code != 0): hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['line'] == line_numbers['unhandled'] session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_tracing(pyfile, start_method, run_as): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() import ptvsd def func(expected_tracing): assert ptvsd.tracing() == expected_tracing, ( "inside func({0!r})".format(expected_tracing) ) print(1) # @inner1 # Test nested change/restore. Going from False to True only works entirely # correctly on Python 3.6+; on earlier versions, if tracing wasn't enabled # when the function is entered, re-enabling it later will not cause the # breakpoints in this function to light up. However, it will allow hitting # breakpoints in functions called from here. def inner2(): print(2) # @inner2 with ptvsd.tracing(not expected_tracing): assert ptvsd.tracing() != expected_tracing, "inside with-statement" inner2() assert ptvsd.tracing() == expected_tracing, "after with-statement" print(3) # @inner3 assert ptvsd.tracing(), "before tracing(False)" ptvsd.tracing(False) assert not ptvsd.tracing(), "after tracing(False)" print(0) # @outer1 func(False) ptvsd.tracing(True) assert ptvsd.tracing(), "after tracing(True)" print(0) # @outer2 func(True) line_numbers = get_marked_line_numbers(code_to_debug) print(line_numbers) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], env={'PTVSD_USE_CONTINUED': '1'}, ) session.set_breakpoints(code_to_debug, line_numbers.values()) session.start_debugging() stop = session.wait_for_thread_stopped() frame = stop.stacktrace.body['stackFrames'][0] assert frame == ANY.dict_with({ "line": line_numbers["inner2"], }) session.send_request('continue').wait_for_response() stop = session.wait_for_thread_stopped() frame = stop.stacktrace.body['stackFrames'][0] assert frame == ANY.dict_with({ "line": line_numbers["outer2"], }) session.send_request('continue').wait_for_response() stop = session.wait_for_thread_stopped() frame = stop.stacktrace.body['stackFrames'][0] assert frame == ANY.dict_with({ "line": line_numbers["inner1"], }) session.send_request('continue').wait_for_response() stop = session.wait_for_thread_stopped() frame = stop.stacktrace.body['stackFrames'][0] assert frame == ANY.dict_with({ "line": line_numbers["inner3"], }) session.send_request('continue').wait_for_response() session.wait_for_exit()
def test_completions_cases(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]) # @break line_numbers = get_marked_line_numbers(code_to_debug) bp_line = line_numbers['break'] 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() response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'b.', 'column': 3, }).wait_for_response() labels = set(target['label'] for target in response.body['targets']) assert labels.issuperset( ['get', 'items', 'keys', 'setdefault', 'update', 'values']) response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'x = b.setdefault', 'column': 13, }).wait_for_response() assert response.body['targets'] == [{ 'label': 'setdefault', 'length': 6, 'start': 6, 'type': 'function' }] response = session.send_request('completions', arguments={ 'frameId': hit.frame_id, 'text': 'not_there', 'column': 10, }).wait_for_response() assert not response.body['targets'] # Check errors with pytest.raises(RequestFailure) as request_failure: response = session.send_request( 'completions', arguments={ 'frameId': 9999999, # frameId not available. 'text': 'not_there', 'column': 10, }).wait_for_response() assert 'Wrong ID sent from the client:' in request_failure.value.message session.send_request('continue').wait_for_response(freeze=False) session.wait_for_exit()
def test_set_next_statement(pyfile, run_as, start_method): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() def func(): print(1) # @inner1 print(2) # @inner2 print(3) # @outer3 func() line_numbers = get_marked_line_numbers(code_to_debug) print(line_numbers) with DebugSession() as session: session.initialize( target=(run_as, code_to_debug), start_method=start_method, ignore_unobserved=[Event('continued')], ) session.set_breakpoints(code_to_debug, [line_numbers['inner1']]) session.start_debugging() stop = session.wait_for_thread_stopped() frames = stop.stacktrace.body['stackFrames'] line = frames[0]['line'] assert line == line_numbers['inner1'] targets = session.send_request('gotoTargets', { 'source': { 'path': code_to_debug }, 'line': line_numbers['outer3'], }).wait_for_response().body['targets'] assert targets == [{ 'id': ANY.num, 'label': ANY.str, 'line': line_numbers['outer3'] }] outer3_target = targets[0]['id'] with pytest.raises(Exception): session.send_request('goto', { 'threadId': stop.thread_id, 'targetId': outer3_target, }).wait_for_response() targets = session.send_request('gotoTargets', { 'source': { 'path': code_to_debug }, 'line': line_numbers['inner2'], }).wait_for_response().body['targets'] assert targets == [{ 'id': ANY.num, 'label': ANY.str, 'line': line_numbers['inner2'], }] inner2_target = targets[0]['id'] session.send_request('goto', { 'threadId': stop.thread_id, 'targetId': inner2_target, }).wait_for_response() session.wait_for_next(Event('continued')) stop = session.wait_for_thread_stopped(reason='goto') frames = stop.stacktrace.body['stackFrames'] line = frames[0]['line'] assert line == line_numbers['inner2'] session.send_request('continue').wait_for_response() session.wait_for_exit()
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 # @in_do_something def someFunction(someVar): someVariable = someVar return SomeClass(someVariable).do_someting() # @in_some_function someFunction('value') print('done') # @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')], ) line_numbers = get_marked_line_numbers(code_to_debug) session.set_breakpoints(code_to_debug, [line_numbers[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_raise_exception_options(pyfile, run_as, start_method, exceptions, break_mode): if break_mode in ('never', 'unhandled', 'userUnhandled'): @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() raise AssertionError() # @AssertionError if break_mode == 'never': expect_exceptions = [] elif 'AssertionError' in exceptions or not exceptions: # Only AssertionError is raised in this use-case. expect_exceptions = ['AssertionError'] else: expect_exceptions = [] else: expect_exceptions = exceptions[:] if not expect_exceptions: # Deal with the Python Exceptions category expect_exceptions = [ 'RuntimeError', 'AssertionError', 'IndexError' ] @pyfile def code_to_debug(): from dbgimporter import import_and_enable_debugger import_and_enable_debugger() try: raise RuntimeError() # @RuntimeError except RuntimeError: pass try: raise AssertionError() # @AssertionError except AssertionError: pass try: raise IndexError() # @IndexError except IndexError: pass 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'), Event('stopped')], expected_returncode=ANY.int, ) path = [ { 'names': ['Python Exceptions'] }, ] if exceptions: path.append({'names': exceptions}) session.send_request( 'setExceptionBreakpoints', { 'filters': [], # Unused when exceptionOptions is passed. 'exceptionOptions': [{ 'path': path, 'breakMode': break_mode, # Can be "never", "always", "unhandled", "userUnhandled" }], }).wait_for_response() session.start_debugging() for expected_exception in expect_exceptions: hit = session.wait_for_thread_stopped(reason='exception') frames = hit.stacktrace.body['stackFrames'] assert frames[0]['source']['path'].endswith('code_to_debug.py') assert frames[0]['line'] == line_numbers[expected_exception] session.send_request('continue').wait_for_response(freeze=False) 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()