mirror of
https://github.com/python/cpython.git
synced 2024-11-26 19:34:19 +08:00
gh-126091: Always link generator frames when propagating a thrown-in exception through a yield-from chain (#126092)
Always link generator frames when propagating a thrown-in exception through a yield-from chain.
This commit is contained in:
parent
3fafc1bd83
commit
e8bb053941
@ -758,7 +758,8 @@ class GeneratorStackTraceTest(unittest.TestCase):
|
|||||||
while frame:
|
while frame:
|
||||||
name = frame.f_code.co_name
|
name = frame.f_code.co_name
|
||||||
# Stop checking frames when we get to our test helper.
|
# 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
|
break
|
||||||
|
|
||||||
names.append(name)
|
names.append(name)
|
||||||
@ -799,6 +800,25 @@ class GeneratorStackTraceTest(unittest.TestCase):
|
|||||||
|
|
||||||
self.check_yield_from_example(call_throw)
|
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):
|
class YieldFromTests(unittest.TestCase):
|
||||||
def test_generator_gi_yieldfrom(self):
|
def test_generator_gi_yieldfrom(self):
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
Ensure stack traces are complete when throwing into a generator chain that
|
||||||
|
ends in a custom generator.
|
@ -471,14 +471,14 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
|||||||
return gen_send_ex(gen, Py_None, 1, 0);
|
return gen_send_ex(gen, Py_None, 1, 0);
|
||||||
goto throw_here;
|
goto throw_here;
|
||||||
}
|
}
|
||||||
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
|
assert(tstate != NULL);
|
||||||
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
|
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
|
||||||
/* `yf` is a generator or a coroutine. */
|
/* `yf` is a generator or a coroutine. */
|
||||||
PyThreadState *tstate = _PyThreadState_GET();
|
|
||||||
/* Since we are fast-tracking things by skipping the eval loop,
|
/* Link frame into the stack to enable complete backtraces. */
|
||||||
we need to update the current frame so the stack trace
|
/* XXX We should probably be updating the current frame somewhere in
|
||||||
will be reported correctly to the user. */
|
ceval.c. */
|
||||||
/* XXX We should probably be updating the current frame
|
|
||||||
somewhere in ceval.c. */
|
|
||||||
_PyInterpreterFrame *prev = tstate->current_frame;
|
_PyInterpreterFrame *prev = tstate->current_frame;
|
||||||
frame->previous = prev;
|
frame->previous = prev;
|
||||||
tstate->current_frame = frame;
|
tstate->current_frame = frame;
|
||||||
@ -502,10 +502,16 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
|
|||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
goto throw_here;
|
goto throw_here;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_PyInterpreterFrame *prev = tstate->current_frame;
|
||||||
|
frame->previous = prev;
|
||||||
|
tstate->current_frame = frame;
|
||||||
PyFrameState state = gen->gi_frame_state;
|
PyFrameState state = gen->gi_frame_state;
|
||||||
gen->gi_frame_state = FRAME_EXECUTING;
|
gen->gi_frame_state = FRAME_EXECUTING;
|
||||||
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
|
||||||
gen->gi_frame_state = state;
|
gen->gi_frame_state = state;
|
||||||
|
tstate->current_frame = prev;
|
||||||
|
frame->previous = NULL;
|
||||||
Py_DECREF(meth);
|
Py_DECREF(meth);
|
||||||
}
|
}
|
||||||
Py_DECREF(yf);
|
Py_DECREF(yf);
|
||||||
|
Loading…
Reference in New Issue
Block a user