1 #include "parts.h"
2
3 #include "clinic/watchers.c.h"
4
5 #define Py_BUILD_CORE
6 #include "pycore_function.h" // FUNC_MAX_WATCHERS
7 #include "pycore_code.h" // CODE_MAX_WATCHERS
8
9 /*[clinic input]
10 module _testcapi
11 [clinic start generated code]*/
12 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
13
14 // Test dict watching
15 static PyObject *g_dict_watch_events;
16 static int g_dict_watchers_installed;
17
18 static int
19 dict_watch_callback(PyDict_WatchEvent event,
20 PyObject *dict,
21 PyObject *key,
22 PyObject *new_value)
23 {
24 PyObject *msg;
25 switch (event) {
26 case PyDict_EVENT_CLEARED:
27 msg = PyUnicode_FromString("clear");
28 break;
29 case PyDict_EVENT_DEALLOCATED:
30 msg = PyUnicode_FromString("dealloc");
31 break;
32 case PyDict_EVENT_CLONED:
33 msg = PyUnicode_FromString("clone");
34 break;
35 case PyDict_EVENT_ADDED:
36 msg = PyUnicode_FromFormat("new:%S:%S", key, new_value);
37 break;
38 case PyDict_EVENT_MODIFIED:
39 msg = PyUnicode_FromFormat("mod:%S:%S", key, new_value);
40 break;
41 case PyDict_EVENT_DELETED:
42 msg = PyUnicode_FromFormat("del:%S", key);
43 break;
44 default:
45 msg = PyUnicode_FromString("unknown");
46 }
47 if (msg == NULL) {
48 return -1;
49 }
50 assert(PyList_Check(g_dict_watch_events));
51 if (PyList_Append(g_dict_watch_events, msg) < 0) {
52 Py_DECREF(msg);
53 return -1;
54 }
55 Py_DECREF(msg);
56 return 0;
57 }
58
59 static int
60 dict_watch_callback_second(PyDict_WatchEvent event,
61 PyObject *dict,
62 PyObject *key,
63 PyObject *new_value)
64 {
65 PyObject *msg = PyUnicode_FromString("second");
66 if (msg == NULL) {
67 return -1;
68 }
69 int rc = PyList_Append(g_dict_watch_events, msg);
70 Py_DECREF(msg);
71 if (rc < 0) {
72 return -1;
73 }
74 return 0;
75 }
76
77 static int
78 dict_watch_callback_error(PyDict_WatchEvent event,
79 PyObject *dict,
80 PyObject *key,
81 PyObject *new_value)
82 {
83 PyErr_SetString(PyExc_RuntimeError, "boom!");
84 return -1;
85 }
86
87 static PyObject *
88 add_dict_watcher(PyObject *self, PyObject *kind)
89 {
90 int watcher_id;
91 assert(PyLong_Check(kind));
92 long kind_l = PyLong_AsLong(kind);
93 if (kind_l == 2) {
94 watcher_id = PyDict_AddWatcher(dict_watch_callback_second);
95 }
96 else if (kind_l == 1) {
97 watcher_id = PyDict_AddWatcher(dict_watch_callback_error);
98 }
99 else {
100 watcher_id = PyDict_AddWatcher(dict_watch_callback);
101 }
102 if (watcher_id < 0) {
103 return NULL;
104 }
105 if (!g_dict_watchers_installed) {
106 assert(!g_dict_watch_events);
107 if (!(g_dict_watch_events = PyList_New(0))) {
108 return NULL;
109 }
110 }
111 g_dict_watchers_installed++;
112 return PyLong_FromLong(watcher_id);
113 }
114
115 static PyObject *
116 clear_dict_watcher(PyObject *self, PyObject *watcher_id)
117 {
118 if (PyDict_ClearWatcher(PyLong_AsLong(watcher_id))) {
119 return NULL;
120 }
121 g_dict_watchers_installed--;
122 if (!g_dict_watchers_installed) {
123 assert(g_dict_watch_events);
124 Py_CLEAR(g_dict_watch_events);
125 }
126 Py_RETURN_NONE;
127 }
128
129 /*[clinic input]
130 _testcapi.watch_dict
131 watcher_id: int
132 dict: object
133 /
134 [clinic start generated code]*/
135
136 static PyObject *
137 _testcapi_watch_dict_impl(PyObject *module, int watcher_id, PyObject *dict)
138 /*[clinic end generated code: output=1426e0273cebe2d8 input=269b006d60c358bd]*/
139 {
140 if (PyDict_Watch(watcher_id, dict)) {
141 return NULL;
142 }
143 Py_RETURN_NONE;
144 }
145
146 /*[clinic input]
147 _testcapi.unwatch_dict = _testcapi.watch_dict
148 [clinic start generated code]*/
149
150 static PyObject *
151 _testcapi_unwatch_dict_impl(PyObject *module, int watcher_id, PyObject *dict)
152 /*[clinic end generated code: output=512b1a71ae33c351 input=cae7dc1b6f7713b8]*/
153 {
154 if (PyDict_Unwatch(watcher_id, dict)) {
155 return NULL;
156 }
157 Py_RETURN_NONE;
158 }
159
160 static PyObject *
161 get_dict_watcher_events(PyObject *self, PyObject *Py_UNUSED(args))
162 {
163 if (!g_dict_watch_events) {
164 PyErr_SetString(PyExc_RuntimeError, "no watchers active");
165 return NULL;
166 }
167 return Py_NewRef(g_dict_watch_events);
168 }
169
170 // Test type watchers
171 static PyObject *g_type_modified_events;
172 static int g_type_watchers_installed;
173
174 static int
175 type_modified_callback(PyTypeObject *type)
176 {
177 assert(PyList_Check(g_type_modified_events));
178 if(PyList_Append(g_type_modified_events, (PyObject *)type) < 0) {
179 return -1;
180 }
181 return 0;
182 }
183
184 static int
185 type_modified_callback_wrap(PyTypeObject *type)
186 {
187 assert(PyList_Check(g_type_modified_events));
188 PyObject *list = PyList_New(0);
189 if (list == NULL) {
190 return -1;
191 }
192 if (PyList_Append(list, (PyObject *)type) < 0) {
193 Py_DECREF(list);
194 return -1;
195 }
196 if (PyList_Append(g_type_modified_events, list) < 0) {
197 Py_DECREF(list);
198 return -1;
199 }
200 Py_DECREF(list);
201 return 0;
202 }
203
204 static int
205 type_modified_callback_error(PyTypeObject *type)
206 {
207 PyErr_SetString(PyExc_RuntimeError, "boom!");
208 return -1;
209 }
210
211 static PyObject *
212 add_type_watcher(PyObject *self, PyObject *kind)
213 {
214 int watcher_id;
215 assert(PyLong_Check(kind));
216 long kind_l = PyLong_AsLong(kind);
217 if (kind_l == 2) {
218 watcher_id = PyType_AddWatcher(type_modified_callback_wrap);
219 }
220 else if (kind_l == 1) {
221 watcher_id = PyType_AddWatcher(type_modified_callback_error);
222 }
223 else {
224 watcher_id = PyType_AddWatcher(type_modified_callback);
225 }
226 if (watcher_id < 0) {
227 return NULL;
228 }
229 if (!g_type_watchers_installed) {
230 assert(!g_type_modified_events);
231 if (!(g_type_modified_events = PyList_New(0))) {
232 return NULL;
233 }
234 }
235 g_type_watchers_installed++;
236 return PyLong_FromLong(watcher_id);
237 }
238
239 static PyObject *
240 clear_type_watcher(PyObject *self, PyObject *watcher_id)
241 {
242 if (PyType_ClearWatcher(PyLong_AsLong(watcher_id))) {
243 return NULL;
244 }
245 g_type_watchers_installed--;
246 if (!g_type_watchers_installed) {
247 assert(g_type_modified_events);
248 Py_CLEAR(g_type_modified_events);
249 }
250 Py_RETURN_NONE;
251 }
252
253 static PyObject *
254 get_type_modified_events(PyObject *self, PyObject *Py_UNUSED(args))
255 {
256 if (!g_type_modified_events) {
257 PyErr_SetString(PyExc_RuntimeError, "no watchers active");
258 return NULL;
259 }
260 return Py_NewRef(g_type_modified_events);
261 }
262
263 /*[clinic input]
264 _testcapi.watch_type
265 watcher_id: int
266 type: object
267 /
268 [clinic start generated code]*/
269
270 static PyObject *
271 _testcapi_watch_type_impl(PyObject *module, int watcher_id, PyObject *type)
272 /*[clinic end generated code: output=fdf4777126724fc4 input=5a808bf12be7e3ed]*/
273 {
274 if (PyType_Watch(watcher_id, type)) {
275 return NULL;
276 }
277 Py_RETURN_NONE;
278 }
279
280 /*[clinic input]
281 _testcapi.unwatch_type = _testcapi.watch_type
282 [clinic start generated code]*/
283
284 static PyObject *
285 _testcapi_unwatch_type_impl(PyObject *module, int watcher_id, PyObject *type)
286 /*[clinic end generated code: output=0389672d4ad5f68b input=6701911fb45edc9e]*/
287 {
288 if (PyType_Unwatch(watcher_id, type)) {
289 return NULL;
290 }
291 Py_RETURN_NONE;
292 }
293
294
295 // Test code object watching
296
297 #define NUM_CODE_WATCHERS 2
298 static int code_watcher_ids[NUM_CODE_WATCHERS] = {-1, -1};
299 static int num_code_object_created_events[NUM_CODE_WATCHERS] = {0, 0};
300 static int num_code_object_destroyed_events[NUM_CODE_WATCHERS] = {0, 0};
301
302 static int
303 handle_code_object_event(int which_watcher, PyCodeEvent event, PyCodeObject *co) {
304 if (event == PY_CODE_EVENT_CREATE) {
305 num_code_object_created_events[which_watcher]++;
306 }
307 else if (event == PY_CODE_EVENT_DESTROY) {
308 num_code_object_destroyed_events[which_watcher]++;
309 }
310 else {
311 return -1;
312 }
313 return 0;
314 }
315
316 static int
317 first_code_object_callback(PyCodeEvent event, PyCodeObject *co)
318 {
319 return handle_code_object_event(0, event, co);
320 }
321
322 static int
323 second_code_object_callback(PyCodeEvent event, PyCodeObject *co)
324 {
325 return handle_code_object_event(1, event, co);
326 }
327
328 static int
329 noop_code_event_handler(PyCodeEvent event, PyCodeObject *co)
330 {
331 return 0;
332 }
333
334 static int
335 error_code_event_handler(PyCodeEvent event, PyCodeObject *co)
336 {
337 PyErr_SetString(PyExc_RuntimeError, "boom!");
338 return -1;
339 }
340
341 static PyObject *
342 add_code_watcher(PyObject *self, PyObject *which_watcher)
343 {
344 int watcher_id;
345 assert(PyLong_Check(which_watcher));
346 long which_l = PyLong_AsLong(which_watcher);
347 if (which_l == 0) {
348 watcher_id = PyCode_AddWatcher(first_code_object_callback);
349 code_watcher_ids[0] = watcher_id;
350 num_code_object_created_events[0] = 0;
351 num_code_object_destroyed_events[0] = 0;
352 }
353 else if (which_l == 1) {
354 watcher_id = PyCode_AddWatcher(second_code_object_callback);
355 code_watcher_ids[1] = watcher_id;
356 num_code_object_created_events[1] = 0;
357 num_code_object_destroyed_events[1] = 0;
358 }
359 else if (which_l == 2) {
360 watcher_id = PyCode_AddWatcher(error_code_event_handler);
361 }
362 else {
363 PyErr_Format(PyExc_ValueError, "invalid watcher %d", which_l);
364 return NULL;
365 }
366 if (watcher_id < 0) {
367 return NULL;
368 }
369 return PyLong_FromLong(watcher_id);
370 }
371
372 static PyObject *
373 clear_code_watcher(PyObject *self, PyObject *watcher_id)
374 {
375 assert(PyLong_Check(watcher_id));
376 long watcher_id_l = PyLong_AsLong(watcher_id);
377 if (PyCode_ClearWatcher(watcher_id_l) < 0) {
378 return NULL;
379 }
380 // reset static events counters
381 if (watcher_id_l >= 0) {
382 for (int i = 0; i < NUM_CODE_WATCHERS; i++) {
383 if (watcher_id_l == code_watcher_ids[i]) {
384 code_watcher_ids[i] = -1;
385 num_code_object_created_events[i] = 0;
386 num_code_object_destroyed_events[i] = 0;
387 }
388 }
389 }
390 Py_RETURN_NONE;
391 }
392
393 static PyObject *
394 get_code_watcher_num_created_events(PyObject *self, PyObject *watcher_id)
395 {
396 assert(PyLong_Check(watcher_id));
397 long watcher_id_l = PyLong_AsLong(watcher_id);
398 assert(watcher_id_l >= 0 && watcher_id_l < NUM_CODE_WATCHERS);
399 return PyLong_FromLong(num_code_object_created_events[watcher_id_l]);
400 }
401
402 static PyObject *
403 get_code_watcher_num_destroyed_events(PyObject *self, PyObject *watcher_id)
404 {
405 assert(PyLong_Check(watcher_id));
406 long watcher_id_l = PyLong_AsLong(watcher_id);
407 assert(watcher_id_l >= 0 && watcher_id_l < NUM_CODE_WATCHERS);
408 return PyLong_FromLong(num_code_object_destroyed_events[watcher_id_l]);
409 }
410
411 static PyObject *
412 allocate_too_many_code_watchers(PyObject *self, PyObject *args)
413 {
414 int watcher_ids[CODE_MAX_WATCHERS + 1];
415 int num_watchers = 0;
416 for (unsigned long i = 0; i < sizeof(watcher_ids) / sizeof(int); i++) {
417 int watcher_id = PyCode_AddWatcher(noop_code_event_handler);
418 if (watcher_id == -1) {
419 break;
420 }
421 watcher_ids[i] = watcher_id;
422 num_watchers++;
423 }
424 PyObject *exc = PyErr_GetRaisedException();
425 for (int i = 0; i < num_watchers; i++) {
426 if (PyCode_ClearWatcher(watcher_ids[i]) < 0) {
427 PyErr_WriteUnraisable(Py_None);
428 break;
429 }
430 }
431 if (exc) {
432 PyErr_SetRaisedException(exc);
433 return NULL;
434 }
435 else if (PyErr_Occurred()) {
436 return NULL;
437 }
438 Py_RETURN_NONE;
439 }
440
441 // Test function watchers
442
443 #define NUM_TEST_FUNC_WATCHERS 2
444 static PyObject *pyfunc_watchers[NUM_TEST_FUNC_WATCHERS];
445 static int func_watcher_ids[NUM_TEST_FUNC_WATCHERS] = {-1, -1};
446
447 static PyObject *
448 get_id(PyObject *obj)
449 {
450 PyObject *builtins = PyEval_GetBuiltins(); // borrowed ref.
451 if (builtins == NULL) {
452 return NULL;
453 }
454 PyObject *id_str = PyUnicode_FromString("id");
455 if (id_str == NULL) {
456 return NULL;
457 }
458 PyObject *id_func = PyObject_GetItem(builtins, id_str);
459 Py_DECREF(id_str);
460 if (id_func == NULL) {
461 return NULL;
462 }
463 PyObject *stack[] = {obj};
464 PyObject *id = PyObject_Vectorcall(id_func, stack, 1, NULL);
465 Py_DECREF(id_func);
466 return id;
467 }
468
469 static int
470 call_pyfunc_watcher(PyObject *watcher, PyFunction_WatchEvent event,
471 PyFunctionObject *func, PyObject *new_value)
472 {
473 PyObject *event_obj = PyLong_FromLong(event);
474 if (event_obj == NULL) {
475 return -1;
476 }
477 if (new_value == NULL) {
478 new_value = Py_None;
479 }
480 Py_INCREF(new_value);
481 PyObject *func_or_id = NULL;
482 if (event == PyFunction_EVENT_DESTROY) {
483 /* Don't expose a function that's about to be destroyed to managed code */
484 func_or_id = get_id((PyObject *) func);
485 if (func_or_id == NULL) {
486 Py_DECREF(event_obj);
487 Py_DECREF(new_value);
488 return -1;
489 }
490 }
491 else {
492 Py_INCREF(func);
493 func_or_id = (PyObject *) func;
494 }
495 PyObject *stack[] = {event_obj, func_or_id, new_value};
496 PyObject *res = PyObject_Vectorcall(watcher, stack, 3, NULL);
497 int st = (res == NULL) ? -1 : 0;
498 Py_XDECREF(res);
499 Py_DECREF(new_value);
500 Py_DECREF(event_obj);
501 Py_DECREF(func_or_id);
502 return st;
503 }
504
505 static int
506 first_func_watcher_callback(PyFunction_WatchEvent event, PyFunctionObject *func,
507 PyObject *new_value)
508 {
509 return call_pyfunc_watcher(pyfunc_watchers[0], event, func, new_value);
510 }
511
512 static int
513 second_func_watcher_callback(PyFunction_WatchEvent event,
514 PyFunctionObject *func, PyObject *new_value)
515 {
516 return call_pyfunc_watcher(pyfunc_watchers[1], event, func, new_value);
517 }
518
519 static PyFunction_WatchCallback func_watcher_callbacks[NUM_TEST_FUNC_WATCHERS] = {
520 first_func_watcher_callback,
521 second_func_watcher_callback
522 };
523
524 static int
525 add_func_event(PyObject *module, const char *name, PyFunction_WatchEvent event)
526 {
527 PyObject *value = PyLong_FromLong(event);
528 if (value == NULL) {
529 return -1;
530 }
531 int ok = PyModule_AddObjectRef(module, name, value);
532 Py_DECREF(value);
533 return ok;
534 }
535
536 static PyObject *
537 add_func_watcher(PyObject *self, PyObject *func)
538 {
539 if (!PyFunction_Check(func)) {
540 PyErr_SetString(PyExc_TypeError, "'func' must be a function");
541 return NULL;
542 }
543 int idx = -1;
544 for (int i = 0; i < NUM_TEST_FUNC_WATCHERS; i++) {
545 if (func_watcher_ids[i] == -1) {
546 idx = i;
547 break;
548 }
549 }
550 if (idx == -1) {
551 PyErr_SetString(PyExc_RuntimeError, "no free test watchers");
552 return NULL;
553 }
554 func_watcher_ids[idx] = PyFunction_AddWatcher(func_watcher_callbacks[idx]);
555 if (func_watcher_ids[idx] < 0) {
556 return NULL;
557 }
558 pyfunc_watchers[idx] = Py_NewRef(func);
559 PyObject *result = PyLong_FromLong(func_watcher_ids[idx]);
560 if (result == NULL) {
561 return NULL;
562 }
563 return result;
564 }
565
566 static PyObject *
567 clear_func_watcher(PyObject *self, PyObject *watcher_id_obj)
568 {
569 long watcher_id = PyLong_AsLong(watcher_id_obj);
570 if ((watcher_id < INT_MIN) || (watcher_id > INT_MAX)) {
571 PyErr_SetString(PyExc_ValueError, "invalid watcher ID");
572 return NULL;
573 }
574 int wid = (int) watcher_id;
575 if (PyFunction_ClearWatcher(wid) < 0) {
576 return NULL;
577 }
578 int idx = -1;
579 for (int i = 0; i < NUM_TEST_FUNC_WATCHERS; i++) {
580 if (func_watcher_ids[i] == wid) {
581 idx = i;
582 break;
583 }
584 }
585 assert(idx != -1);
586 Py_CLEAR(pyfunc_watchers[idx]);
587 func_watcher_ids[idx] = -1;
588 Py_RETURN_NONE;
589 }
590
591 static int
592 noop_func_event_handler(PyFunction_WatchEvent event, PyFunctionObject *func,
593 PyObject *new_value)
594 {
595 return 0;
596 }
597
598 static PyObject *
599 allocate_too_many_func_watchers(PyObject *self, PyObject *args)
600 {
601 int watcher_ids[FUNC_MAX_WATCHERS + 1];
602 int num_watchers = 0;
603 for (unsigned long i = 0; i < sizeof(watcher_ids) / sizeof(int); i++) {
604 int watcher_id = PyFunction_AddWatcher(noop_func_event_handler);
605 if (watcher_id == -1) {
606 break;
607 }
608 watcher_ids[i] = watcher_id;
609 num_watchers++;
610 }
611 PyObject *exc = PyErr_GetRaisedException();
612 for (int i = 0; i < num_watchers; i++) {
613 if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) {
614 PyErr_WriteUnraisable(Py_None);
615 break;
616 }
617 }
618 if (exc) {
619 PyErr_SetRaisedException(exc);
620 return NULL;
621 }
622 else if (PyErr_Occurred()) {
623 return NULL;
624 }
625 Py_RETURN_NONE;
626 }
627
628 /*[clinic input]
629 _testcapi.set_func_defaults_via_capi
630 func: object
631 defaults: object
632 /
633 [clinic start generated code]*/
634
635 static PyObject *
636 _testcapi_set_func_defaults_via_capi_impl(PyObject *module, PyObject *func,
637 PyObject *defaults)
638 /*[clinic end generated code: output=caf0cb39db31ac24 input=e04a8508ca9d42fc]*/
639 {
640 if (PyFunction_SetDefaults(func, defaults) < 0) {
641 return NULL;
642 }
643 Py_RETURN_NONE;
644 }
645
646 /*[clinic input]
647 _testcapi.set_func_kwdefaults_via_capi = _testcapi.set_func_defaults_via_capi
648 [clinic start generated code]*/
649
650 static PyObject *
651 _testcapi_set_func_kwdefaults_via_capi_impl(PyObject *module, PyObject *func,
652 PyObject *defaults)
653 /*[clinic end generated code: output=9ed3b08177025070 input=f3cd1ca3c18de8ce]*/
654 {
655 if (PyFunction_SetKwDefaults(func, defaults) < 0) {
656 return NULL;
657 }
658 Py_RETURN_NONE;
659 }
660
661 static PyMethodDef test_methods[] = {
662 // Dict watchers.
663 {"add_dict_watcher", add_dict_watcher, METH_O, NULL},
664 {"clear_dict_watcher", clear_dict_watcher, METH_O, NULL},
665 _TESTCAPI_WATCH_DICT_METHODDEF
666 _TESTCAPI_UNWATCH_DICT_METHODDEF
667 {"get_dict_watcher_events",
668 (PyCFunction) get_dict_watcher_events, METH_NOARGS, NULL},
669
670 // Type watchers.
671 {"add_type_watcher", add_type_watcher, METH_O, NULL},
672 {"clear_type_watcher", clear_type_watcher, METH_O, NULL},
673 _TESTCAPI_WATCH_TYPE_METHODDEF
674 _TESTCAPI_UNWATCH_TYPE_METHODDEF
675 {"get_type_modified_events",
676 (PyCFunction) get_type_modified_events, METH_NOARGS, NULL},
677
678 // Code object watchers.
679 {"add_code_watcher", add_code_watcher, METH_O, NULL},
680 {"clear_code_watcher", clear_code_watcher, METH_O, NULL},
681 {"get_code_watcher_num_created_events",
682 get_code_watcher_num_created_events, METH_O, NULL},
683 {"get_code_watcher_num_destroyed_events",
684 get_code_watcher_num_destroyed_events, METH_O, NULL},
685 {"allocate_too_many_code_watchers",
686 (PyCFunction) allocate_too_many_code_watchers, METH_NOARGS, NULL},
687
688 // Function watchers.
689 {"add_func_watcher", add_func_watcher, METH_O, NULL},
690 {"clear_func_watcher", clear_func_watcher, METH_O, NULL},
691 _TESTCAPI_SET_FUNC_DEFAULTS_VIA_CAPI_METHODDEF
692 _TESTCAPI_SET_FUNC_KWDEFAULTS_VIA_CAPI_METHODDEF
693 {"allocate_too_many_func_watchers", allocate_too_many_func_watchers,
694 METH_NOARGS, NULL},
695 {NULL},
696 };
697
698 int
699 _PyTestCapi_Init_Watchers(PyObject *mod)
700 {
701 if (PyModule_AddFunctions(mod, test_methods) < 0) {
702 return -1;
703 }
704
705 /* Expose each event as an attribute on the module */
706 #define ADD_EVENT(event) \
707 if (add_func_event(mod, "PYFUNC_EVENT_" #event, \
708 PyFunction_EVENT_##event)) { \
709 return -1; \
710 }
711 PY_FOREACH_FUNC_EVENT(ADD_EVENT);
712 #undef ADD_EVENT
713
714 return 0;
715 }