gh-140373: Correctly emit PY_UNWIND event when generator is closed (GH-140767)
This commit is contained in:
@@ -301,6 +301,7 @@ PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame *
|
||||
PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs);
|
||||
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
|
||||
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
|
||||
PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate);
|
||||
PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
|
||||
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
|
||||
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);
|
||||
|
||||
@@ -1079,6 +1079,25 @@ class ExceptionMonitoringTest(CheckEvents):
|
||||
|
||||
self.assertEqual(events, expected)
|
||||
|
||||
# gh-140373
|
||||
def test_gen_unwind(self):
|
||||
def gen():
|
||||
yield 1
|
||||
|
||||
def f():
|
||||
g = gen()
|
||||
next(g)
|
||||
g.close()
|
||||
|
||||
recorders = (
|
||||
UnwindRecorder,
|
||||
)
|
||||
events = self.get_events(f, TEST_TOOL, recorders)
|
||||
expected = [
|
||||
("unwind", GeneratorExit, "gen"),
|
||||
]
|
||||
self.assertEqual(events, expected)
|
||||
|
||||
class LineRecorder:
|
||||
|
||||
event_type = E.LINE
|
||||
|
||||
@@ -272,6 +272,8 @@ class ProfileHookTestCase(TestCaseBase):
|
||||
self.check_events(g, [(1, 'call', g_ident, None),
|
||||
(2, 'call', f_ident, None),
|
||||
(2, 'return', f_ident, 0),
|
||||
(2, 'call', f_ident, None),
|
||||
(2, 'return', f_ident, None),
|
||||
(1, 'return', g_ident, None),
|
||||
], check_args=True)
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Correctly emit ``PY_UNWIND`` event when generator object is closed. Patch by
|
||||
Mikhail Efimov.
|
||||
@@ -407,11 +407,12 @@ gen_close(PyObject *self, PyObject *args)
|
||||
}
|
||||
_PyInterpreterFrame *frame = &gen->gi_iframe;
|
||||
if (is_resume(frame->instr_ptr)) {
|
||||
bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET());
|
||||
/* We can safely ignore the outermost try block
|
||||
* as it is automatically generated to handle
|
||||
* StopIteration. */
|
||||
int oparg = frame->instr_ptr->op.arg;
|
||||
if (oparg & RESUME_OPARG_DEPTH1_MASK) {
|
||||
if (oparg & RESUME_OPARG_DEPTH1_MASK && no_unwind_tools) {
|
||||
// RESUME after YIELD_VALUE and exception depth is 1
|
||||
assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START);
|
||||
gen->gi_frame_state = FRAME_COMPLETED;
|
||||
|
||||
@@ -2466,6 +2466,10 @@ monitor_unwind(PyThreadState *tstate,
|
||||
do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND);
|
||||
}
|
||||
|
||||
bool
|
||||
_PyEval_NoToolsForUnwind(PyThreadState *tstate) {
|
||||
return no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND);
|
||||
}
|
||||
|
||||
static int
|
||||
monitor_handled(PyThreadState *tstate,
|
||||
|
||||
Reference in New Issue
Block a user