mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 10:26:02 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			1229 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			1229 lines
		
	
	
	
		
			44 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """ Test the bdb module.
 | |
| 
 | |
|     A test defines a list of tuples that may be seen as paired tuples, each
 | |
|     pair being defined by 'expect_tuple, set_tuple' as follows:
 | |
| 
 | |
|         ([event, [lineno[, co_name[, eargs]]]]), (set_type, [sargs])
 | |
| 
 | |
|     * 'expect_tuple' describes the expected current state of the Bdb instance.
 | |
|       It may be the empty tuple and no check is done in that case.
 | |
|     * 'set_tuple' defines the set_*() method to be invoked when the Bdb
 | |
|       instance reaches this state.
 | |
| 
 | |
|     Example of an 'expect_tuple, set_tuple' pair:
 | |
| 
 | |
|         ('line', 2, 'tfunc_main'), ('step', )
 | |
| 
 | |
|     Definitions of the members of the 'expect_tuple':
 | |
|         event:
 | |
|             Name of the trace event. The set methods that do not give back
 | |
|             control to the tracer [1] do not trigger a tracer event and in
 | |
|             that case the next 'event' may be 'None' by convention, its value
 | |
|             is not checked.
 | |
|             [1] Methods that trigger a trace event are set_step(), set_next(),
 | |
|             set_return(), set_until() and set_continue().
 | |
|         lineno:
 | |
|             Line number. Line numbers are relative to the start of the
 | |
|             function when tracing a function in the test_bdb module (i.e. this
 | |
|             module).
 | |
|         co_name:
 | |
|             Name of the function being currently traced.
 | |
|         eargs:
 | |
|             A tuple:
 | |
|             * On an 'exception' event the tuple holds a class object, the
 | |
|               current exception must be an instance of this class.
 | |
|             * On a 'line' event, the tuple holds a dictionary and a list. The
 | |
|               dictionary maps each breakpoint number that has been hit on this
 | |
|               line to its hits count. The list holds the list of breakpoint
 | |
|               number temporaries that are being deleted.
 | |
| 
 | |
|     Definitions of the members of the 'set_tuple':
 | |
|         set_type:
 | |
|             The type of the set method to be invoked. This may
 | |
|             be the type of one of the Bdb set methods: 'step', 'next',
 | |
|             'until', 'return', 'continue', 'break', 'quit', or the type of one
 | |
|             of the set methods added by test_bdb.Bdb: 'ignore', 'enable',
 | |
|             'disable', 'clear', 'up', 'down'.
 | |
|         sargs:
 | |
|             The arguments of the set method if any, packed in a tuple.
 | |
| """
 | |
| 
 | |
| import bdb as _bdb
 | |
| import sys
 | |
| import os
 | |
| import unittest
 | |
| import textwrap
 | |
| import importlib
 | |
| import linecache
 | |
| from contextlib import contextmanager
 | |
| from itertools import islice, repeat
 | |
| from test.support import import_helper
 | |
| from test.support import os_helper
 | |
| from test.support import patch_list
 | |
| 
 | |
| 
 | |
| class BdbException(Exception): pass
 | |
| class BdbError(BdbException): """Error raised by the Bdb instance."""
 | |
| class BdbSyntaxError(BdbException): """Syntax error in the test case."""
 | |
| class BdbNotExpectedError(BdbException): """Unexpected result."""
 | |
| 
 | |
| # When 'dry_run' is set to true, expect tuples are ignored and the actual
 | |
| # state of the tracer is printed after running each set_*() method of the test
 | |
| # case. The full list of breakpoints and their attributes is also printed
 | |
| # after each 'line' event where a breakpoint has been hit.
 | |
| dry_run = 0
 | |
| 
 | |
| def reset_Breakpoint():
 | |
|     _bdb.Breakpoint.clearBreakpoints()
 | |
| 
 | |
| def info_breakpoints():
 | |
|     bp_list = [bp for  bp in _bdb.Breakpoint.bpbynumber if bp]
 | |
|     if not bp_list:
 | |
|         return ''
 | |
| 
 | |
|     header_added = False
 | |
|     for bp in bp_list:
 | |
|         if not header_added:
 | |
|             info = 'BpNum Temp Enb Hits Ignore Where\n'
 | |
|             header_added = True
 | |
| 
 | |
|         disp = 'yes ' if bp.temporary else 'no  '
 | |
|         enab = 'yes' if bp.enabled else 'no '
 | |
|         info += ('%-5d %s %s %-4d %-6d at %s:%d' %
 | |
|                     (bp.number, disp, enab, bp.hits, bp.ignore,
 | |
|                      os.path.basename(bp.file), bp.line))
 | |
|         if bp.cond:
 | |
|             info += '\n\tstop only if %s' % (bp.cond,)
 | |
|         info += '\n'
 | |
|     return info
 | |
| 
 | |
| class Bdb(_bdb.Bdb):
 | |
|     """Extend Bdb to enhance test coverage."""
 | |
| 
 | |
|     def trace_dispatch(self, frame, event, arg):
 | |
|         self.currentbp = None
 | |
|         return super().trace_dispatch(frame, event, arg)
 | |
| 
 | |
|     def set_break(self, filename, lineno, temporary=False, cond=None,
 | |
|                   funcname=None):
 | |
|         if isinstance(funcname, str):
 | |
|             if filename == __file__:
 | |
|                 globals_ = globals()
 | |
|             else:
 | |
|                 module = importlib.import_module(filename[:-3])
 | |
|                 globals_ = module.__dict__
 | |
|             func = eval(funcname, globals_)
 | |
|             code = func.__code__
 | |
|             filename = code.co_filename
 | |
|             lineno = code.co_firstlineno
 | |
