summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNick Coghlan <ncoghlan@gmail.com>2017-09-08 00:14:16 (GMT)
committerGitHub <noreply@github.com>2017-09-08 00:14:16 (GMT)
commit5a8516701f5140c8c989c40e261a4f4e20e8af86 (patch)
treece7c8c4d443132b27203a834904469458191a154 /Lib
parent2eb0cb4787d02d995a9bb6dc075983792c12835c (diff)
downloadcpython-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.py2
-rw-r--r--Lib/test/test_sys_settrace.py56
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
)