Skip to content

Commit 836fac8

Browse files
gh-160676: Propagate monitoring callback errors from for loops
1 parent c436e8d commit 836fac8

5 files changed

Lines changed: 39 additions & 10 deletions

File tree

‎Include/internal/pycore_ceval.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ PyObject * _PyEval_ImportNameWithImport(
314314
PyObject *locals, PyObject *name, PyObject *fromlist, PyObject *level);
315315
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
316316
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
317-
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
317+
PyAPI_FUNC(int) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
318318
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate, _PyInterpreterFrame *frame);
319319
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
320320
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);

‎Lib/test/test_monitoring.py‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,31 @@ def implicit_stop_iteration(iterator=None):
895895
# This will record a STOP_ITERATION event for the StopIteration.
896896
self.check_events(implicit_stop_iteration, expected, recorders=recorders)
897897

898+
def test_raise_callback_stop_iteration_in_for(self):
899+
class Empty:
900+
def __iter__(self):
901+
return self
902+
903+
def __next__(self):
904+
raise StopIteration
905+
906+
def run():
907+
for _ in Empty():
908+
pass
909+
910+
def on_raise(code, offset, exc):
911+
if code is run.__code__ and isinstance(exc, StopIteration):
912+
raise RuntimeError("monitor callback failed")
913+
914+
try:
915+
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, on_raise)
916+
sys.monitoring.set_events(TEST_TOOL, E.RAISE)
917+
with self.assertRaisesRegex(RuntimeError, "monitor callback failed"):
918+
run()
919+
finally:
920+
sys.monitoring.set_events(TEST_TOOL, 0)
921+
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None)
922+
898923
initial = [
899924
("raise", ZeroDivisionError),
900925
("handled", ZeroDivisionError)

‎Python/bytecodes.c‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3818,7 +3818,9 @@ dummy_func(
38183818
if (item == NULL) {
38193819
if (_PyErr_Occurred(tstate)) {
38203820
if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
3821-
_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr);
3821+
if (_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr) < 0) {
3822+
ERROR_NO_POP();
3823+
}
38223824
_PyErr_Clear(tstate);
38233825
}
38243826
else {

‎Python/ceval.c‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2542,14 +2542,14 @@ _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v,
25422542

25432543

25442544

2545-
void
2545+
int
25462546
_PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame,
25472547
_Py_CODEUNIT *instr)
25482548
{
25492549
if (no_tools_for_local_event(tstate, frame, PY_MONITORING_EVENT_RAISE)) {
2550-
return;
2550+
return 0;
25512551
}
2552-
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
2552+
return do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_RAISE);
25532553
}
25542554

25552555
bool
@@ -3834,7 +3834,9 @@ _PyStackRef _PyForIter_VirtualIteratorNext(PyThreadState* tstate, _PyInterpreter
38343834
if (next == NULL) {
38353835
if (_PyErr_Occurred(tstate)) {
38363836
if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
3837-
_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr);
3837+
if (_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr) < 0) {
3838+
return PyStackRef_ERROR;
3839+
}
38383840
_PyErr_Clear(tstate);
38393841
}
38403842
else {

‎Python/executor_cases.c.h‎

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)