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_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(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys);
|
||||||
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
|
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(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp);
|
||||||
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
|
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame);
|
||||||
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);
|
PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch);
|
||||||
|
|||||||
@@ -1079,6 +1079,25 @@ class ExceptionMonitoringTest(CheckEvents):
|
|||||||
|
|
||||||
self.assertEqual(events, expected)
|
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:
|
class LineRecorder:
|
||||||
|
|
||||||
event_type = E.LINE
|
event_type = E.LINE
|
||||||
|
|||||||
@@ -272,6 +272,8 @@ class ProfileHookTestCase(TestCaseBase):
|
|||||||
self.check_events(g, [(1, 'call', g_ident, None),
|
self.check_events(g, [(1, 'call', g_ident, None),
|
||||||
(2, 'call', f_ident, None),
|
(2, 'call', f_ident, None),
|
||||||
(2, 'return', f_ident, 0),
|
(2, 'return', f_ident, 0),
|
||||||
|
(2, 'call', f_ident, None),
|
||||||
|
(2, 'return', f_ident, None),
|
||||||
(1, 'return', g_ident, None),
|
(1, 'return', g_ident, None),
|
||||||
], check_args=True)
|
], 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;
|
_PyInterpreterFrame *frame = &gen->gi_iframe;
|
||||||
if (is_resume(frame->instr_ptr)) {
|
if (is_resume(frame->instr_ptr)) {
|
||||||
|
bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET());
|
||||||
/* We can safely ignore the outermost try block
|
/* We can safely ignore the outermost try block
|
||||||
* as it is automatically generated to handle
|
* as it is automatically generated to handle
|
||||||
* StopIteration. */
|
* StopIteration. */
|
||||||
int oparg = frame->instr_ptr->op.arg;
|
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
|
// RESUME after YIELD_VALUE and exception depth is 1
|
||||||
assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START);
|
assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START);
|
||||||
gen->gi_frame_state = FRAME_COMPLETED;
|
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);
|
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
|
static int
|
||||||
monitor_handled(PyThreadState *tstate,
|
monitor_handled(PyThreadState *tstate,
|
||||||
|
|||||||
Reference in New Issue
Block a user