|             funcname = code.co_name
 | |
| 
 | |
|         res = super().set_break(filename, lineno, temporary=temporary,
 | |
|                                  cond=cond, funcname=funcname)
 | |
|         if isinstance(res, str):
 | |
|             raise BdbError(res)
 | |
|         return res
 | |
| 
 | |
|     def get_stack(self, f, t):
 | |
|         self.stack, self.index = super().get_stack(f, t)
 | |
|         self.frame = self.stack[self.index][0]
 | |
|         return self.stack, self.index
 | |
| 
 | |
|     def set_ignore(self, bpnum):
 | |
|         """Increment the ignore count of Breakpoint number 'bpnum'."""
 | |
|         bp = self.get_bpbynumber(bpnum)
 | |
|         bp.ignore += 1
 | |
| 
 | |
|     def set_enable(self, bpnum):
 | |
|         bp = self.get_bpbynumber(bpnum)
 | |
|         bp.enabled = True
 | |
| 
 | |
|     def set_disable(self, bpnum):
 | |
|         bp = self.get_bpbynumber(bpnum)
 | |
|         bp.enabled = False
 | |
| 
 | |
|     def set_clear(self, fname, lineno):
 | |
|         err = self.clear_break(fname, lineno)
 | |
|         if err:
 | |
|             raise BdbError(err)
 | |
| 
 | |
|     def set_up(self):
 | |
|         """Move up in the frame stack."""
 | |
|         if not self.index:
 | |
|             raise BdbError('Oldest frame')
 | |
|         self.index -= 1
 | |
|         self.frame = self.stack[self.index][0]
 | |
| 
 | |
|     def set_down(self):
 | |
|         """Move down in the frame stack."""
 | |
|         if self.index + 1 == len(self.stack):
 | |
|             raise BdbError('Newest frame')
 | |
|         self.index += 1
 | |
|         self.frame = self.stack[self.index][0]
 | |
| 
 | |
| class Tracer(Bdb):
 | |
|     """A tracer for testing the bdb module."""
 | |
| 
 | |
|     def __init__(self, expect_set, skip=None, dry_run=False, test_case=None):
 | |
|         super().__init__(skip=skip)
 | |
|         self.expect_set = expect_set
 | |
|         self.dry_run = dry_run
 | |
|         self.header = ('Dry-run results for %s:' % test_case if
 | |
|                        test_case is not None else None)
 | |
|         self.init_test()
 | |
| 
 | |
|     def init_test(self):
 | |
|         self.cur_except = None
 | |
|         self.expect_set_no = 0
 | |
|         self.breakpoint_hits = None
 | |
|         self.expected_list = list(islice(self.expect_set, 0, None, 2))
 | |
|         self.set_list = list(islice(self.expect_set, 1, None, 2))
 | |
| 
 | |
|     def trace_dispatch(self, frame, event, arg):
 | |
|         # On an 'exception' event, call_exc_trace() in Python/ceval.c discards
 | |
|         # a BdbException raised by the Tracer instance, so we raise it on the
 | |
|         # next trace_dispatch() call that occurs unless the set_quit() or
 | |
|         # set_continue() method has been invoked on the 'exception' event.
 | |
|         if self.cur_except is not None:
 | |
|             raise self.cur_except
 | |
| 
 | |
|         if event == 'exception':
 | |
|             try:
 | |
|                 res = super().trace_dispatch(frame, event, arg)
 | |
|                 return res
 | |
|             except BdbException as e:
 | |
|                 self.cur_except = e
 | |
|                 return self.trace_dispatch
 | |
|         else:
 | |
|             return super().trace_dispatch(frame, event, arg)
 | |
| 
 | |
|     def user_call(self, frame, argument_list):
 | |
|         # Adopt the same behavior as pdb and, as a side effect, skip also the
 | |
|         # first 'call' event when the Tracer is started with Tracer.runcall()
 | |
|         # which may be possibly considered as a bug.
 | |
|         if not self.stop_here(frame):
 | |
|             return
 | |
|         self.process_event('call', frame, argument_list)
 | |
|         self.next_set_method()
 | |
| 
 | |
|     def user_line(self, frame):
 | |
|         self.process_event('line', frame)
 | |
| 
 | |
|         if self.dry_run and self.breakpoint_hits:
 | |
|             info = info_breakpoints().strip('\n')
 | |
|             # Indent each line.
 | |
|             for line in info.split('\n'):
 | |
|                 print('  ' + line)
 | |
|         self.delete_temporaries()
 | |
|         self.breakpoint_hits = None
 | |
| 
 | |
|         self.next_set_method()
 | |
| 
 | |
|     def user_return(self, frame, return_value):
 | |
|         self.process_event('return', frame, return_value)
 | |
|         self.next_set_method()
 | |
| 
 | |
|     def user_exception(self, frame, exc_info):
 | |
|         self.exc_info = exc_info
 | |
|         self.process_event('exception', frame)
 | |
|         self.next_set_method()
 | |
| 
 | |
|     def user_opcode(self, frame):
 | |
|         self.process_event('opcode', frame)
 | |
|         self.next_set_method()
 | |
| 
 | |
|     def do_clear(self, arg):
 | |
|         # The temporary breakpoints are deleted in user_line().
 | |
|         bp_list = [self.currentbp]
 | |
|         self.breakpoint_hits = (bp_list, bp_list)
 | |
| 
 | |
|     def delete_temporaries(self):
 | |
|         if self.breakpoint_hits:
 | |
|             for n in self.breakpoint_hits[1]:
 | |
|                 self.clear_bpbynumber(n)
 | |
| 
 | |
|     def pop_next(self):
 | |
|         self.expect_set_no += 1
 | |
