From 0f26c066223c94979854b7570d9b2ad732f6c0ca Mon Sep 17 00:00:00 2001 From: Bot <1978924+jbower-fb@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:17:48 -0700 Subject: [PATCH 1/4] Always link generator frames when propagating a thrown-in exception through a yield-from chain. --- Lib/test/test_generators.py | 22 +++++++++++++++++++++- Objects/genobject.c | 15 +++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index bf2cb1160723b0..2ea6dba12effc1 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -758,7 +758,8 @@ def check_stack_names(self, frame, expected): while frame: name = frame.f_code.co_name # Stop checking frames when we get to our test helper. - if name.startswith('check_') or name.startswith('call_'): + if (name.startswith('check_') or name.startswith('call_') + or name.startswith('test')): break names.append(name) @@ -799,6 +800,25 @@ def call_throw(gen): self.check_yield_from_example(call_throw) + def test_throw_with_yield_from_custom_generator(self): + + class CustomGen: + def __init__(self, test): + self.test = test + def throw(self, *args): + self.test.check_stack_names(sys._getframe(), ['throw', 'g']) + def __iter__(self): + return self + def __next__(self): + return 42 + + def g(target): + yield from target + + gen = g(CustomGen(self)) + gen.send(None) + gen.throw(RuntimeError) + class YieldFromTests(unittest.TestCase): def test_generator_gi_yieldfrom(self): diff --git a/Objects/genobject.c b/Objects/genobject.c index 19c2c4e3331a89..865bc5e47ca750 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -471,14 +471,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, return gen_send_ex(gen, Py_None, 1, 0); goto throw_here; } + PyThreadState *tstate = _PyThreadState_GET(); if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { /* `yf` is a generator or a coroutine. */ - PyThreadState *tstate = _PyThreadState_GET(); - /* Since we are fast-tracking things by skipping the eval loop, - we need to update the current frame so the stack trace - will be reported correctly to the user. */ - /* XXX We should probably be updating the current frame - somewhere in ceval.c. */ + + /* Link frame into the stack to enable complete backtraces. */ _PyInterpreterFrame *prev = tstate->current_frame; frame->previous = prev; tstate->current_frame = frame; @@ -502,10 +499,16 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } + + _PyInterpreterFrame *prev = tstate->current_frame; + frame->previous = prev; + tstate->current_frame = frame; PyFrameState state = gen->gi_frame_state; gen->gi_frame_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); gen->gi_frame_state = state; + tstate->current_frame = prev; + frame->previous = NULL; Py_DECREF(meth); } Py_DECREF(yf); From 8be455114933ecaf544c6e185fd4e4802b3572f3 Mon Sep 17 00:00:00 2001 From: Bot <1978924+jbower-fb@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:50:25 -0800 Subject: [PATCH 2/4] Add NEWS entry. Summary: Test Plan: Reviewers: Subscribers: Tasks: Tags: --- .../2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst new file mode 100644 index 00000000000000..08118ff1af657d --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-07-21-48-23.gh-issue-126091.ETaRGE.rst @@ -0,0 +1,2 @@ +Ensure stack traces are complete when throwing into a generator chain that +ends in a custom generator. From 30b09d7b90a380cbbfd91522ccb08ee609675e8c Mon Sep 17 00:00:00 2001 From: Jacob Bower <1978924+jbower-fb@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:15:10 -0800 Subject: [PATCH 3/4] Add assertion on tstate. Co-authored-by: Peter Bierma --- Objects/genobject.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/genobject.c b/Objects/genobject.c index 865bc5e47ca750..af1e4e67d0ee41 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -472,6 +472,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, goto throw_here; } PyThreadState *tstate = _PyThreadState_GET(); + assert(tstate != NULL); if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { /* `yf` is a generator or a coroutine. */ From 940f28f5344f9fe19d0828077f775f9c92a53f71 Mon Sep 17 00:00:00 2001 From: Bot <1978924+jbower-fb@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:40:46 -0800 Subject: [PATCH 4/4] Put back XXX comment. --- Objects/genobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/genobject.c b/Objects/genobject.c index 865bc5e47ca750..870daaead25667 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -476,6 +476,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, /* `yf` is a generator or a coroutine. */ /* Link frame into the stack to enable complete backtraces. */ + /* XXX We should probably be updating the current frame somewhere in + ceval.c. */ _PyInterpreterFrame *prev = tstate->current_frame; frame->previous = prev; tstate->current_frame = frame;