@@ -234,16 +234,29 @@ def generator_example():
234234
235235
236236class Tracer :
237- def __init__ (self ):
237+ def __init__ (self , trace_line_events = None , trace_opcode_events = None ):
238+ self .trace_line_events = trace_line_events
239+ self .trace_opcode_events = trace_opcode_events
238240 self .events = []
241+
242+ def _reconfigure_frame (self , frame ):
243+ if self .trace_line_events is not None :
244+ frame .f_trace_lines = self .trace_line_events
245+ if self .trace_opcode_events is not None :
246+ frame .f_trace_opcodes = self .trace_opcode_events
247+
239248 def trace (self , frame , event , arg ):
249+ self ._reconfigure_frame (frame )
240250 self .events .append ((frame .f_lineno , event ))
241251 return self .trace
252+
242253 def traceWithGenexp (self , frame , event , arg ):
254+ self ._reconfigure_frame (frame )
243255 (o for o in [1 ])
244256 self .events .append ((frame .f_lineno , event ))
245257 return self .trace
246258
259+
247260class TraceTestCase (unittest .TestCase ):
248261
249262 # Disable gc collection when tracing, otherwise the
@@ -257,6 +270,11 @@ def tearDown(self):
257270 if self .using_gc :
258271 gc .enable ()
259272
273+ @staticmethod
274+ def make_tracer ():
275+ """Helper to allow test subclasses to configure tracers differently"""
276+ return Tracer ()
277+
260278 def compare_events (self , line_offset , events , expected_events ):
261279 events = [(l - line_offset , e ) for (l , e ) in events ]
262280 if events != expected_events :
@@ -266,7 +284,7 @@ def compare_events(self, line_offset, events, expected_events):
266284 [str (x ) for x in events ])))
267285
268286 def run_and_compare (self , func , events ):
269- tracer = Tracer ()
287+ tracer = self . make_tracer ()
270288 sys .settrace (tracer .trace )
271289 func ()
272290 sys .settrace (None )
@@ -277,7 +295,7 @@ def run_test(self, func):
277295 self .run_and_compare (func , func .events )
278296
279297 def run_test2 (self , func ):
280- tracer = Tracer ()
298+ tracer = self . make_tracer ()
281299 func (tracer .trace )
282300 sys .settrace (None )
283301 self .compare_events (func .__code__ .co_firstlineno ,
@@ -329,7 +347,7 @@ def test_13_genexp(self):
329347 # and if the traced function contains another generator
330348 # that is not completely exhausted, the trace stopped.
331349 # Worse: the 'finally' clause was not invoked.
332- tracer = Tracer ()
350+ tracer = self . make_tracer ()
333351 sys .settrace (tracer .traceWithGenexp )
334352 generator_example ()
335353 sys .settrace (None )
@@ -398,6 +416,34 @@ def func():
398416 (1 , 'line' )])
399417
400418
419+ class SkipLineEventsTraceTestCase (TraceTestCase ):
420+ """Repeat the trace tests, but with per-line events skipped"""
421+
422+ def compare_events (self , line_offset , events , expected_events ):
423+ skip_line_events = [e for e in expected_events if e [1 ] != 'line' ]
424+ super ().compare_events (line_offset , events , skip_line_events )
425+
426+ @staticmethod
427+ def make_tracer ():
428+ return Tracer (trace_line_events = False )
429+
430+
431+ @support .cpython_only
432+ class TraceOpcodesTestCase (TraceTestCase ):
433+ """Repeat the trace tests, but with per-opcodes events enabled"""
434+
435+ def compare_events (self , line_offset , events , expected_events ):
436+ skip_opcode_events = [e for e in events if e [1 ] != 'opcode' ]
437+ if len (events ) > 1 :
438+ self .assertLess (len (skip_opcode_events ), len (events ),
439+ msg = "No 'opcode' events received by the tracer" )
440+ super ().compare_events (line_offset , skip_opcode_events , expected_events )
441+
442+ @staticmethod
443+ def make_tracer ():
444+ return Tracer (trace_opcode_events = True )
445+
446+
401447class RaisingTraceFuncTestCase (unittest .TestCase ):
402448 def setUp (self ):
403449 self .addCleanup (sys .settrace , sys .gettrace ())
@@ -846,6 +892,8 @@ class fake_function:
846892def test_main ():
847893 support .run_unittest (
848894 TraceTestCase ,
895+ SkipLineEventsTraceTestCase ,
896+ TraceOpcodesTestCase ,
849897 RaisingTraceFuncTestCase ,
850898 JumpTestCase
851899 )
0 commit comments