8000 gh-119333: Back up exception before calling PyContext_WatchCallback · python/cpython@1636f10 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1636f10

Browse files
committed
gh-119333: Back up exception before calling PyContext_WatchCallback
I believe that the value of a simpler API (and defense against poorly written callbacks) outweighs the cost of backing up and restoring the thread's exception state.
1 parent c00964e commit 1636f10

File tree

3 files changed

+12
-6
lines changed

3 files changed

+12
-6
lines changed

Doc/c-api/contextvars.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,6 @@ Context object management functions:
139139
exception will be printed as an unraisable exception using
140140
:c:func:`PyErr_FormatUnraisable`. Otherwise it should return ``0``.
141141
142-
There may already be a pending exception set on entry to the callback. In
143-
this case, the callback should return ``0`` with the same exception still
144-
set. This means the callback may not call any other API that can set an
145-
exception unless it saves and clears the exception state first, and restores
146-
it before returning.
147-
148142
.. versionadded:: 3.14
149143
150144

Lib/test/test_capi/test_watchers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,16 @@ def _in_context(stack):
640640
ctx.run(_in_context, stack)
641641
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
642642

643+
def test_exception_save(self):
644+
with self.context_watcher(2):
645+
with catch_unraisable_exception() as cm:
646+
def _in_context():
647+
raise RuntimeError("test")
648+
649+
with self.assertRaisesRegex(RuntimeError, "test"):
650+
contextvars.copy_context().run(_in_context)
651+
self.assertEqual(str(cm.unraisable.exc_value), "boom!")
652+
643653
def test_clear_out_of_range_watcher_id(self):
644654
with self.assertRaisesRegex(ValueError, r"Invalid context watcher ID -1"):
645655
_testcapi.clear_context_watcher(-1)

Python/context.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ static void notify_context_watchers(PyContextEvent event, PyContext *ctx, PyThre
119119
assert(interp->_initialized);
120120
uint8_t bits = interp->active_context_watchers;
121121
int i = 0;
122+
PyObject *exc = _PyErr_GetRaisedException(ts);
122123
while (bits) {
123124
assert(i < CONTEXT_MAX_WATCHERS);
124125
if (bits & 1) {
@@ -133,6 +134,7 @@ static void notify_context_watchers(PyContextEvent event, PyContext *ctx, PyThre
133134
i++;
134135
bits >>= 1;
135136
}
137+
_PyErr_SetRaisedException(ts, exc);
136138
}
137139

138140

0 commit comments

Comments
 (0)
0