gen.throw(ValueError)
+class GeneratorStackTraceTest(unittest.TestCase):
+
+ def check_stack_names(self, frame, expected):
+ names = []
+ 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_'):
+ break
+
+ names.append(name)
+ frame = frame.f_back
+
+ self.assertEqual(names, expected)
+
+ def check_yield_from_example(self, call_method):
+ def f():
+ self.check_stack_names(sys._getframe(), ['f', 'g'])
+ try:
+ yield
+ except Exception:
+ pass
+ self.check_stack_names(sys._getframe(), ['f', 'g'])
+
+ def g():
+ self.check_stack_names(sys._getframe(), ['g'])
+ yield from f()
+ self.check_stack_names(sys._getframe(), ['g'])
+
+ gen = g()
+ gen.send(None)
+ try:
+ call_method(gen)
+ except StopIteration:
+ pass
+
+ def test_send_with_yield_from(self):
+ def call_send(gen):
+ gen.send(None)
+
+ self.check_yield_from_example(call_send)
+
+ def test_throw_with_yield_from(self):
+ def call_throw(gen):
+ gen.throw(RuntimeError)
+
+ self.check_yield_from_example(call_throw)
+
+
class YieldFromTests(unittest.TestCase):
def test_generator_gi_yieldfrom(self):
def a():
}
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
/* `yf` is a generator or a coroutine. */
+ PyThreadState *tstate = _PyThreadState_GET();
+ PyFrameObject *f = tstate->frame;
+
gen->gi_running = 1;
+ /* 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. */
+ tstate->frame = gen->gi_frame;
/* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
typ, val, tb);
+ tstate->frame = f;
gen->gi_running = 0;
} else {
/* `yf` is an iterator or a coroutine-like object. */