|         try:
 | |
|             self.expect = self.expected_list.pop(0)
 | |
|         except IndexError:
 | |
|             raise BdbNotExpectedError(
 | |
|                 'expect_set list exhausted, cannot pop item %d' %
 | |
|                 self.expect_set_no)
 | |
|         self.set_tuple = self.set_list.pop(0)
 | |
| 
 | |
|     def process_event(self, event, frame, *args):
 | |
|         # Call get_stack() to enable walking the stack with set_up() and
 | |
|         # set_down().
 | |
|         tb = None
 | |
|         if event == 'exception':
 | |
|             tb = self.exc_info[2]
 | |
|         self.get_stack(frame, tb)
 | |
| 
 | |
|         # A breakpoint has been hit and it is not a temporary.
 | |
|         if self.currentbp is not None and not self.breakpoint_hits:
 | |
|             bp_list = [self.currentbp]
 | |
|             self.breakpoint_hits = (bp_list, [])
 | |
| 
 | |
|         # Pop next event.
 | |
|         self.event= event
 | |
|         self.pop_next()
 | |
|         if self.dry_run:
 | |
|             self.print_state(self.header)
 | |
|             return
 | |
| 
 | |
|         # Validate the expected results.
 | |
|         if self.expect:
 | |
|             self.check_equal(self.expect[0], event, 'Wrong event type')
 | |
|             self.check_lno_name()
 | |
| 
 | |
|         if event in ('call', 'return'):
 | |
|             self.check_expect_max_size(3)
 | |
|         elif len(self.expect) > 3:
 | |
|             if event == 'line':
 | |
|                 bps, temporaries = self.expect[3]
 | |
|                 bpnums = sorted(bps.keys())
 | |
|                 if not self.breakpoint_hits:
 | |
|                     self.raise_not_expected(
 | |
|                         'No breakpoints hit at expect_set item %d' %
 | |
|                         self.expect_set_no)
 | |
|                 self.check_equal(bpnums, self.breakpoint_hits[0],
 | |
|                     'Breakpoint numbers do not match')
 | |
|                 self.check_equal([bps[n] for n in bpnums],
 | |
|                     [self.get_bpbynumber(n).hits for
 | |
|                         n in self.breakpoint_hits[0]],
 | |
|                     'Wrong breakpoint hit count')
 | |
|                 self.check_equal(sorted(temporaries), self.breakpoint_hits[1],
 | |
|                     'Wrong temporary breakpoints')
 | |
| 
 | |
|             elif event == 'exception':
 | |
|                 if not isinstance(self.exc_info[1], self.expect[3]):
 | |
|                     self.raise_not_expected(
 | |
|                         "Wrong exception at expect_set item %d, got '%s'" %
 | |
|                         (self.expect_set_no, self.exc_info))
 | |
| 
 | |
|     def check_equal(self, expected, result, msg):
 | |
|         if expected == result:
 | |
|             return
 | |
|         self.raise_not_expected("%s at expect_set item %d, got '%s'" %
 | |
|                                 (msg, self.expect_set_no, result))
 | |
| 
 | |
|     def check_lno_name(self):
 | |
|         """Check the line number and function co_name."""
 | |
|         s = len(self.expect)
 | |
|         if s > 1:
 | |
|             lineno = self.lno_abs2rel()
 | |
|             self.check_equal(self.expect[1], lineno, 'Wrong line number')
 | |
|         if s > 2:
 | |
|             self.check_equal(self.expect[2], self.frame.f_code.co_name,
 | |
|                                                 'Wrong function name')
 | |
| 
 | |
|     def check_expect_max_size(self, size):
 | |
|         if len(self.expect) > size:
 | |
|             raise BdbSyntaxError('Invalid size of the %s expect tuple: %s' %
 | |
|                                  (self.event, self.expect))
 | |
| 
 | |
|     def lno_abs2rel(self):
 | |
|         fname = self.canonic(self.frame.f_code.co_filename)
 | |
|         lineno = self.frame.f_lineno
 | |
|         return ((lineno - self.frame.f_code.co_firstlineno + 1)
 | |
|             if fname == self.canonic(__file__) else lineno)
 | |
| 
 | |
|     def lno_rel2abs(self, fname, lineno):
 | |
|         return (self.frame.f_code.co_firstlineno + lineno - 1
 | |
|             if (lineno and self.canonic(fname) == self.canonic(__file__))
 | |
|             else lineno)
 | |
| 
 | |
|     def get_state(self):
 | |
|         lineno = self.lno_abs2rel()
 | |
|         co_name = self.frame.f_code.co_name
 | |
|         state = "('%s', %d, '%s'" % (self.event, lineno, co_name)
 | |
|         if self.breakpoint_hits:
 | |
|             bps = '{'
 | |
|             for n in self.breakpoint_hits[0]:
 | |
|                 if bps != '{':
 | |
|                     bps += ', '
 | |
|                 bps += '%s: %s' % (n, self.get_bpbynumber(n).hits)
 | |
|             bps += '}'
 | |
|             bps = '(' + bps + ', ' + str(self.breakpoint_hits[1]) + ')'
 | |
|             state += ', ' + bps
 | |
|         elif self.event == 'exception':
 | |
|             state += ', ' + self.exc_info[0].__name__
 | |
|         state += '), '
 | |
|         return state.ljust(32) + str(self.set_tuple) + ','
 | |
| 
 | |
|     def print_state(self, header=None):
 | |
|         if header is not None and self.expect_set_no == 1:
 | |
|             print()
 | |
|             print(header)
 | |
|         print('%d: %s' % (self.expect_set_no, self.get_state()))
 | |
| 
 | |
|     def raise_not_expected(self, msg):
 | |
