@@ -767,6 +767,168 @@ def func3():
767767 ('line' , 'check_events' , 11 ),
768768 ('call' , 'set_events' , 2 )])
769769
770+ class InstructionRecorder :
771+
772+ event_type = E .INSTRUCTION
773+
774+ def __init__ (self , events ):
775+ self .events = events
776+
777+ def __call__ (self , code , offset ):
778+ # Filter out instructions in check_events to lower noise
779+ if code .co_name != "check_events" :
780+ self .events .append (("instruction" , code .co_name , offset ))
781+
782+
783+ LINE_AND_INSTRUCTION_RECORDERS = InstructionRecorder , LineRecorder
784+
785+ class TestLineAndInstructionEvents (CheckEvents ):
786+ maxDiff = None
787+
788+ def test_simple (self ):
789+
790+ def func1 ():
791+ line1 = 1
792+ line2 = 2
793+ line3 = 3
794+
795+ self .check_events (func1 , recorders = LINE_AND_INSTRUCTION_RECORDERS , expected = [
796+ ('line' , 'check_events' , 10 ),
797+ ('instruction' , 'func1' , 1 ),
798+ ('line' , 'func1' , 1 ),
799+ ('instruction' , 'func1' , 2 ),
800+ ('instruction' , 'func1' , 3 ),
801+ ('line' , 'func1' , 2 ),
802+ ('instruction' , 'func1' , 4 ),
803+ ('instruction' , 'func1' , 5 ),
804+ ('line' , 'func1' , 3 ),
805+ ('instruction' , 'func1' , 6 ),
806+ ('instruction' , 'func1' , 7 ),
807+ ('line' , 'check_events' , 11 )])
808+
809+ def test_c_call (self ):
810+
811+ def func2 ():
812+ line1 = 1
813+ [].append (2 )
814+ line3 = 3
815+
816+ self .check_events (func2 , recorders = LINE_AND_INSTRUCTION_RECORDERS , expected = [
817+ ('line' , 'check_events' , 10 ),
818+ ('instruction' , 'func2' , 1 ),
819+ ('line' , 'func2' , 1 ),
820+ ('instruction' , 'func2' , 2 ),
821+ ('instruction' , 'func2' , 3 ),
822+ ('line' , 'func2' , 2 ),
823+ ('instruction' , 'func2' , 4 ),
824+ ('instruction' , 'func2' , 14 ),
825+ ('instruction' , 'func2' , 15 ),
826+ ('instruction' , 'func2' , 20 ),
827+ ('instruction' , 'func2' , 21 ),
828+ ('line' , 'func2' , 3 ),
829+ ('instruction' , 'func2' , 22 ),
830+ ('instruction' , 'func2' , 23 ),
831+ ('line' , 'check_events' , 11 )])
832+
833+ def test_try_except (self ):
834+
835+ def func3 ():
836+ try :
837+ line = 2
838+ raise KeyError
839+ except :
840+ line = 5
841+ line = 6
842+
843+ self .check_events (func3 , recorders = LINE_AND_INSTRUCTION_RECORDERS , expected = [
844+ ('line' , 'check_events' , 10 ),
845+ ('instruction' , 'func3' , 1 ),
846+ ('line' , 'func3' , 1 ),
847+ ('instruction' , 'func3' , 2 ),
848+ ('line' , 'func3' , 2 ),
849+ ('instruction' , 'func3' , 3 ),
850+ ('instruction' , 'func3' , 4 ),
851+ ('line' , 'func3' , 3 ),
852+ ('instruction' , 'func3' , 9 ),
853+ ('instruction' , 'func3' , 10 ),
854+ ('instruction' , 'func3' , 11 ),
855+ ('line' , 'func3' , 4 ),
856+ ('instruction' , 'func3' , 12 ),
857+ ('line' , 'func3' , 5 ),
858+ ('instruction' , 'func3' , 13 ),
859+ ('instruction' , 'func3' , 14 ),
860+ ('instruction' , 'func3' , 15 ),
861+ ('line' , 'func3' , 6 ),
862+ ('instruction' , 'func3' , 16 ),
863+ ('instruction' , 'func3' , 17 ),
864+ ('line' , 'check_events' , 11 )])
865+
866+ class TestInstallIncrementallly (unittest .TestCase ):
867+
868+ def check_events (self , func , must_include , tool = TEST_TOOL , recorders = (ExceptionRecorder ,)):
869+ try :
870+ self .assertEqual (sys .monitoring ._all_events (), {})
871+ event_list = []
872+ all_events = 0
873+ for recorder in recorders :
874+ all_events |= recorder .event_type
875+ sys .monitoring .set_events (tool , all_events )
876+ for recorder in recorders :
877+ sys .monitoring .register_callback (tool , recorder .event_type , recorder (event_list ))
878+ func ()
879+ sys .monitoring .set_events (tool , 0 )
880+ for recorder in recorders :
881+ sys .monitoring .register_callback (tool , recorder .event_type , None )
882+ for line in must_include :
883+ self .assertIn (line , event_list )
884+ finally :
885+ sys .monitoring .set_events (tool , 0 )
886+ for recorder in recorders :
887+ sys .monitoring .register_callback (tool , recorder .event_type , None )
888+
889+ @staticmethod
890+ def func1 ():
891+ line1 = 1
892+
893+ MUST_INCLUDE_LI = [
894+ ('instruction' , 'func1' , 1 ),
895+ ('line' , 'func1' , 1 ),
896+ ('instruction' , 'func1' , 2 ),
897+ ('instruction' , 'func1' , 3 )]
898+
899+ def test_line_then_instruction (self ):
900+ recorders = [ LineRecorder , InstructionRecorder ]
901+ self .check_events (self .func1 ,
902+ recorders = recorders , must_include = self .EXPECTED_LI )
903+
904+ def test_instruction_then_line (self ):
905+ recorders = [ InstructionRecorder , LineRecorderLowNoise ]
906+ self .check_events (self .func1 ,
907+ recorders = recorders , must_include = self .EXPECTED_LI )
908+
909+ @staticmethod
910+ def func2 ():
911+ len (())
912+
913+ MUST_INCLUDE_CI = [
914+ ('instruction' , 'func2' , 1 ),
915+ ('call' , 'func2' , sys .monitoring .MISSING ),
916+ ('call' , 'len' , ()),
917+ ('instruction' , 'func2' , 6 ),
918+ ('instruction' , 'func2' , 7 )]
919+
920+
921+
922+ def test_line_then_instruction (self ):
923+ recorders = [ CallRecorder , InstructionRecorder ]
924+ self .check_events (self .func2 ,
925+ recorders = recorders , must_include = self .MUST_INCLUDE_CI )
926+
927+ def test_instruction_then_line (self ):
928+ recorders = [ InstructionRecorder , CallRecorder ]
929+ self .check_events (self .func2 ,
930+ recorders = recorders , must_include = self .MUST_INCLUDE_CI )
931+
770932class TestLocalEvents (unittest .TestCase ):
771933
772934 def check_events (self , func , expected , tool = TEST_TOOL , recorders = (ExceptionRecorder ,)):
0 commit comments