diff options
author | Nick Coghlan <ncoghlan@gmail.com> | 2017-09-08 00:14:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-08 00:14:16 (GMT) |
commit | 5a8516701f5140c8c989c40e261a4f4e20e8af86 (patch) | |
tree | ce7c8c4d443132b27203a834904469458191a154 /Lib | |
parent | 2eb0cb4787d02d995a9bb6dc075983792c12835c (diff) | |
download | cpython-5a8516701f5140c8c989c40e261a4f4e20e8af86.zip cpython-5a8516701f5140c8c989c40e261a4f4e20e8af86.tar.gz cpython-5a8516701f5140c8c989c40e261a4f4e20e8af86.tar.bz2 |
bpo-31344: Per-frame control of trace events (GH-3417)
f_trace_lines: enable/disable line trace events
f_trace_opcodes: enable/disable opcode trace events
These are intended primarily for testing of the interpreter
itself, as they make it much easier to emulate signals
arriving at unfortunate times.
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/test/test_sys.py | 2 | ||||
-rw-r--r-- | Lib/test/test_sys_settrace.py | 56 |
2 files changed, 53 insertions, 5 deletions
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 04550e5..fd45abe 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -971,7 +971,7 @@ class SizeofTest(unittest.TestCase): nfrees = len(x.f_code.co_freevars) extras = x.f_code.co_stacksize + x.f_code.co_nlocals +\ ncells + nfrees - 1 - check(x, vsize('12P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) + check(x, vsize('8P2c4P3ic' + CO_MAXBLOCKS*'3i' + 'P' + extras*'P')) # function def func(): pass check(func, size('12P')) diff --git a/Lib/test/test_sys_settrace.py b/Lib/test/test_sys_settrace.py index 25c5835..ed9e6d4 100644 --- a/Lib/test/test_sys_settrace.py +++ b/Lib/test/test_sys_settrace.py @@ -234,16 +234,29 @@ generator_example.events = ([(0, 'call'), class Tracer: - def __init__(self): + def __init__(self, trace_line_events=None, trace_opcode_events=None): + self.trace_line_events = trace_line_events + self.trace_opcode_events = trace_opcode_events self.events = [] + + def _reconfigure_frame(self, frame): + if self.trace_line_events is not None: + frame.f_trace_lines = self.trace_line_events + if self.trace_opcode_events is not None: + frame.f_trace_opcodes = self.trace_opcode_events + def trace(self, frame, event, arg): + self._reconfigure_frame(frame) self.events.append((frame.f_lineno, event)) return self.trace + def traceWithGenexp(self, frame, event, arg): + self._reconfigure_frame(frame) (o for o in [1]) self.events.append((frame.f_lineno, event)) return self.trace + class TraceTestCase(unittest.TestCase): # Disable gc collection when tracing, otherwise the @@ -257,6 +270,11 @@ class TraceTestCase(unittest.TestCase): if self.using_gc: gc.enable() + @staticmethod + def make_tracer(): + """Helper to allow test subclasses to configure tracers differently""" + return Tracer() + def compare_events(self, line_offset, events, expected_events): events = [(l - line_offset, e) for (l, e) in events] if events != expected_events: @@ -266,7 +284,7 @@ class TraceTestCase(unittest.TestCase): [str(x) for x in events]))) def run_and_compare(self, func, events): - tracer = Tracer() + tracer = self.make_tracer() sys.settrace(tracer.trace) func() sys.settrace(None) @@ -277,7 +295,7 @@ class TraceTestCase(unittest.TestCase): self.run_and_compare(func, func.events) def run_test2(self, func): - tracer = Tracer() + tracer = self.make_tracer() func(tracer.trace) sys.settrace(None) self.compare_events(func.__code__.co_firstlineno, @@ -329,7 +347,7 @@ class TraceTestCase(unittest.TestCase): # and if the traced function contains another generator # that is not completely exhausted, the trace stopped. # Worse: the 'finally' clause was not invoked. - tracer = Tracer() + tracer = self.make_tracer() sys.settrace(tracer.traceWithGenexp) generator_example() sys.settrace(None) @@ -398,6 +416,34 @@ class TraceTestCase(unittest.TestCase): (1, 'line')]) +class SkipLineEventsTraceTestCase(TraceTestCase): + """Repeat the trace tests, but with per-line events skipped""" + + def compare_events(self, line_offset, events, expected_events): + skip_line_events = [e for e in expected_events if e[1] != 'line'] + super().compare_events(line_offset, events, skip_line_events) + + @staticmethod + def make_tracer(): + return Tracer(trace_line_events=False) + + +@support.cpython_only +class TraceOpcodesTestCase(TraceTestCase): + """Repeat the trace tests, but with per-opcodes events enabled""" + + def compare_events(self, line_offset, events, expected_events): + skip_opcode_events = [e for e in events if e[1] != 'opcode'] + if len(events) > 1: + self.assertLess(len(skip_opcode_events), len(events), + msg="No 'opcode' events received by the tracer") + super().compare_events(line_offset, skip_opcode_events, expected_events) + + @staticmethod + def make_tracer(): + return Tracer(trace_opcode_events=True) + + class RaisingTraceFuncTestCase(unittest.TestCase): def setUp(self): self.addCleanup(sys.settrace, sys.gettrace()) @@ -846,6 +892,8 @@ output.append(4) def test_main(): support.run_unittest( TraceTestCase, + SkipLineEventsTraceTestCase, + TraceOpcodesTestCase, RaisingTraceFuncTestCase, JumpTestCase ) |