@@ -5169,6 +5169,206 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
5169
5169
}
5170
5170
5171
5171
5172
+ // Test dict watching
5173
+ static PyObject * g_dict_watch_events ;
5174
+
5175
+ static void
5176
+ dict_watch_callback (PyDict_WatchEvent event ,
5177
+ PyObject * dict ,
5178
+ PyObject * key ,
5179
+ PyObject * new_value )
5180
+ {
5181
+ PyObject * msg ;
5182
+ switch (event ) {
5183
+ case PyDict_EVENT_CLEARED :
5184
+ msg = PyUnicode_FromString ("clear" );
5185
+ break ;
5186
+ case PyDict_EVENT_DEALLOCED :
5187
+ msg = PyUnicode_FromString ("dealloc" );
5188
+ break ;
5189
+ case PyDict_EVENT_CLONED :
5190
+ msg = PyUnicode_FromString ("clone" );
5191
+ break ;
5192
+ case PyDict_EVENT_ADDED :
5193
+ msg = PyUnicode_FromFormat ("new:%S:%S" , key , new_value );
5194
+ break ;
5195
+ case PyDict_EVENT_MODIFIED :
5196
+ msg = PyUnicode_FromFormat ("mod:%S:%S" , key , new_value );
5197
+ break ;
5198
+ case PyDict_EVENT_DELETED :
5199
+ msg = PyUnicode_FromFormat ("del:%S" , key );
5200
+ break ;
5201
+ default :
5202
+ msg = PyUnicode_FromString ("unknown" );
5203
+ }
5204
+ assert (PyList_Check (g_dict_watch_events ));
5205
+ PyList_Append (g_dict_watch_events , msg );
5206
+ }
5207
+
5208
+ static int
5209
+ dict_watch_assert (Py_ssize_t expected_num_events ,
5210
+ const char * expected_last_msg )
5211
+ {
5212
+ char buf [512 ];
5213
+ Py_ssize_t actual_num_events = PyList_Size (g_dict_watch_events );
5214
+ if (expected_num_events != actual_num_events ) {
5215
+ snprintf (buf ,
5216
+ 512 ,
5217
+ "got %d dict watch events, expected %d" ,
5218
+ (int )actual_num_events ,
5219
+ (int )expected_num_events );
5220
+ raiseTestError ("test_watch_dict" , (const char * )& buf );
5221
+ return -1 ;
5222
+ }
5223
+ PyObject * last_msg = PyList_GetItem (g_dict_watch_events ,
5224
+ PyList_Size (g_dict_watch_events )- 1 );
5225
+ if (PyUnicode_CompareWithASCIIString (last_msg , expected_last_msg )) {
5226
+ snprintf (buf ,
5227
+ 512 ,
5228
+ "last event is '%s', expected '%s'" ,
5229
+ PyUnicode_AsUTF8 (last_msg ),
5230
+ expected_last_msg );
5231
+ raiseTestError ("test_watch_dict" , (const char * )& buf );
5232
+ return -1 ;
5233
+ }
5234
+ return 0 ;
5235
+ }
5236
+
5237
+ static int
5238
+ try_watch (int watcher_id , PyObject * obj ) {
5239
+ if (PyDict_Watch (watcher_id , obj )) {
5240
+ raiseTestError ("test_watch_dict" , "PyDict_Watch() failed on dict" );
5241
+ return -1 ;
5242
+ }
5243
+ return 0 ;
5244
+ }
5245
+
5246
+ static int
5247
+ dict_watch_assert_error (int watcher_id , PyObject * obj , const char * fail_msg )
5248
+ {
5249
+ if (!PyDict_Watch (watcher_id , obj )) {
5250
+ raiseTestError ("test_watch_dict" , fail_msg );
5251
+ return -1 ;
5252
+ } else if (!PyErr_Occurred ()) {
5253
+ raiseTestError ("test_watch_dict" , "PyDict_Watch() returned error code without exception set" );
5254
+ return -1 ;
5255
+ } else {
5256
+ PyErr_Clear ();
5257
+ }
5258
+ return 0 ;
5259
+ }
5260
+
5261
+ static PyObject *
5262
+ test_watch_dict (PyObject * self , PyObject * Py_UNUSED (args ))
5263
+ {
5264
+ PyObject * watched = PyDict_New ();
5265
+ PyObject * unwatched = PyDict_New ();
5266
+ PyObject * one = PyLong_FromLong (1 );
5267
+ PyObject * two = PyLong_FromLong (2 );
5268
+ PyObject * key1 = PyUnicode_FromString ("key1" );
5269
+ PyObject * key2 = PyUnicode_FromString ("key2" );
5270
+
5271
+ g_dict_watch_events = PyList_New (0 );
5272
+
5273
+ int wid = PyDict_AddWatcher (dict_watch_callback );
5274
+ if (try_watch (wid , watched )) {
5275
+ return NULL ;
5276
+ }
5277
+
5278
+ PyDict_SetItem (unwatched , key1 , two );
5279
+ PyDict_Merge (watched , unwatched , 1 );
5280
+
5281
+ if (dict_watch_assert (1 , "clone" )) {
5282
+ return NULL ;
5283
+ }
5284
+
5285
+ PyDict_SetItem (watched , key1 , one );
5286
+ PyDict_SetItem (unwatched , key1 , one );
5287
+
5288
+ if (dict_watch_assert (2 , "mod:key1:1" )) {
5289
+ return NULL ;
5290
+ }
5291
+
5292
+ PyDict_SetItemString (watched , "key1" , two );
5293
+ PyDict_SetItemString (unwatched , "key1" , two );
5294
+
5295
+ if (dict_watch_assert (3 , "mod:key1:2" )) {
5296
+ return NULL ;
5297
+ }
5298
+
5299
+ PyDict_SetItem (watched , key2 , one );
5300
+ PyDict_SetItem (unwatched , key2 , one );
5301
+
5302
+ if (dict_watch_assert (4 , "new:key2:1" )) {
5303
+ return NULL ;
5304
+ }
5305
+
5306
+ _PyDict_Pop (watched , key2 , Py_None );
5307
+ _PyDict_Pop (unwatched , key2 , Py_None );
5308
+
5309
+ if (dict_watch_assert (5 , "del:key2" )) {
5310
+ return NULL ;
5311
+ }
5312
+
5313
+ PyDict_DelItemString (watched , "key1" );
5314
+ PyDict_DelItemString (unwatched , "key1" );
5315
+
5316
+ if (dict_watch_assert (6 , "del:key1" )) {
5317
+ return NULL ;
5318
+ }
5319
+
5320
+ PyDict_SetDefault (watched , key1 , one );
5321
+ PyDict_SetDefault (unwatched , key1 , one );
5322
+
5323
+ if (dict_watch_assert (7 , "new:key1:1" )) {
5324
+ return NULL ;
5325
+ }
5326
+
5327
+ PyDict_Clear (watched );
5328
+ PyDict_Clear (unwatched );
5329
+
5330
+ if (dict_watch_assert (8 , "clear" )) {
5331
+ return NULL ;
5332
+ }
5333
+
5334
+ PyObject * copy = PyDict_Copy (watched );
5335
+ // copied dict is not watched, so this does not add an event
5336
+ Py_CLEAR (copy );
5337
+
5338
+ Py_CLEAR (watched );
5339
+
5340
+ if (dict_watch_assert (9 , "dealloc" )) {
5341
+ return NULL ;
5342
+ }
5343
+
5344
+ // it is an error to try to watch a non-dict
5345
+ if (dict_watch_assert_error (wid , one , "PyDict_Watch() succeeded on non-dict" )) {
5346
+ return NULL ;
5347
+ }
5348
+
5349
+ // It is an error to pass an out-of-range watcher ID
5350
+ if (dict_watch_assert_error (-1 , unwatched , "PyDict_Watch() succeeded on negative watcher ID" )) {
5351
+ return NULL ;
5352
+ }
5353
+ if (dict_watch_assert_error (8 , unwatched , "PyDict_Watch() succeeded on too-large watcher ID" )) {
5354
+ return NULL ;
5355
+ }
5356
+
5357
+ // It is an error to pass a never-registered watcher ID
5358
+ if (dict_watch_assert_error (7 , unwatched , "PyDict_Watch() succeeded on unused watcher ID" )) {
5359
+ return NULL ;
5360
+ }
5361
+
5362
+ Py_CLEAR (unwatched );
5363
+ Py_CLEAR (g_dict_watch_events );
5364
+ Py_DECREF (one );
5365
+ Py_DECREF (two );
5366
+ Py_DECREF (key1 );
5367
+ Py_DECREF (key2 );
5368
+ Py_RETURN_NONE ;
5369
+ }
5370
+
5371
+
5172
5372
// Test PyFloat_Pack2(), PyFloat_Pack4() and PyFloat_Pack8()
5173
5373
static PyObject *
5174
5374
test_float_pack (PyObject * self , PyObject * args )
@@ -5762,6 +5962,7 @@ static PyMethodDef TestMethods[] = {
5762
5962
{"settrace_to_record" , settrace_to_record , METH_O , NULL },
5763
5963
{"test_macros" , test_macros , METH_NOARGS , NULL },
5764
5964
{"clear_managed_dict" , clear_managed_dict , METH_O , NULL },
5965
+ {"test_watch_dict" , test_watch_dict , METH_NOARGS , NULL },
5765
5966
{NULL , NULL } /* sentinel */
5766
5967
};
5767
5968
0 commit comments