@@ -3074,6 +3074,114 @@ def foo(*args):
30743074 del foo
30753075 sys .settrace (sys .gettrace ())
30763076
3077+ def test_local_trace_replacement (self ):
3078+ events = []
3079+
3080+ def assigned (frame , event , arg ):
3081+ events .append (('assigned' , event ))
3082+ return assigned
3083+
3084+ def replacement (frame , event , arg ):
3085+ events .append (('replacement' , event ))
3086+ return replacement
3087+
3088+ def local_trace (frame , event , arg ):
3089+ events .append (('original' , event ))
3090+ frame .f_trace = assigned
3091+ return replacement
3092+
3093+ def trace (frame , event , arg ):
3094+ if frame .f_code is target .__code__ :
3095+ return local_trace
3096+
3097+ def target ():
3098+ value = 1
3099+ return value
3100+
3101+ sys .settrace (trace )
3102+ target ()
3103+ sys .settrace (None )
3104+ self .assertEqual (events , [('original' , 'line' ),
3105+ ('replacement' , 'line' ),
3106+ ('replacement' , 'return' )])
3107+
3108+ def test_local_trace_clear (self ):
3109+ events = []
3110+
3111+ def local_trace (frame , event , arg ):
3112+ events .append (event )
3113+ frame .f_trace = None
3114+ return None
3115+
3116+ def trace (frame , event , arg ):
3117+ if frame .f_code is target .__code__ :
3118+ return local_trace
3119+
3120+ def target ():
3121+ value = 1
3122+ return value
3123+
3124+ sys .settrace (trace )
3125+ target ()
3126+ sys .settrace (None )
3127+ self .assertEqual (events , ['line' ])
3128+
3129+ def test_local_trace_error (self ):
3130+ frames = []
3131+
3132+ def local_trace (frame , event , arg ):
3133+ raise RuntimeError ('local trace error' )
3134+
3135+ def trace (frame , event , arg ):
3136+ if frame .f_code is target .__code__ :
3137+ frames .append (frame )
3138+ return local_trace
3139+
3140+ def target ():
3141+ return 1
3142+
3143+ sys .settrace (trace )
3144+ with self .assertRaisesRegex (RuntimeError , 'local trace error' ):
3145+ target ()
3146+ self .assertIsNone (sys .gettrace ())
3147+ self .assertEqual (len (frames ), 1 )
3148+ self .assertIsNone (frames [0 ].f_trace )
3149+
3150+ def test_local_trace_finalizer_reentrancy (self ):
3151+ events = []
3152+
3153+ def final_trace (frame , event , arg ):
3154+ events .append (('final' , event ))
3155+ return final_trace
3156+
3157+ def replacement (frame , event , arg ):
3158+ events .append (('replacement' , event ))
3159+ return replacement
3160+
3161+ class LocalTrace :
3162+ def __call__ (self , frame , event , arg ):
3163+ self .frame = frame
3164+ events .append (('original' , event ))
3165+ return replacement
3166+
3167+ def __del__ (self ):
3168+ events .append (('finalize' , self .frame .f_trace is replacement ))
3169+ self .frame .f_trace = final_trace
3170+
3171+ def trace (frame , event , arg ):
3172+ if frame .f_code is target .__code__ :
3173+ return LocalTrace ()
3174+
3175+ def target ():
3176+ value = 1
3177+ return value
3178+
3179+ sys .settrace (trace )
3180+ target ()
3181+ sys .settrace (None )
3182+ self .assertEqual (events , [('original' , 'line' ), ('finalize' , True ),
3183+ ('final' , 'line' ), ('final' , 'return' )])
3184+
30773185
30783186class TestLinesAfterTraceStarted (TraceTestCase ):
30793187
0 commit comments