Skip to content

Commit 4894127

Browse files
committed
add event name to watcher callback error message
1 parent b893187 commit 4894127

File tree

6 files changed

+69
-18
lines changed

6 files changed

+69
-18
lines changed

Include/cpython/code.h

+9-4
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,14 @@ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int);
224224

225225
PyAPI_FUNC(int) PyCode_Addr2Location(PyCodeObject *, int, int *, int *, int *, int *);
226226

227-
typedef enum PyCodeEvent {
228-
PY_CODE_EVENT_CREATE,
229-
PY_CODE_EVENT_DESTROY
227+
#define FOREACH_CODE_EVENT(V) \
228+
V(CREATE) \
229+
V(DESTROY)
230+
231+
typedef enum {
232+
#define DEF_EVENT(op) PY_CODE_EVENT_##op,
233+
FOREACH_CODE_EVENT(DEF_EVENT)
234+
#undef DEF_EVENT
230235
} PyCodeEvent;
231236

232237

@@ -236,7 +241,7 @@ typedef enum PyCodeEvent {
236241
* The callback is invoked with a borrowed reference to co, after it is
237242
* created and before it is destroyed.
238243
*
239-
* If the callback returns with an exception set, it must return -1. Otherwise
244+
* If the callback sets an exception, it must return -1. Otherwise
240245
* it should return 0.
241246
*/
242247
typedef int (*PyCode_WatchCallback)(

Include/cpython/dictobject.h

+13-8
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ typedef struct {
1616

1717
/* Dictionary version: globally unique, value change each time
1818
the dictionary is modified */
19-
#ifdef Py_BUILD_CORE
19+
#ifdef Py_BUILD_CORE
2020
uint64_t ma_version_tag;
2121
#else
2222
Py_DEPRECATED(3.12) uint64_t ma_version_tag;
23-
#endif
23+
#endif
2424

2525
PyDictKeysObject *ma_keys;
2626

@@ -90,13 +90,18 @@ PyAPI_FUNC(PyObject *) _PyDictView_Intersect(PyObject* self, PyObject *other);
9090

9191
/* Dictionary watchers */
9292

93+
#define FOREACH_DICT_EVENT(V) \
94+
V(ADDED) \
95+
V(MODIFIED) \
96+
V(DELETED) \
97+
V(CLONED) \
98+
V(CLEARED) \
99+
V(DEALLOCATED)
100+
93101
typedef enum {
94-
PyDict_EVENT_ADDED,
95-
PyDict_EVENT_MODIFIED,
96-
PyDict_EVENT_DELETED,
97-
PyDict_EVENT_CLONED,
98-
PyDict_EVENT_CLEARED,
99-
PyDict_EVENT_DEALLOCATED,
102+
#define DEF_EVENT(EVENT) PyDict_EVENT_##EVENT,
103+
FOREACH_DICT_EVENT(DEF_EVENT)
104+
#undef DEF_EVENT
100105
} PyDict_WatchEvent;
101106

102107
// Callback to be invoked when a watched dict is cleared, dealloced, or modified.

Lib/test/test_capi/test_watchers.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def test_error(self):
110110
with catch_unraisable_exception() as cm:
111111
d["foo"] = "bar"
112112
self.assertIn(
113-
"watcher callback for <dict at",
113+
"PyDict_EVENT_ADDED watcher callback for <dict at",
114114
cm.unraisable.object
115115
)
116116
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
@@ -405,7 +405,10 @@ def test_error(self):
405405
with catch_unraisable_exception() as cm:
406406
co = _testcapi.code_newempty("test_watchers", "dummy0", 0)
407407

408-
self.assertEqual(cm.unraisable.object, f"watcher callback for {co!r}")
408+
self.assertEqual(
409+
cm.unraisable.object,
410+
f"PY_CODE_EVENT_CREATE watcher callback for {co!r}"
411+
)
409412
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
410413

411414
def test_dealloc_error(self):
@@ -508,7 +511,7 @@ def myfunc():
508511

509512
self.assertEqual(
510513
cm.unraisable.object,
511-
f"watcher callback for {myfunc!r}"
514+
f"PyFunction_EVENT_CREATE watcher callback for {myfunc!r}"
512515
)
513516

514517
def test_dealloc_watcher_raises_error(self):

Objects/codeobject.c

+14-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@
1313

1414
static PyObject* code_repr(PyCodeObject *co);
1515

16+
static const char *
17+
code_event_name(PyCodeEvent event) {
18+
switch (event) {
19+
#define CASE(op) \
20+
case PY_CODE_EVENT_##op: \
21+
return "PY_CODE_EVENT_" #op;
22+
FOREACH_CODE_EVENT(CASE)
23+
#undef CASE
24+
}
25+
}
26+
1627
static void
1728
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
1829
{
@@ -33,7 +44,9 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
3344
PyObject *context = NULL;
3445
PyObject *repr = code_repr(co);
3546
if (repr) {
36-
context = PyUnicode_FromFormat("watcher callback for %U", repr);
47+
context = PyUnicode_FromFormat(
48+
"%s watcher callback for %U",
49+
code_event_name(event), repr);
3750
Py_DECREF(repr);
3851
}
3952
if (context == NULL) {

Objects/dictobject.c

+13-1
Original file line numberDiff line numberDiff line change
@@ -5739,6 +5739,17 @@ PyDict_ClearWatcher(int watcher_id)
57395739
return 0;
57405740
}
57415741

5742+
static const char *
5743+
dict_event_name(PyDict_WatchEvent event) {
5744+
switch (event) {
5745+
#define CASE(op) \
5746+
case PyDict_EVENT_##op: \
5747+
return "PyDict_EVENT_" #op;
5748+
FOREACH_DICT_EVENT(CASE)
5749+
#undef CASE
5750+
}
5751+
}
5752+
57425753
void
57435754
_PyDict_SendEvent(int watcher_bits,
57445755
PyDict_WatchEvent event,
@@ -5756,7 +5767,8 @@ _PyDict_SendEvent(int watcher_bits,
57565767
// dict as context, just an informative string message. Dict
57575768
// repr can call arbitrary code, so we invent a simpler version.
57585769
PyObject *context = PyUnicode_FromFormat(
5759-
"watcher callback for <dict at %p>", mp);
5770+
"%s watcher callback for <dict at %p>",
5771+
dict_event_name(event), mp);
57605772
if (context == NULL) {
57615773
context = Py_NewRef(Py_None);
57625774
}

Objects/funcobject.c

+14-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@
1010

1111
static PyObject* func_repr(PyFunctionObject *op);
1212

13+
static const char *
14+
func_event_name(PyFunction_WatchEvent event) {
15+
switch (event) {
16+
#define CASE(op) \
17+
case PyFunction_EVENT_##op: \
18+
return "PyFunction_EVENT_" #op;
19+
FOREACH_FUNC_EVENT(CASE)
20+
#undef CASE
21+
}
22+
}
23+
1324
static void
1425
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
1526
PyFunctionObject *func, PyObject *new_value)
@@ -28,7 +39,9 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
2839
PyObject *context = NULL;
2940
PyObject *repr = func_repr(func);
3041
if (repr != NULL) {
31-
context = PyUnicode_FromFormat("watcher callback for %U", repr);
42+
context = PyUnicode_FromFormat(
43+
"%s watcher callback for %U",
44+
func_event_name(event), repr);
3245
Py_DECREF(repr);
3346
}
3447
if (context == NULL) {

0 commit comments

Comments
 (0)