https://github.com/python/cpython/commit/4504ff89d5cfaa9f5a57956d20030184ca959361
commit: 4504ff89d5cfaa9f5a57956d20030184ca959361
branch: 3.13
author: Mikhail Efimov <[email protected]>
committer: hugovk <[email protected]>
date: 2025-12-20T16:21:11+02:00
summary:
[3.13] gh-140373: Correctly emit PY_UNWIND event when generator is closed
(GH-140767) (#140821)
files:
A Misc/NEWS.d/next/Core and
Builtins/2025-10-29-20-59-10.gh-issue-140373.-uoaPP.rst
M Include/internal/pycore_ceval.h
M Lib/test/test_cprofile.py
M Lib/test/test_monitoring.py
M Lib/test/test_sys_setprofile.py
M Objects/genobject.c
M Python/ceval.c
diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h
index f804823cc530dd..25605533aacf8f 100644
--- a/Include/internal/pycore_ceval.h
+++ b/Include/internal/pycore_ceval.h
@@ -258,6 +258,7 @@ PyAPI_FUNC(PyObject *)_PyEval_MatchClass(PyThreadState
*tstate, PyObject *subjec
PyAPI_FUNC(PyObject *)_PyEval_MatchKeys(PyThreadState *tstate, PyObject *map,
PyObject *keys);
PyAPI_FUNC(int) _PyEval_UnpackIterable(PyThreadState *tstate, PyObject *v, int
argcnt, int argcntafter, PyObject **sp);
PyAPI_FUNC(void) _PyEval_MonitorRaise(PyThreadState *tstate,
_PyInterpreterFrame *frame, _Py_CODEUNIT *instr);
+PyAPI_FUNC(bool) _PyEval_NoToolsForUnwind(PyThreadState *tstate);
PyAPI_FUNC(void) _PyEval_FrameClearAndPop(PyThreadState *tstate,
_PyInterpreterFrame *frame);
diff --git a/Lib/test/test_cprofile.py b/Lib/test/test_cprofile.py
index b46edf66bf09f8..acead07d507e4b 100644
--- a/Lib/test/test_cprofile.py
+++ b/Lib/test/test_cprofile.py
@@ -136,8 +136,8 @@ def test_throw(self):
for func, (cc, nc, _, _, _) in pr.stats.items():
if func[2] == "<genexpr>":
- self.assertEqual(cc, 1)
- self.assertEqual(nc, 1)
+ self.assertEqual(cc, 2)
+ self.assertEqual(nc, 2)
def test_bad_descriptor(self):
# gh-132250
diff --git a/Lib/test/test_monitoring.py b/Lib/test/test_monitoring.py
index 094d25b88c6e61..64435a382bb578 100644
--- a/Lib/test/test_monitoring.py
+++ b/Lib/test/test_monitoring.py
@@ -1017,6 +1017,25 @@ def f():
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
diff --git a/Lib/test/test_sys_setprofile.py b/Lib/test/test_sys_setprofile.py
index b2e8e8a15b67ea..e7ba97891a3635 100644
--- a/Lib/test/test_sys_setprofile.py
+++ b/Lib/test/test_sys_setprofile.py
@@ -272,6 +272,8 @@ def g(p):
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)
diff --git a/Misc/NEWS.d/next/Core and
Builtins/2025-10-29-20-59-10.gh-issue-140373.-uoaPP.rst b/Misc/NEWS.d/next/Core
and Builtins/2025-10-29-20-59-10.gh-issue-140373.-uoaPP.rst
new file mode 100644
index 00000000000000..c9a97037920fda
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and
Builtins/2025-10-29-20-59-10.gh-issue-140373.-uoaPP.rst
@@ -0,0 +1,2 @@
+Correctly emit ``PY_UNWIND`` event when generator object is closed. Patch by
+Mikhail Efimov.
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 412d3c1090b522..b19ff252ddb213 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -382,11 +382,12 @@ gen_close(PyGenObject *gen, PyObject *args)
}
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)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;
diff --git a/Python/ceval.c b/Python/ceval.c
index 301cc3b2b90358..ca07bfbaaf6a38 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2272,6 +2272,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,
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]