@@ -198,11 +198,11 @@ def test_c_return_count(self):
198198
199199EXCEPT_EVENTS = [
200200 (E .RAISE , "raise" ),
201- (E .PY_UNWIND , "unwind" ),
202201 (E .EXCEPTION_HANDLED , "exception_handled" ),
203202]
204203
205204SIMPLE_EVENTS = INSTRUMENTED_EVENTS + EXCEPT_EVENTS + [
205+ (E .PY_UNWIND , "unwind" ), # local event (not in EXCEPT_EVENTS: DISABLE is legal)
206206 (E .C_RAISE , "c_raise" ),
207207 (E .C_RETURN , "c_return" ),
208208]
@@ -1484,6 +1484,72 @@ def test_set_non_local_event(self):
14841484 with self .assertRaises (ValueError ):
14851485 sys .monitoring .set_local_events (TEST_TOOL , just_call .__code__ , E .RAISE )
14861486
1487+ def test_local_py_unwind (self ):
1488+ """PY_UNWIND fires as a local event only for the instrumented code object."""
1489+
1490+ def foo ():
1491+ raise RuntimeError ("test" )
1492+
1493+ def bar ():
1494+ raise RuntimeError ("test" )
1495+
1496+ events = []
1497+
1498+ def callback (code , offset , exc ):
1499+ events .append (code .co_name )
1500+
1501+ try :
1502+ sys .monitoring .register_callback (TEST_TOOL , E .PY_UNWIND , callback )
1503+ sys .monitoring .set_local_events (TEST_TOOL , foo .__code__ , E .PY_UNWIND )
1504+
1505+ try :
1506+ foo ()
1507+ except RuntimeError :
1508+ pass
1509+
1510+ try :
1511+ bar () # should NOT trigger the callback
1512+ except RuntimeError :
1513+ pass
1514+
1515+ self .assertEqual (events , ['foo' ])
1516+ finally :
1517+ sys .monitoring .set_local_events (TEST_TOOL , foo .__code__ , 0 )
1518+ sys .monitoring .register_callback (TEST_TOOL , E .PY_UNWIND , None )
1519+
1520+ def test_local_py_unwind_disable (self ):
1521+ """Returning DISABLE from a PY_UNWIND callback disables it for that code object."""
1522+
1523+ call_count = 0
1524+
1525+ def foo ():
1526+ raise RuntimeError ("test" )
1527+
1528+ def callback (code , offset , exc ):
1529+ nonlocal call_count
1530+ call_count += 1
1531+ return sys .monitoring .DISABLE
1532+
1533+ try :
1534+ sys .monitoring .register_callback (TEST_TOOL , E .PY_UNWIND , callback )
1535+ sys .monitoring .set_local_events (TEST_TOOL , foo .__code__ , E .PY_UNWIND )
1536+
1537+ try :
1538+ foo ()
1539+ except RuntimeError :
1540+ pass
1541+ self .assertEqual (call_count , 1 ) # fired once
1542+
1543+ try :
1544+ foo ()
1545+ except RuntimeError :
1546+ pass
1547+ self .assertEqual (call_count , 1 ) # not fired again — disabled by DISABLE return
1548+
1549+ finally :
1550+ sys .monitoring .set_local_events (TEST_TOOL , foo .__code__ , 0 )
1551+ sys .monitoring .register_callback (TEST_TOOL , E .PY_UNWIND , None )
1552+
14871553def line_from_offset (code , offset ):
14881554 for start , end , line in code .co_lines ():
14891555 if start <= offset < end :
0 commit comments