diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -232,6 +232,20 @@ [(5, 'line'), (6, 'line')] * 10 + [(5, 'line'), (5, 'return')]) +def _settrace_at_return(tracefunc): + sys.settrace(tracefunc) + sys._getframe().f_back.f_trace = tracefunc + return True +def settrace_at_return(tracefunc): + a = 1 + while a: + x = 1 + if _settrace_at_return(tracefunc): break + else: + x = 2 + +settrace_at_return.events = [(4, 'return')] + class Tracer: def __init__(self): @@ -243,6 +257,18 @@ (o for o in [1]) self.events.append((frame.f_lineno, event)) return self.trace + def traceSetNone(self, frame, event, arg): + if event == 'line': + if frame.f_code.co_name == 'set_none': + sys.settrace(None) + while frame: + del frame.f_trace + frame = frame.f_back + return None + elif frame.f_code.co_name == 'set_none_keep_f_trace': + sys.settrace(None) + return None + return self.traceSetNone class TraceTestCase(unittest.TestCase): @@ -297,6 +323,35 @@ finally: sys.settrace(None) + def test_set_none_generator(self): + # Issue17277: set trace None while a generator is suspended. + def gen(): + yield 1 + self.assertEqual(sys._getframe().f_lineno + - gen.__code__.co_firstlineno, 2) + yield 2 + + def set_none(): + pass + + sys.settrace(Tracer().traceSetNone) + it = gen() + next(it) + set_none() + next(it) + + def test_set_none(self): + # Issue17697: set trace None without deleting f_trace of the frames on + # the stack. + def set_none_keep_f_trace(): + x = 1 + self.assertEqual(sys._getframe().f_lineno + - set_none_keep_f_trace.__code__.co_firstlineno, + 2) + + sys.settrace(Tracer().traceSetNone) + set_none_keep_f_trace() + def test_01_basic(self): self.run_test(basic) def test_02_arigo(self): @@ -388,6 +443,11 @@ (257, 'line'), (257, 'return')]) + def test_17_settrace_at_return(self): + # Tracing is set while the next tracing event for a frame on the stack + # is a "return" event. + self.run_test2(settrace_at_return) + class RaisingTraceFuncTestCase(unittest.TestCase): def setUp(self):