|         msg += '\n'
 | |
|         msg += '  Expected: %s\n' % str(self.expect)
 | |
|         msg += '  Got:      ' + self.get_state()
 | |
|         raise BdbNotExpectedError(msg)
 | |
| 
 | |
|     def next_set_method(self):
 | |
|         set_type = self.set_tuple[0]
 | |
|         args = self.set_tuple[1] if len(self.set_tuple) == 2 else None
 | |
|         set_method = getattr(self, 'set_' + set_type)
 | |
| 
 | |
|         # The following set methods give back control to the tracer.
 | |
|         if set_type in ('step', 'stepinstr', 'continue', 'quit'):
 | |
|             set_method()
 | |
|             return
 | |
|         elif set_type in ('next', 'return'):
 | |
|             set_method(self.frame)
 | |
|             return
 | |
|         elif set_type == 'until':
 | |
|             lineno = None
 | |
|             if args:
 | |
|                 lineno = self.lno_rel2abs(self.frame.f_code.co_filename,
 | |
|                                           args[0])
 | |
|             set_method(self.frame, lineno)
 | |
|             return
 | |
| 
 | |
|         # The following set methods do not give back control to the tracer and
 | |
|         # next_set_method() is called recursively.
 | |
|         if (args and set_type in ('break', 'clear', 'ignore', 'enable',
 | |
|                                     'disable')) or set_type in ('up', 'down'):
 | |
|             if set_type in ('break', 'clear'):
 | |
|                 fname, lineno, *remain = args
 | |
|                 lineno = self.lno_rel2abs(fname, lineno)
 | |
|                 args = [fname, lineno]
 | |
|                 args.extend(remain)
 | |
|                 set_method(*args)
 | |
|             elif set_type in ('ignore', 'enable', 'disable'):
 | |
|                 set_method(*args)
 | |
|             elif set_type in ('up', 'down'):
 | |
|                 set_method()
 | |
| 
 | |
|             # Process the next expect_set item.
 | |
|             # It is not expected that a test may reach the recursion limit.
 | |
|             self.event= None
 | |
|             self.pop_next()
 | |
|             if self.dry_run:
 | |
|                 self.print_state()
 | |
|             else:
 | |
|                 if self.expect:
 | |
|                     self.check_lno_name()
 | |
|                 self.check_expect_max_size(3)
 | |
|             self.next_set_method()
 | |
|         else:
 | |
|             raise BdbSyntaxError('"%s" is an invalid set_tuple' %
 | |
|                                  self.set_tuple)
 | |
| 
 | |
| class TracerRun():
 | |
|     """Provide a context for running a Tracer instance with a test case."""
 | |
| 
 | |
|     def __init__(self, test_case, skip=None):
 | |
|         self.test_case = test_case
 | |
|         self.dry_run = test_case.dry_run
 | |
|         self.tracer = Tracer(test_case.expect_set, skip=skip,
 | |
|                              dry_run=self.dry_run, test_case=test_case.id())
 | |
|         self._original_tracer = None
 | |
| 
 | |
|     def __enter__(self):
 | |
|         # test_pdb does not reset Breakpoint class attributes on exit :-(
 | |
|         reset_Breakpoint()
 | |
|         self._original_tracer = sys.gettrace()
 | |
|         return self.tracer
 | |
| 
 | |
|     def __exit__(self, type_=None, value=None, traceback=None):
 | |
|         reset_Breakpoint()
 | |
|         sys.settrace(self._original_tracer)
 | |
| 
 | |
|         not_empty = ''
 | |
|         if self.tracer.set_list:
 | |
|             not_empty += 'All paired tuples have not been processed, '
 | |
|             not_empty += ('the last one was number %d\n' %
 | |
|                           self.tracer.expect_set_no)
 | |
|             not_empty += repr(self.tracer.set_list)
 | |
| 
 | |
|         # Make a BdbNotExpectedError a unittest failure.
 | |
|         if type_ is not None and issubclass(BdbNotExpectedError, type_):
 | |
|             if isinstance(value, BaseException) and value.args:
 | |
|                 err_msg = value.args[0]
 | |
|                 if not_empty:
 | |
|                     err_msg += '\n' + not_empty
 | |
|                 if self.dry_run:
 | |
|                     print(err_msg)
 | |
|                     return True
 | |
|                 else:
 | |
|                     self.test_case.fail(err_msg)
 | |
|             else:
 | |
|                 assert False, 'BdbNotExpectedError with empty args'
 | |
| 
 | |
|         if not_empty:
 | |
|             if self.dry_run:
 | |
|                 print(not_empty)
 | |
|             else:
 | |
|                 self.test_case.fail(not_empty)
 | |
| 
 | |
| def run_test(modules, set_list, skip=None):
 | |
