Skip to content

Commit 0ba9059

Browse files
gh-160676: Cover tier-2 monitoring callback errors
1 parent 65a6418 commit 0ba9059

4 files changed

Lines changed: 40 additions & 3 deletions

File tree

‎Lib/test/test_monitoring.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,37 @@ def test_func(x):
26772677

26782678
class TestTier2Optimizer(CheckEvents):
26792679

2680+
@requires_specialization
2681+
def test_raise_callback_stop_iteration_in_for_tier2(self):
2682+
_testinternalcapi = import_helper.import_module("_testinternalcapi")
2683+
_opcode = import_helper.import_module("_opcode")
2684+
threshold = _testinternalcapi.SPECIALIZATION_THRESHOLD
2685+
callback_executor = None
2686+
2687+
def run():
2688+
for i in range(threshold + 1):
2689+
sys.monitoring.set_events(
2690+
TEST_TOOL,
2691+
E.RAISE if i == threshold else 0,
2692+
)
2693+
2694+
def on_raise(code, offset, exc):
2695+
nonlocal callback_executor
2696+
if code is run.__code__ and isinstance(exc, StopIteration):
2697+
callback_executor = _opcode.get_executor(code, offset)
2698+
raise RuntimeError("monitor callback failed")
2699+
2700+
try:
2701+
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, on_raise)
2702+
with self.assertRaisesRegex(RuntimeError, "monitor callback failed"):
2703+
run()
2704+
self.assertIsNotNone(callback_executor)
2705+
uops = [op[0] for op in callback_executor]
2706+
self.assertIn("_ITER_NEXT_INLINE", uops)
2707+
finally:
2708+
sys.monitoring.set_events(TEST_TOOL, 0)
2709+
sys.monitoring.register_callback(TEST_TOOL, E.RAISE, None)
2710+
26802711
def test_monitoring_already_opimized_loop(self):
26812712
_testinternalcapi = import_helper.import_module("_testinternalcapi")
26822713

‎Python/bytecodes.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3818,7 +3818,8 @@ dummy_func(
38183818
if (item == NULL) {
38193819
if (_PyErr_Occurred(tstate)) {
38203820
if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
3821-
if (_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr) < 0) {
3821+
int err = _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr);
3822+
if (err < 0) {
38223823
ERROR_NO_POP();
38233824
}
38243825
_PyErr_Clear(tstate);

‎Python/ceval.c‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3834,7 +3834,8 @@ _PyStackRef _PyForIter_VirtualIteratorNext(PyThreadState* tstate, _PyInterpreter
38343834
if (next == NULL) {
38353835
if (_PyErr_Occurred(tstate)) {
38363836
if (_PyErr_ExceptionMatches(tstate, PyExc_StopIteration)) {
3837-
if (_PyEval_MonitorRaise(tstate, frame, frame->instr_ptr) < 0) {
3837+
int err = _PyEval_MonitorRaise(tstate, frame, frame->instr_ptr);
3838+
if (err < 0) {
38383839
return PyStackRef_ERROR;
38393840
}
38403841
_PyErr_Clear(tstate);

‎Python/executor_cases.c.h‎

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

0 commit comments

Comments
 (0)