Skip to content

Commit

Permalink
add event name to watcher callback error message
Browse files Browse the repository at this point in the history
  • Loading branch information
carljm committed Mar 6, 2023
1 parent b893187 commit 4894127
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 18 deletions.
13 changes: 9 additions & 4 deletions Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,14 @@ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int);

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

typedef enum PyCodeEvent {
PY_CODE_EVENT_CREATE,
PY_CODE_EVENT_DESTROY
#define FOREACH_CODE_EVENT(V) \
V(CREATE) \
V(DESTROY)

typedef enum {
#define DEF_EVENT(op) PY_CODE_EVENT_##op,
FOREACH_CODE_EVENT(DEF_EVENT)
#undef DEF_EVENT
} PyCodeEvent;


Expand All @@ -236,7 +241,7 @@ typedef enum PyCodeEvent {
* The callback is invoked with a borrowed reference to co, after it is
* created and before it is destroyed.
*
* If the callback returns with an exception set, it must return -1. Otherwise
* If the callback sets an exception, it must return -1. Otherwise
* it should return 0.
*/
typedef int (*PyCode_WatchCallback)(
Expand Down
21 changes: 13 additions & 8 deletions Include/cpython/dictobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ typedef struct {

/* Dictionary version: globally unique, value change each time
the dictionary is modified */
#ifdef Py_BUILD_CORE
#ifdef Py_BUILD_CORE
uint64_t ma_version_tag;
#else
Py_DEPRECATED(3.12) uint64_t ma_version_tag;
#endif
#endif

PyDictKeysObject *ma_keys;

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

/* Dictionary watchers */

#define FOREACH_DICT_EVENT(V) \
V(ADDED) \
V(MODIFIED) \
V(DELETED) \
V(CLONED) \
V(CLEARED) \
V(DEALLOCATED)

typedef enum {
PyDict_EVENT_ADDED,
PyDict_EVENT_MODIFIED,
PyDict_EVENT_DELETED,
PyDict_EVENT_CLONED,
PyDict_EVENT_CLEARED,
PyDict_EVENT_DEALLOCATED,
#define DEF_EVENT(EVENT) PyDict_EVENT_##EVENT,
FOREACH_DICT_EVENT(DEF_EVENT)
#undef DEF_EVENT
} PyDict_WatchEvent;

// Callback to be invoked when a watched dict is cleared, dealloced, or modified.
Expand Down
9 changes: 6 additions & 3 deletions Lib/test/test_capi/test_watchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def test_error(self):
with catch_unraisable_exception() as cm:
d["foo"] = "bar"
self.assertIn(
"watcher callback for <dict at",
"PyDict_EVENT_ADDED watcher callback for <dict at",
cm.unraisable.object
)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
Expand Down Expand Up @@ -405,7 +405,10 @@ def test_error(self):
with catch_unraisable_exception() as cm:
co = _testcapi.code_newempty("test_watchers", "dummy0", 0)

self.assertEqual(cm.unraisable.object, f"watcher callback for {co!r}")
self.assertEqual(
cm.unraisable.object,
f"PY_CODE_EVENT_CREATE watcher callback for {co!r}"
)
self.assertEqual(str(cm.unraisable.exc_value), "boom!")

def test_dealloc_error(self):
Expand Down Expand Up @@ -508,7 +511,7 @@ def myfunc():

self.assertEqual(
cm.unraisable.object,
f"watcher callback for {myfunc!r}"
f"PyFunction_EVENT_CREATE watcher callback for {myfunc!r}"
)

def test_dealloc_watcher_raises_error(self):
Expand Down
15 changes: 14 additions & 1 deletion Objects/codeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@

static PyObject* code_repr(PyCodeObject *co);

static const char *
code_event_name(PyCodeEvent event) {
switch (event) {
#define CASE(op) \
case PY_CODE_EVENT_##op: \
return "PY_CODE_EVENT_" #op;
FOREACH_CODE_EVENT(CASE)
#undef CASE
}
}

static void
notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
{
Expand All @@ -33,7 +44,9 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
PyObject *context = NULL;
PyObject *repr = code_repr(co);
if (repr) {
context = PyUnicode_FromFormat("watcher callback for %U", repr);
context = PyUnicode_FromFormat(
"%s watcher callback for %U",
code_event_name(event), repr);
Py_DECREF(repr);
}
if (context == NULL) {
Expand Down
14 changes: 13 additions & 1 deletion Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -5739,6 +5739,17 @@ PyDict_ClearWatcher(int watcher_id)
return 0;
}

static const char *
dict_event_name(PyDict_WatchEvent event) {
switch (event) {
#define CASE(op) \
case PyDict_EVENT_##op: \
return "PyDict_EVENT_" #op;
FOREACH_DICT_EVENT(CASE)
#undef CASE
}
}

void
_PyDict_SendEvent(int watcher_bits,
PyDict_WatchEvent event,
Expand All @@ -5756,7 +5767,8 @@ _PyDict_SendEvent(int watcher_bits,
// dict as context, just an informative string message. Dict
// repr can call arbitrary code, so we invent a simpler version.
PyObject *context = PyUnicode_FromFormat(
"watcher callback for <dict at %p>", mp);
"%s watcher callback for <dict at %p>",
dict_event_name(event), mp);
if (context == NULL) {
context = Py_NewRef(Py_None);
}
Expand Down
15 changes: 14 additions & 1 deletion Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@

static PyObject* func_repr(PyFunctionObject *op);

static const char *
func_event_name(PyFunction_WatchEvent event) {
switch (event) {
#define CASE(op) \
case PyFunction_EVENT_##op: \
return "PyFunction_EVENT_" #op;
FOREACH_FUNC_EVENT(CASE)
#undef CASE
}
}

static void
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value)
Expand All @@ -28,7 +39,9 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyObject *context = NULL;
PyObject *repr = func_repr(func);
if (repr != NULL) {
context = PyUnicode_FromFormat("watcher callback for %U", repr);
context = PyUnicode_FromFormat(
"%s watcher callback for %U",
func_event_name(event), repr);
Py_DECREF(repr);
}
if (context == NULL) {
Expand Down

0 comments on commit 4894127

Please sign in to comment.