|     """Run a test and print the dry-run results.
 | |
| 
 | |
|     'modules':  A dictionary mapping module names to their source code as a
 | |
|                 string. The dictionary MUST include one module named
 | |
|                 'test_module' with a main() function.
 | |
|     'set_list': A list of set_type tuples to be run on the module.
 | |
| 
 | |
|     For example, running the following script outputs the following results:
 | |
| 
 | |
|     *****************************   SCRIPT   ********************************
 | |
| 
 | |
|     from test.test_bdb import run_test, break_in_func
 | |
| 
 | |
|     code = '''
 | |
|         def func():
 | |
|             lno = 3
 | |
| 
 | |
|         def main():
 | |
|             func()
 | |
|             lno = 7
 | |
|     '''
 | |
| 
 | |
|     set_list = [
 | |
|                 break_in_func('func', 'test_module.py'),
 | |
|                 ('continue', ),
 | |
|                 ('step', ),
 | |
|                 ('step', ),
 | |
|                 ('step', ),
 | |
|                 ('quit', ),
 | |
|             ]
 | |
| 
 | |
|     modules = { 'test_module': code }
 | |
|     run_test(modules, set_list)
 | |
| 
 | |
|     ****************************   results   ********************************
 | |
| 
 | |
|     1: ('line', 2, 'tfunc_import'),    ('next',),
 | |
|     2: ('line', 3, 'tfunc_import'),    ('step',),
 | |
|     3: ('call', 5, 'main'),            ('break', ('test_module.py', None, False, None, 'func')),
 | |
|     4: ('None', 5, 'main'),            ('continue',),
 | |
|     5: ('line', 3, 'func', ({1: 1}, [])), ('step',),
 | |
|       BpNum Temp Enb Hits Ignore Where
 | |
|       1     no   yes 1    0      at test_module.py:2
 | |
|     6: ('return', 3, 'func'),          ('step',),
 | |
|     7: ('line', 7, 'main'),            ('step',),
 | |
|     8: ('return', 7, 'main'),          ('quit',),
 | |
| 
 | |
|     *************************************************************************
 | |
| 
 | |
|     """
 | |
|     def gen(a, b):
 | |
|         try:
 | |
|             while 1:
 | |
|                 x = next(a)
 | |
|                 y = next(b)
 | |
|                 yield x
 | |
|                 yield y
 | |
|         except StopIteration:
 | |
|             return
 | |
| 
 | |
|     # Step over the import statement in tfunc_import using 'next' and step
 | |
|     # into main() in test_module.
 | |
|     sl = [('next', ), ('step', )]
 | |
|     sl.extend(set_list)
 | |
| 
 | |
|     test = BaseTestCase()
 | |
|     test.dry_run = True
 | |
|     test.id = lambda : None
 | |
|     test.expect_set = list(gen(repeat(()), iter(sl)))
 | |
|     with create_modules(modules):
 | |
|         with TracerRun(test, skip=skip) as tracer:
 | |
|             tracer.runcall(tfunc_import)
 | |
| 
 | |
| @contextmanager
 | |
| def create_modules(modules):
 | |
|     with os_helper.temp_cwd():
 | |
|         sys.path.append(os.getcwd())
 | |
|         try:
 | |
|             for m in modules:
 | |
|                 fname = m + '.py'
 | |
|                 with open(fname, 'w', encoding="utf-8") as f:
 | |
|                     f.write(textwrap.dedent(modules[m]))
 | |
|                 linecache.checkcache(fname)
 | |
|             importlib.invalidate_caches()
 | |
|             yield
 | |
|         finally:
 | |
|             for m in modules:
 | |
|                 import_helper.forget(m)
 | |
|             sys.path.pop()
 | |
| 
 | |
| def break_in_func(funcname, fname=__file__, temporary=False, cond=None):
 | |
|     return 'break', (fname, None, temporary, cond, funcname)
 | |
| 
 | |
| TEST_MODULE = 'test_module_for_bdb'
 | |
| TEST_MODULE_FNAME = TEST_MODULE + '.py'
 | |
| def tfunc_import():
 | |
|     import test_module_for_bdb
 | |
|     test_module_for_bdb.main()
 | |
| 
 | |
| def tfunc_main():
 | |
|     lno = 2
 | |
|     tfunc_first()
 | |
|     tfunc_second()
 | |
|     lno = 5
 | |
|     lno = 6
 | |
|     lno = 7
 | |
| 
 | |
| def tfunc_first():
 | |
|     lno = 2
 | |
|     lno = 3
 | |
|     lno = 4
 | |
| 
 | |
| def tfunc_second():
 | |
|     lno = 2
 | |
| 
 | |
| class BaseTestCase(unittest.TestCase):
 | |
|     """Base class for all tests."""
 | |
| 
 | |
|     dry_run = dry_run
 | |
| 
 | |
|     def fail(self, msg=None):
 | |
|         # Override fail() to use 'raise from None' to avoid repetition of the
 | |
|         # error message and traceback.
 | |
|         raise self.failureException(msg) from None
 | |
| 
 | |
| class StateTestCase(BaseTestCase):
 | |
|     """Test the step, next, return, until and quit 'set_' methods."""
 | |
| 
 | |
