@@ -1221,5 +1221,116 @@ def test_clear_unassigned_watcher_id(self):
12211221 self .clear_watcher (1 )
12221222
12231223
1224+ class TestFuncWatchers (unittest .TestCase ):
1225+ @contextmanager
1226+ def add_watcher (self , func ):
1227+ wid = _testcapi .add_func_watcher (func )
1228+ try :
1229+ yield
1230+ finally :
1231+ _testcapi .clear_func_watcher (wid )
1232+
1233+ def test_func_events_dispatched (self ):
1234+ events = []
1235+ def watcher (* args ):
1236+ events .append (args )
1237+
1238+ with self .add_watcher (watcher ):
1239+ def myfunc ():
1240+ pass
1241+ self .assertIn ((_testcapi .PYFUNC_EVENT_CREATE , myfunc , None ), events )
1242+ myfunc_id = id (myfunc )
1243+
1244+ new_code = self .test_func_events_dispatched .__code__
1245+ myfunc .__code__ = new_code
1246+ self .assertIn ((_testcapi .PYFUNC_EVENT_MODIFY_CODE , myfunc , new_code ), events )
1247+
1248+ new_defaults = (123 ,)
1249+ myfunc .__defaults__ = new_defaults
1250+ self .assertIn ((_testcapi .PYFUNC_EVENT_MODIFY_DEFAULTS , myfunc , new_defaults ), events )
1251+
1252+ new_defaults = (456 ,)
1253+ _testcapi .set_func_defaults_via_capi (myfunc , new_defaults )
1254+ self .assertIn ((_testcapi .PYFUNC_EVENT_MODIFY_DEFAULTS , myfunc , new_defaults ), events )
1255+
1256+ new_kwdefaults = {"self" : 123 }
1257+ myfunc .__kwdefaults__ = new_kwdefaults
1258+ self .assertIn ((_testcapi .PYFUNC_EVENT_MODIFY_KWDEFAULTS , myfunc , new_kwdefaults ), events )
1259+
1260+ new_kwdefaults = {"self" : 456 }
1261+ _testcapi .set_func_kwdefaults_via_capi (myfunc , new_kwdefaults )
1262+ self .assertIn ((_testcapi .PYFUNC_EVENT_MODIFY_KWDEFAULTS , myfunc , new_kwdefaults ), events )
1263+
1264+ # Clear events reference to func
1265+ events = []
1266+ del myfunc
1267+ self .assertIn ((_testcapi .PYFUNC_EVENT_DESTROY , myfunc_id , None ), events )
1268+
1269+ def test_multiple_watchers (self ):
1270+ events0 = []
1271+ def first_watcher (* args ):
1272+ events0 .append (args )
1273+
1274+ events1 = []
1275+ def second_watcher (* args ):
1276+ events1 .append (args )
1277+
1278+ with self .add_watcher (first_watcher ):
1279+ with self .add_watcher (second_watcher ):
1280+ def myfunc ():
1281+ pass
1282+
1283+ event = (_testcapi .PYFUNC_EVENT_CREATE , myfunc , None )
1284+ self .assertIn (event , events0 )
1285+ self .assertIn (event , events1 )
1286+
1287+ def test_watcher_raises_error (self ):
1288+ class MyError (Exception ):
1289+ pass
1290+
1291+ def watcher (* args ):
1292+ raise MyError ("testing 123" )
1293+
1294+ with self .add_watcher (watcher ):
1295+ with catch_unraisable_exception () as cm :
1296+ def myfunc ():
1297+ pass
1298+
1299+ self .assertEqual (
1300+ cm .unraisable .object ,
1301+ f"PyFunction_EVENT_CREATE watcher callback for { myfunc !r} "
1302+ )
1303+
1304+ def test_dealloc_watcher_raises_error (self ):
1305+ class MyError (Exception ):
1306+ pass
1307+
1308+ def watcher (* args ):
1309+ raise MyError ("testing 123" )
1310+
1311+ def myfunc ():
1312+ pass
1313+
1314+ with self .add_watcher (watcher ):
1315+ with catch_unraisable_exception () as cm :
1316+ del myfunc
1317+
1318+ self .assertIsInstance (cm .unraisable .exc_value , MyError )
1319+
1320+ def test_clear_out_of_range_watcher_id (self ):
1321+ with self .assertRaisesRegex (ValueError , r"invalid func watcher ID -1" ):
1322+ _testcapi .clear_func_watcher (- 1 )
1323+ with self .assertRaisesRegex (ValueError , r"invalid func watcher ID 8" ):
1324+ _testcapi .clear_func_watcher (8 ) # FUNC_MAX_WATCHERS = 8
1325+
1326+ def test_clear_unassigned_watcher_id (self ):
1327+ with self .assertRaisesRegex (ValueError , r"no func watcher set for ID 1" ):
1328+ _testcapi .clear_func_watcher (1 )
1329+
1330+ def test_allocate_too_many_watchers (self ):
1331+ with self .assertRaisesRegex (RuntimeError , r"no more func watcher IDs" ):
1332+ _testcapi .allocate_too_many_func_watchers ()
1333+
1334+
12241335if __name__ == "__main__" :
12251336 unittest .main ()
0 commit comments