GeezerVison is showing github.com — [3.14] gh-140373: Correctly emit `PY_UNWIND` event when generator is … · python/cpython@52731c4
File treeExpand file treeCollapse file tree Open diff view settings Misc/NEWS.d/next/Core_and_Builtins
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: Include/internal/pycore_ceval.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -296,6 +296,7 @@ PyAPI_FUNC(PyObject *) _PyEval_ImportName(PyThreadState *, _PyInterpreterFrame * | 296 | 296 | PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type, Py_ssize_t nargs, PyObject *kwargs); | 297 | 297 | PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map, PyObject *keys); | 298 | 298 | PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate, _PyInterpreterFrame *frame, _Py_CODEUNIT *instr); | | 299 | +PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate); | 299 | 300 | PyAPI_FUNC(int) _PyEval_UnpackIterableStackRef(PyThreadState *tstate, PyObject *v, int argcnt, int argcntafter, _PyStackRef *sp); | 300 | 301 | PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame *frame); | 301 | 302 | PyAPI_FUNC(PyObject **) _PyObjectArray_FromStackRefArray(_PyStackRef *input, Py_ssize_t nargs, PyObject **scratch); | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_monitoring.py +19Lines changed: 19 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1079,6 +1079,25 @@ def f(): | 1079 | 1079 |
| 1080 | 1080 | self.assertEqual(events, expected) | 1081 | 1081 |
| | 1082 | + # gh-140373 | | 1083 | + def test_gen_unwind(self): | | 1084 | + def gen(): | | 1085 | + yield 1 | | 1086 | + | | 1087 | + def f(): | | 1088 | + g = gen() | | 1089 | + next(g) | | 1090 | + g.close() | | 1091 | + | | 1092 | + recorders = ( | | 1093 | + UnwindRecorder, | | 1094 | + ) | | 1095 | + events = self.get_events(f, TEST_TOOL, recorders) | | 1096 | + expected = [ | | 1097 | + ("unwind", GeneratorExit, "gen"), | | 1098 | + ] | | 1099 | + self.assertEqual(events, expected) | | 1100 | + | 1082 | 1101 | class LineRecorder: | 1083 | 1102 |
| 1084 | 1103 | event_type = E.LINE | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_sys_setprofile.py +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -272,6 +272,8 @@ def g(p): | 272 | 272 | self.check_events(g, [(1, 'call', g_ident, None), | 273 | 273 | (2, 'call', f_ident, None), | 274 | 274 | (2, 'return', f_ident, 0), | | 275 | + (2, 'call', f_ident, None), | | 276 | + (2, 'return', f_ident, None), | 275 | 277 | (1, 'return', g_ident, None), | 276 | 278 | ], check_args=True) | 277 | 279 |
| |
Collapse file Copy file name to clipboard +2Lines changed: 2 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +Correctly emit ``PY_UNWIND`` event when generator object is closed. Patch by | | 2 | +Mikhail Efimov. |
Collapse file Copy file name to clipboardExpand all lines: Objects/genobject.c +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -407,11 +407,12 @@ gen_close(PyObject *self, PyObject *args) | 407 | 407 | } | 408 | 408 | _PyInterpreterFrame *frame = &gen->gi_iframe; | 409 | 409 | if (is_resume(frame->instr_ptr)) { | | 410 | + bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET()); | 410 | 411 | /* We can safely ignore the outermost try block | 411 | 412 | * as it is automatically generated to handle | 412 | 413 | * StopIteration. */ | 413 | 414 | int oparg = frame->instr_ptr->op.arg; | 414 | | - if (oparg & RESUME_OPARG_DEPTH1_MASK) { | | 415 | + if (oparg & RESUME_OPARG_DEPTH1_MASK && no_unwind_tools) { | 415 | 416 | // RESUME after YIELD_VALUE and exception depth is 1 | 416 | 417 | assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START); | 417 | 418 | gen->gi_frame_state = FRAME_COMPLETED; | |
Collapse file Copy file name to clipboardExpand all lines: Python/ceval.c +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -2545,6 +2545,10 @@ monitor_unwind(PyThreadState *tstate, | 2545 | 2545 | do_monitor_exc(tstate, frame, instr, PY_MONITORING_EVENT_PY_UNWIND); | 2546 | 2546 | } | 2547 | 2547 |
| | 2548 | +bool | | 2549 | +_PyEval_NoToolsForUnwind(PyThreadState *tstate) { | | 2550 | + return no_tools_for_global_event(tstate, PY_MONITORING_EVENT_PY_UNWIND); | | 2551 | +} | 2548 | 2552 |
| 2549 | 2553 | static int | 2550 | 2554 | monitor_handled(PyThreadState *tstate, | |
|