|     def test_step(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('step', ),
 | |
|             ('line', 2, 'tfunc_first'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_step_next_on_last_statement(self):
 | |
|         for set_type in ('step', 'next'):
 | |
|             with self.subTest(set_type=set_type):
 | |
|                 self.expect_set = [
 | |
|                     ('line', 2, 'tfunc_main'),               ('step', ),
 | |
|                     ('line', 3, 'tfunc_main'),               ('step', ),
 | |
|                     ('call', 1, 'tfunc_first'),              ('break', (__file__, 3)),
 | |
|                     ('None', 1, 'tfunc_first'),              ('continue', ),
 | |
|                     ('line', 3, 'tfunc_first', ({1:1}, [])), (set_type, ),
 | |
|                     ('line', 4, 'tfunc_first'),              ('quit', ),
 | |
|                 ]
 | |
|                 with TracerRun(self) as tracer:
 | |
|                     tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_stepinstr(self):
 | |
|         self.expect_set = [
 | |
|             ('line',   2, 'tfunc_main'),  ('stepinstr', ),
 | |
|             ('opcode', 2, 'tfunc_main'),  ('next', ),
 | |
|             ('line',   3, 'tfunc_main'),  ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_next(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),   ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),   ('next', ),
 | |
|             ('line', 4, 'tfunc_main'),   ('step', ),
 | |
|             ('call', 1, 'tfunc_second'), ('step', ),
 | |
|             ('line', 2, 'tfunc_second'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_next_over_import(self):
 | |
|         code = """
 | |
|             def main():
 | |
|                 lno = 3
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'), ('next', ),
 | |
|                 ('line', 3, 'tfunc_import'), ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_next_on_plain_statement(self):
 | |
|         # Check that set_next() is equivalent to set_step() on a plain
 | |
|         # statement.
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('next', ),
 | |
|             ('line', 2, 'tfunc_first'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_next_in_caller_frame(self):
 | |
|         # Check that set_next() in the caller frame causes the tracer
 | |
|         # to stop next in the caller frame.
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('up', ),
 | |
|             ('None', 3, 'tfunc_main'),  ('next', ),
 | |
|             ('line', 4, 'tfunc_main'),  ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_return(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),    ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),    ('step', ),
 | |
|             ('call', 1, 'tfunc_first'),   ('step', ),
 | |
|             ('line', 2, 'tfunc_first'),   ('return', ),
 | |
|             ('return', 4, 'tfunc_first'), ('step', ),
 | |
|             ('line', 4, 'tfunc_main'),    ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_return_in_caller_frame(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),   ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),   ('step', ),
 | |
|             ('call', 1, 'tfunc_first'),  ('up', ),
 | |
|             ('None', 3, 'tfunc_main'),   ('return', ),
 | |
|             ('return', 7, 'tfunc_main'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_until(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('step', ),
 | |
|             ('line', 2, 'tfunc_first'), ('until', (4, )),
 | |
|             ('line', 4, 'tfunc_first'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_until_with_too_large_count(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),               break_in_func('tfunc_first'),
 | |
|             ('None', 2, 'tfunc_main'),               ('continue', ),
 | |
|             ('line', 2, 'tfunc_first', ({1:1}, [])), ('until', (9999, )),
 | |
|             ('return', 4, 'tfunc_first'),            ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     def test_until_in_caller_frame(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('up', ),
 | |
|             ('None', 3, 'tfunc_main'),  ('until', (6, )),
 | |
|             ('line', 6, 'tfunc_main'),  ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
|     @patch_list(sys.meta_path)
 | |
|     def test_skip(self):
 | |
|         # Check that tracing is skipped over the import statement in
 | |
|         # 'tfunc_import()'.
 | |
| 
 | |
|         # Remove all but the standard importers.
 | |
|         sys.meta_path[:] = (
 | |
|             item
 | |
|             for item in sys.meta_path
 | |
|             if item.__module__.startswith('_frozen_importlib')
 | |
|         )
 | |
| 
 | |
|         code = """
 | |
|             def main():
 | |
|                 lno = 3
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'), ('step', ),
 | |
|                 ('line', 3, 'tfunc_import'), ('quit', ),
 | |
|             ]
 | |
|             skip = ('importlib*', 'zipimport', 'encodings.*', TEST_MODULE)
 | |
|             with TracerRun(self, skip=skip) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_skip_with_no_name_module(self):
 | |
|         # some frames have `globals` with no `__name__`
 | |
|         # for instance the second frame in this traceback
 | |
|         # exec(compile('raise ValueError()', '', 'exec'), {})
 | |
|         bdb = Bdb(skip=['anything*'])
 | |
|         self.assertIs(bdb.is_skipped_module(None), False)
 | |
| 
 | |
|     def test_down(self):
 | |
|         # Check that set_down() raises BdbError at the newest frame.
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'), ('down', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             self.assertRaises(BdbError, tracer.runcall, tfunc_main)
 | |
| 
 | |
|     def test_up(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_main'),  ('step', ),
 | |
|             ('line', 3, 'tfunc_main'),  ('step', ),
 | |
|             ('call', 1, 'tfunc_first'), ('up', ),
 | |
|             ('None', 3, 'tfunc_main'),  ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.runcall(tfunc_main)
 | |
| 
 | |
| class BreakpointTestCase(BaseTestCase):
 | |
|     """Test the breakpoint set method."""
 | |
| 
 | |
|     def test_bp_on_non_existent_module(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_import'), ('break', ('/non/existent/module.py', 1))
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             self.assertRaises(BdbError, tracer.runcall, tfunc_import)
 | |
| 
 | |
|     def test_bp_after_last_statement(self):
 | |
|         code = """
 | |
|             def main():
 | |
|                 lno = 3
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'), ('break', (TEST_MODULE_FNAME, 4))
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 self.assertRaises(BdbError, tracer.runcall, tfunc_import)
 | |
| 
 | |
|     def test_temporary_bp(self):
 | |
|         code = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(2):
 | |
|                     func()
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME, True),
 | |
|                 ('None', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME, True),
 | |
|                 ('None', 2, 'tfunc_import'),       ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:1}, [1])), ('continue', ),
 | |
|                 ('line', 3, 'func', ({2:1}, [2])), ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_disabled_temporary_bp(self):
 | |
|         code = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(3):
 | |
|                     func()
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME, True),
 | |
|                 ('None', 2, 'tfunc_import'),       ('disable', (2, )),
 | |
|                 ('None', 2, 'tfunc_import'),       ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:1}, [])),  ('enable', (2, )),
 | |
|                 ('None', 3, 'func'),               ('disable', (1, )),
 | |
|                 ('None', 3, 'func'),               ('continue', ),
 | |
|                 ('line', 3, 'func', ({2:1}, [2])), ('enable', (1, )),
 | |
|                 ('None', 3, 'func'),               ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:2}, [])),  ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_bp_condition(self):
 | |
|         code = """
 | |
|             def func(a):
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(3):
 | |
|                     func(i)
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME, False, 'a == 2'),
 | |
|                 ('None', 2, 'tfunc_import'),       ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:3}, [])),  ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_bp_exception_on_condition_evaluation(self):
 | |
