Skip to content

Commit

Permalink
pythongh-124872: Change PyContext_WatchCallback return type to void
Browse files Browse the repository at this point in the history
The callback cannot prevent the event from occurring by raising an
exception.  Nor should it be able to; any failure to switch contexts
would be difficult if not impossible to handle correctly.  The API
should reflect that the callback is purely informational, and that the
context switch will happen even if the callback doesn't want it to.
  • Loading branch information
rhansen committed Oct 14, 2024
1 parent 4c115ab commit 300f2fc
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 21 deletions.
8 changes: 3 additions & 5 deletions Doc/c-api/contextvars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,16 @@ Context object management functions:
.. versionadded:: 3.14
.. c:type:: int (*PyContext_WatchCallback)(PyContextEvent event, PyObject *obj)
.. c:type:: void (*PyContext_WatchCallback)(PyContextEvent event, PyObject *obj)
Context object watcher callback function. The object passed to the callback
is event-specific; see :c:type:`PyContextEvent` for details.
Any pending exception is cleared before the callback is called and restored
after the callback returns.
If the callback raises an exception it must return ``-1``; the exception
will be printed as an unraisable exception using
:c:func:`PyErr_FormatUnraisable` and discarded. Otherwise it must return
``0``.
If the callback raises an exception it will be printed as an unraisable
exception using :c:func`PyErr_FormatUnraisable` and discarded.
.. versionadded:: 3.14
Expand Down
7 changes: 3 additions & 4 deletions Include/cpython/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,10 @@ typedef enum {
* Any pending exception is cleared before the callback is called and restored
* after the callback returns.
*
* If the callback raises an exception it must return -1; the exception will be
* printed as an unraisable exception using PyErr_FormatUnraisable and
* discarded. Otherwise it must return 0.
* If the callback raises an exception it will be printed as an unraisable
* exception using PyErr_FormatUnraisable and discarded.
*/
typedef int (*PyContext_WatchCallback)(PyContextEvent, PyObject *);
typedef void (*PyContext_WatchCallback)(PyContextEvent, PyObject *);

/*
* Register a per-interpreter callback that will be invoked for context object
Expand Down
19 changes: 8 additions & 11 deletions Modules/_testcapi/watchers.c
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ static int context_watcher_ids[NUM_CONTEXT_WATCHERS] = {-1, -1};
static int num_context_object_enter_events[NUM_CONTEXT_WATCHERS] = {0, 0};
static int num_context_object_exit_events[NUM_CONTEXT_WATCHERS] = {0, 0};

static int
static void
handle_context_watcher_event(int which_watcher, PyContextEvent event, PyObject *ctx) {
if (event == Py_CONTEXT_EVENT_ENTER) {
num_context_object_enter_events[which_watcher]++;
Expand All @@ -638,30 +638,27 @@ handle_context_watcher_event(int which_watcher, PyContextEvent event, PyObject *
num_context_object_exit_events[which_watcher]++;
}
else {
return -1;
Py_UNREACHABLE();
}
return 0;
}

static int
static void
first_context_watcher_callback(PyContextEvent event, PyObject *ctx) {
return handle_context_watcher_event(0, event, ctx);
handle_context_watcher_event(0, event, ctx);
}

static int
static void
second_context_watcher_callback(PyContextEvent event, PyObject *ctx) {
return handle_context_watcher_event(1, event, ctx);
handle_context_watcher_event(1, event, ctx);
}

static int
static void
noop_context_event_handler(PyContextEvent event, PyObject *ctx) {
return 0;
}

static int
static void
error_context_event_handler(PyContextEvent event, PyObject *ctx) {
PyErr_SetString(PyExc_RuntimeError, "boom!");
return -1;
}

static PyObject *
Expand Down
3 changes: 2 additions & 1 deletion Python/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ notify_context_watchers(PyThreadState *ts, PyContextEvent event, PyObject *ctx)
PyContext_WatchCallback cb = interp->context_watchers[i];
assert(cb != NULL);
PyObject *exc = _PyErr_GetRaisedException(ts);
if (cb(event, ctx) < 0) {
cb(event, ctx);
if (_PyErr_Occurred(ts) != NULL) {
PyErr_FormatUnraisable(
"Exception ignored in %s watcher callback for %R",
context_event_name(event), ctx);
Expand Down

0 comments on commit 300f2fc

Please sign in to comment.