Skip to content

Commit 601ae09

Browse files
authored
GH-105162: Account for INSTRUMENTED_RESUME in gen.close/throw. (GH-105187)
1 parent ee26ca1 commit 601ae09

File tree

3 files changed

+52
-3
lines changed

3 files changed

+52
-3
lines changed

Lib/test/test_monitoring.py

+35
Original file line numberDiff line numberDiff line change
@@ -1425,3 +1425,38 @@ def f():
14251425

14261426
def test_get_local_events_uninitialized(self):
14271427
self.assertEqual(sys.monitoring.get_local_events(TEST_TOOL, self.f.__code__), 0)
1428+
1429+
class TestRegressions(MonitoringTestBase, unittest.TestCase):
1430+
1431+
def test_105162(self):
1432+
caught = None
1433+
1434+
def inner():
1435+
nonlocal caught
1436+
try:
1437+
yield
1438+
except Exception:
1439+
caught = "inner"
1440+
yield
1441+
1442+
def outer():
1443+
nonlocal caught
1444+
try:
1445+
yield from inner()
1446+
except Exception:
1447+
caught = "outer"
1448+
yield
1449+
1450+
def run():
1451+
gen = outer()
1452+
gen.send(None)
1453+
gen.throw(Exception)
1454+
run()
1455+
self.assertEqual(caught, "inner")
1456+
caught = None
1457+
try:
1458+
sys.monitoring.set_events(TEST_TOOL, E.PY_RESUME)
1459+
run()
1460+
self.assertEqual(caught, "inner")
1461+
finally:
1462+
sys.monitoring.set_events(TEST_TOOL, 0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed bug in generator.close()/throw() where an inner iterator would be
2+
ignored when the outer iterator was instrumented.

Objects/genobject.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ gen_close_iter(PyObject *yf)
331331
return 0;
332332
}
333333

334+
static inline bool
335+
is_resume(_Py_CODEUNIT *instr)
336+
{
337+
return instr->op.code == RESUME || instr->op.code == INSTRUMENTED_RESUME;
338+
}
339+
340+
static inline bool
341+
is_yield(_Py_CODEUNIT *instr)
342+
{
343+
return instr->op.code == YIELD_VALUE || instr->op.code == INSTRUMENTED_YIELD_VALUE;
344+
}
345+
334346
PyObject *
335347
_PyGen_yf(PyGenObject *gen)
336348
{
@@ -347,7 +359,7 @@ _PyGen_yf(PyGenObject *gen)
347359
return NULL;
348360
}
349361
_Py_CODEUNIT next = frame->prev_instr[1];
350-
if (next.op.code != RESUME || next.op.arg < 2)
362+
if (!is_resume(&next) || next.op.arg < 2)
351363
{
352364
/* Not in a yield from */
353365
return NULL;
@@ -382,8 +394,8 @@ gen_close(PyGenObject *gen, PyObject *args)
382394
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
383395
/* It is possible for the previous instruction to not be a
384396
* YIELD_VALUE if the debugger has changed the lineno. */
385-
if (err == 0 && frame->prev_instr[0].op.code == YIELD_VALUE) {
386-
assert(frame->prev_instr[1].op.code == RESUME);
397+
if (err == 0 && is_yield(frame->prev_instr)) {
398+
assert(is_resume(frame->prev_instr + 1));
387399
int exception_handler_depth = frame->prev_instr[0].op.code;
388400
assert(exception_handler_depth > 0);
389401
/* We can safely ignore the outermost try block

0 commit comments

Comments
 (0)