|         code = """
 | |
|             def func(a):
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 func(0)
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME, False, '1 / 0'),
 | |
|                 ('None', 2, 'tfunc_import'),       ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:1}, [])),  ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_bp_ignore_count(self):
 | |
|         code = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(2):
 | |
|                     func()
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),      ('ignore', (1, )),
 | |
|                 ('None', 2, 'tfunc_import'),      ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:2}, [])), ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_ignore_count_on_disabled_bp(self):
 | |
|         code = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(3):
 | |
|                     func()
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),      ('ignore', (1, )),
 | |
|                 ('None', 2, 'tfunc_import'),      ('disable', (1, )),
 | |
|                 ('None', 2, 'tfunc_import'),      ('continue', ),
 | |
|                 ('line', 3, 'func', ({2:1}, [])), ('enable', (1, )),
 | |
|                 ('None', 3, 'func'),              ('continue', ),
 | |
|                 ('line', 3, 'func', ({2:2}, [])), ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:2}, [])), ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_clear_two_bp_on_same_line(self):
 | |
|         code = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
|                 lno = 4
 | |
| 
 | |
|             def main():
 | |
|                 for i in range(3):
 | |
|                     func()
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 3)),
 | |
|                 ('None', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 3)),
 | |
|                 ('None', 2, 'tfunc_import'),      ('break', (TEST_MODULE_FNAME, 4)),
 | |
|                 ('None', 2, 'tfunc_import'),      ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:1}, [])), ('continue', ),
 | |
|                 ('line', 4, 'func', ({3:1}, [])), ('clear', (TEST_MODULE_FNAME, 3)),
 | |
|                 ('None', 4, 'func'),              ('continue', ),
 | |
|                 ('line', 4, 'func', ({3:2}, [])), ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_clear_at_no_bp(self):
 | |
|         self.expect_set = [
 | |
|             ('line', 2, 'tfunc_import'), ('clear', (__file__, 1))
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             self.assertRaises(BdbError, tracer.runcall, tfunc_import)
 | |
| 
 | |
|     def test_load_bps_from_previous_Bdb_instance(self):
 | |
|         reset_Breakpoint()
 | |
|         db1 = Bdb()
 | |
|         fname = db1.canonic(__file__)
 | |
|         db1.set_break(__file__, 1)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
| 
 | |
|         db2 = Bdb()
 | |
|         db2.set_break(__file__, 2)
 | |
|         db2.set_break(__file__, 3)
 | |
|         db2.set_break(__file__, 4)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [1, 2, 3, 4]})
 | |
|         db2.clear_break(__file__, 1)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
 | |
| 
 | |
|         db3 = Bdb()
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [2, 3, 4]})
 | |
|         self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
 | |
|         db2.clear_break(__file__, 2)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
 | |
|         self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
 | |
| 
 | |
|         db4 = Bdb()
 | |
|         db4.set_break(__file__, 5)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
 | |
|         self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
 | |
|         self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
 | |
|         reset_Breakpoint()
 | |
| 
 | |
|         db5 = Bdb()
 | |
|         db5.set_break(__file__, 6)
 | |
|         self.assertEqual(db1.get_all_breaks(), {fname: [1]})
 | |
|         self.assertEqual(db2.get_all_breaks(), {fname: [3, 4]})
 | |
|         self.assertEqual(db3.get_all_breaks(), {fname: [2, 3, 4]})
 | |
|         self.assertEqual(db4.get_all_breaks(), {fname: [3, 4, 5]})
 | |
|         self.assertEqual(db5.get_all_breaks(), {fname: [6]})
 | |
| 
 | |
| 
 | |
| class RunTestCase(BaseTestCase):
 | |
|     """Test run, runeval and set_trace."""
 | |
| 
 | |
|     def test_run_step(self):
 | |
|         # Check that the bdb 'run' method stops at the first line event.
 | |
|         code = """
 | |
|             lno = 2
 | |
|         """
 | |
|         self.expect_set = [
 | |
|             ('line', 2, '<module>'),   ('step', ),
 | |
|             ('return', 2, '<module>'), ('quit', ),
 | |
|         ]
 | |
|         with TracerRun(self) as tracer:
 | |
|             tracer.run(compile(textwrap.dedent(code), '<string>', 'exec'))
 | |
| 
 | |
|     def test_runeval_step(self):
 | |
|         # Test bdb 'runeval'.
 | |
|         code = """
 | |
|             def main():
 | |
|                 lno = 3
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 1, '<module>'),   ('step', ),
 | |
|                 ('call', 2, 'main'),       ('step', ),
 | |
|                 ('line', 3, 'main'),       ('step', ),
 | |
|                 ('return', 3, 'main'),     ('step', ),
 | |
|                 ('return', 1, '<module>'), ('quit', ),
 | |
|             ]
 | |
|             import test_module_for_bdb
 | |
|             ns = {'test_module_for_bdb': test_module_for_bdb}
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runeval('test_module_for_bdb.main()', ns, ns)
 | |
| 
 | |
| class IssuesTestCase(BaseTestCase):
 | |
|     """Test fixed bdb issues."""
 | |
| 
 | |
|     def test_step_at_return_with_no_trace_in_caller(self):
 | |
|         # Issue #13183.
 | |
|         # Check that the tracer does step into the caller frame when the
 | |
|         # trace function is not set in that frame.
 | |
|         code_1 = """
 | |
|             from test_module_for_bdb_2 import func
 | |
|             def main():
 | |
|                 func()
 | |
|                 lno = 5
 | |
|         """
 | |
|         code_2 = """
 | |
|             def func():
 | |
|                 lno = 3
 | |
|         """
 | |
|         modules = {
 | |
|             TEST_MODULE: code_1,
 | |
|             'test_module_for_bdb_2': code_2,
 | |
|         }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('func', 'test_module_for_bdb_2.py'),
 | |
|                 ('None', 2, 'tfunc_import'),      ('continue', ),
 | |
|                 ('line', 3, 'func', ({1:1}, [])), ('step', ),
 | |
|                 ('return', 3, 'func'),            ('step', ),
 | |
|                 ('line', 5, 'main'),              ('quit', ),
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_next_until_return_in_generator(self):
 | |
|         # Issue #16596.
 | |
|         # Check that set_next(), set_until() and set_return() do not treat the
 | |
|         # `yield` and `yield from` statements as if they were returns and stop
 | |
|         # instead in the current frame.
 | |
|         code = """
 | |
|             def test_gen():
 | |
|                 yield 0
 | |
|                 lno = 4
 | |
|                 return 123
 | |
| 
 | |
|             def main():
 | |
|                 it = test_gen()
 | |
|                 next(it)
 | |
|                 next(it)
 | |
|                 lno = 11
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         for set_type in ('next', 'until', 'return'):
 | |
|             with self.subTest(set_type=set_type):
 | |
|                 with create_modules(modules):
 | |
|                     self.expect_set = [
 | |
|                         ('line', 2, 'tfunc_import'),
 | |
|                             break_in_func('test_gen', TEST_MODULE_FNAME),
 | |
|                         ('None', 2, 'tfunc_import'),          ('continue', ),
 | |
|                         ('line', 3, 'test_gen', ({1:1}, [])), (set_type, ),
 | |
|                     ]
 | |
| 
 | |
|                     if set_type == 'return':
 | |
|                         self.expect_set.extend(
 | |
|                             [('exception', 10, 'main', StopIteration), ('step',),
 | |
|                              ('return', 10, 'main'),                   ('quit', ),
 | |
|                             ]
 | |
|                         )
 | |
|                     else:
 | |
|                         self.expect_set.extend(
 | |
|                             [('line', 4, 'test_gen'), ('quit', ),]
 | |
|                         )
 | |
|                     with TracerRun(self) as tracer:
 | |
|                         tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_next_command_in_generator_for_loop(self):
 | |
|         # Issue #16596.
 | |
|         code = """
 | |
|             def test_gen():
 | |
|                 yield 0
 | |
|                 lno = 4
 | |
|                 yield 1
 | |
|                 return 123
 | |
| 
 | |
|             def main():
 | |
|                 for i in test_gen():
 | |
|                     lno = 10
 | |
|                 lno = 11
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('test_gen', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),             ('continue', ),
 | |
|                 ('line', 3, 'test_gen', ({1:1}, [])),    ('next', ),
 | |
|                 ('line', 4, 'test_gen'),                 ('next', ),
 | |
|                 ('line', 5, 'test_gen'),                 ('next', ),
 | |
|                 ('line', 6, 'test_gen'),                 ('next', ),
 | |
|                 ('exception', 9, 'main', StopIteration), ('step', ),
 | |
|                 ('line', 11, 'main'),                    ('quit', ),
 | |
| 
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_next_command_in_generator_with_subiterator(self):
 | |
|         # Issue #16596.
 | |
|         code = """
 | |
|             def test_subgen():
 | |
|                 yield 0
 | |
|                 return 123
 | |
| 
 | |
|             def test_gen():
 | |
|                 x = yield from test_subgen()
 | |
|                 return 456
 | |
| 
 | |
|             def main():
 | |
|                 for i in test_gen():
 | |
|                     lno = 12
 | |
|                 lno = 13
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('test_gen', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),              ('continue', ),
 | |
|                 ('line', 7, 'test_gen', ({1:1}, [])),     ('next', ),
 | |
|                 ('line', 8, 'test_gen'),                  ('next', ),
 | |
|                 ('exception', 11, 'main', StopIteration), ('step', ),
 | |
|                 ('line', 13, 'main'),                     ('quit', ),
 | |
| 
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
|     def test_return_command_in_generator_with_subiterator(self):
 | |
|         # Issue #16596.
 | |
|         code = """
 | |
|             def test_subgen():
 | |
|                 yield 0
 | |
|                 return 123
 | |
| 
 | |
|             def test_gen():
 | |
|                 x = yield from test_subgen()
 | |
|                 return 456
 | |
| 
 | |
|             def main():
 | |
|                 for i in test_gen():
 | |
|                     lno = 12
 | |
|                 lno = 13
 | |
|         """
 | |
|         modules = { TEST_MODULE: code }
 | |
|         with create_modules(modules):
 | |
|             self.expect_set = [
 | |
|                 ('line', 2, 'tfunc_import'),
 | |
|                     break_in_func('test_subgen', TEST_MODULE_FNAME),
 | |
|                 ('None', 2, 'tfunc_import'),                  ('continue', ),
 | |
|                 ('line', 3, 'test_subgen', ({1:1}, [])),      ('return', ),
 | |
|                 ('exception', 7, 'test_gen', StopIteration),  ('return', ),
 | |
|                 ('exception', 11, 'main', StopIteration),     ('step', ),
 | |
|                 ('line', 13, 'main'),                         ('quit', ),
 | |
| 
 | |
|             ]
 | |
|             with TracerRun(self) as tracer:
 | |
|                 tracer.runcall(tfunc_import)
 | |
| 
 | |
| 
 | |
| class TestRegressions(unittest.TestCase):
 | |
|     def test_format_stack_entry_no_lineno(self):
 | |
|         # See gh-101517
 | |
|         self.assertIn('Warning: lineno is None',
 | |
|                       Bdb().format_stack_entry((sys._getframe(), None)))
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     unittest.main()
 | 
