]> granicus.if.org Git - python/commitdiff
Issue #23996: Added _PyGen_SetStopIterationValue for safe raising
authorSerhiy Storchaka <storchaka@gmail.com>
Sun, 6 Nov 2016 16:47:03 +0000 (18:47 +0200)
committerSerhiy Storchaka <storchaka@gmail.com>
Sun, 6 Nov 2016 16:47:03 +0000 (18:47 +0200)
StopIteration with value. More safely handle non-normalized exceptions
in -_PyGen_FetchStopIterationValue.

1  2 
Include/genobject.h
Lib/test/test_asyncgen.py
Lib/test/test_coroutines.py
Lib/test/test_generators.py
Lib/test/test_yield_from.py
Modules/_asynciomodule.c
Objects/genobject.c

index 1ee4fd55293a81fc1fe62fa6ad4692ffeee43172,61e708a73ab610dd8562e618e455ab350ed7623c..8c1825fc070131d2d5caad43d26072dfd79c4eac
@@@ -41,8 -41,9 +41,9 @@@ PyAPI_FUNC(PyObject *) PyGen_New(struc
  PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *,
      PyObject *name, PyObject *qualname);
  PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *);
+ PyAPI_FUNC(int) _PyGen_SetStopIterationValue(PyObject *);
  PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
 -PyObject *_PyGen_Send(PyGenObject *, PyObject *);
 +PyAPI_FUNC(PyObject *) _PyGen_Send(PyGenObject *, PyObject *);
  PyObject *_PyGen_yf(PyGenObject *);
  PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
  
index c24fbea5b0945725ffce3e521d181c02fc1e475c,0000000000000000000000000000000000000000..68d202956f4e8448e37f0c2f8d619f09b3fbbff1
mode 100644,000000..100644
--- /dev/null
@@@ -1,825 -1,0 +1,904 @@@
 +import inspect
 +import sys
 +import types
 +import unittest
 +
 +from unittest import mock
 +
 +from test.support import import_module
 +asyncio = import_module("asyncio")
 +
 +
 +class AwaitException(Exception):
 +    pass
 +
 +
 +@types.coroutine
 +def awaitable(*, throw=False):
 +    if throw:
 +        yield ('throw',)
 +    else:
 +        yield ('result',)
 +
 +
 +def run_until_complete(coro):
 +    exc = False
 +    while True:
 +        try:
 +            if exc:
 +                exc = False
 +                fut = coro.throw(AwaitException)
 +            else:
 +                fut = coro.send(None)
 +        except StopIteration as ex:
 +            return ex.args[0]
 +
 +        if fut == ('throw',):
 +            exc = True
 +
 +
 +def to_list(gen):
 +    async def iterate():
 +        res = []
 +        async for i in gen:
 +            res.append(i)
 +        return res
 +
 +    return run_until_complete(iterate())
 +
 +
 +class AsyncGenSyntaxTest(unittest.TestCase):
 +
 +    def test_async_gen_syntax_01(self):
 +        code = '''async def foo():
 +            await abc
 +            yield from 123
 +        '''
 +
 +        with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'):
 +            exec(code, {}, {})
 +
 +    def test_async_gen_syntax_02(self):
 +        code = '''async def foo():
 +            yield from 123
 +        '''
 +
 +        with self.assertRaisesRegex(SyntaxError, 'yield from.*inside async'):
 +            exec(code, {}, {})
 +
 +    def test_async_gen_syntax_03(self):
 +        code = '''async def foo():
 +            await abc
 +            yield
 +            return 123
 +        '''
 +
 +        with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'):
 +            exec(code, {}, {})
 +
 +    def test_async_gen_syntax_04(self):
 +        code = '''async def foo():
 +            yield
 +            return 123
 +        '''
 +
 +        with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'):
 +            exec(code, {}, {})
 +
 +    def test_async_gen_syntax_05(self):
 +        code = '''async def foo():
 +            if 0:
 +                yield
 +            return 12
 +        '''
 +
 +        with self.assertRaisesRegex(SyntaxError, 'return.*value.*async gen'):
 +            exec(code, {}, {})
 +
 +
 +class AsyncGenTest(unittest.TestCase):
 +
 +    def compare_generators(self, sync_gen, async_gen):
 +        def sync_iterate(g):
 +            res = []
 +            while True:
 +                try:
 +                    res.append(g.__next__())
 +                except StopIteration:
 +                    res.append('STOP')
 +                    break
 +                except Exception as ex:
 +                    res.append(str(type(ex)))
 +            return res
 +
 +        def async_iterate(g):
 +            res = []
 +            while True:
 +                try:
 +                    g.__anext__().__next__()
 +                except StopAsyncIteration:
 +                    res.append('STOP')
 +                    break
 +                except StopIteration as ex:
 +                    if ex.args:
 +                        res.append(ex.args[0])
 +                    else:
 +                        res.append('EMPTY StopIteration')
 +                        break
 +                except Exception as ex:
 +                    res.append(str(type(ex)))
 +            return res
 +
 +        sync_gen_result = sync_iterate(sync_gen)
 +        async_gen_result = async_iterate(async_gen)
 +        self.assertEqual(sync_gen_result, async_gen_result)
 +        return async_gen_result
 +
 +    def test_async_gen_iteration_01(self):
 +        async def gen():
 +            await awaitable()
 +            a = yield 123
 +            self.assertIs(a, None)
 +            await awaitable()
 +            yield 456
 +            await awaitable()
 +            yield 789
 +
 +        self.assertEqual(to_list(gen()), [123, 456, 789])
 +
 +    def test_async_gen_iteration_02(self):
 +        async def gen():
 +            await awaitable()
 +            yield 123
 +            await awaitable()
 +
 +        g = gen()
 +        ai = g.__aiter__()
 +        self.assertEqual(ai.__anext__().__next__(), ('result',))
 +
 +        try:
 +            ai.__anext__().__next__()
 +        except StopIteration as ex:
 +            self.assertEqual(ex.args[0], 123)
 +        else:
 +            self.fail('StopIteration was not raised')
 +
 +        self.assertEqual(ai.__anext__().__next__(), ('result',))
 +
 +        try:
 +            ai.__anext__().__next__()
 +        except StopAsyncIteration as ex:
 +            self.assertFalse(ex.args)
 +        else:
 +            self.fail('StopAsyncIteration was not raised')
 +
 +    def test_async_gen_exception_03(self):
 +        async def gen():
 +            await awaitable()
 +            yield 123
 +            await awaitable(throw=True)
 +            yield 456
 +
 +        with self.assertRaises(AwaitException):
 +            to_list(gen())
 +
 +    def test_async_gen_exception_04(self):
 +        async def gen():
 +            await awaitable()
 +            yield 123
 +            1 / 0
 +
 +        g = gen()
 +        ai = g.__aiter__()
 +        self.assertEqual(ai.__anext__().__next__(), ('result',))
 +
 +        try:
 +            ai.__anext__().__next__()
 +        except StopIteration as ex:
 +            self.assertEqual(ex.args[0], 123)
 +        else:
 +            self.fail('StopIteration was not raised')
 +
 +        with self.assertRaises(ZeroDivisionError):
 +            ai.__anext__().__next__()
 +
 +    def test_async_gen_exception_05(self):
 +        async def gen():
 +            yield 123
 +            raise StopAsyncIteration
 +
 +        with self.assertRaisesRegex(RuntimeError,
 +                                    'async generator.*StopAsyncIteration'):
 +            to_list(gen())
 +
 +    def test_async_gen_exception_06(self):
 +        async def gen():
 +            yield 123
 +            raise StopIteration
 +
 +        with self.assertRaisesRegex(RuntimeError,
 +                                    'async generator.*StopIteration'):
 +            to_list(gen())
 +
 +    def test_async_gen_exception_07(self):
 +        def sync_gen():
 +            try:
 +                yield 1
 +                1 / 0
 +            finally:
 +                yield 2
 +                yield 3
 +
 +            yield 100
 +
 +        async def async_gen():
 +            try:
 +                yield 1
 +                1 / 0
 +            finally:
 +                yield 2
 +                yield 3
 +
 +            yield 100
 +
 +        self.compare_generators(sync_gen(), async_gen())
 +
 +    def test_async_gen_exception_08(self):
 +        def sync_gen():
 +            try:
 +                yield 1
 +            finally:
 +                yield 2
 +                1 / 0
 +                yield 3
 +
 +            yield 100
 +
 +        async def async_gen():
 +            try:
 +                yield 1
 +                await awaitable()
 +            finally:
 +                await awaitable()
 +                yield 2
 +                1 / 0
 +                yield 3
 +
 +            yield 100
 +
 +        self.compare_generators(sync_gen(), async_gen())
 +
 +    def test_async_gen_exception_09(self):
 +        def sync_gen():
 +            try:
 +                yield 1
 +                1 / 0
 +            finally:
 +                yield 2
 +                yield 3
 +
 +            yield 100
 +
 +        async def async_gen():
 +            try:
 +                await awaitable()
 +                yield 1
 +                1 / 0
 +            finally:
 +                yield 2
 +                await awaitable()
 +                yield 3
 +
 +            yield 100
 +
 +        self.compare_generators(sync_gen(), async_gen())
 +
 +    def test_async_gen_exception_10(self):
 +        async def gen():
 +            yield 123
 +        with self.assertRaisesRegex(TypeError,
 +                                    "non-None value .* async generator"):
 +            gen().__anext__().send(100)
 +
 +    def test_async_gen_api_01(self):
 +        async def gen():
 +            yield 123
 +
 +        g = gen()
 +
 +        self.assertEqual(g.__name__, 'gen')
 +        g.__name__ = '123'
 +        self.assertEqual(g.__name__, '123')
 +
 +        self.assertIn('.gen', g.__qualname__)
 +        g.__qualname__ = '123'
 +        self.assertEqual(g.__qualname__, '123')
 +
 +        self.assertIsNone(g.ag_await)
 +        self.assertIsInstance(g.ag_frame, types.FrameType)
 +        self.assertFalse(g.ag_running)
 +        self.assertIsInstance(g.ag_code, types.CodeType)
 +
 +        self.assertTrue(inspect.isawaitable(g.aclose()))
 +
 +
 +class AsyncGenAsyncioTest(unittest.TestCase):
 +
 +    def setUp(self):
 +        self.loop = asyncio.new_event_loop()
 +        asyncio.set_event_loop(None)
 +
 +    def tearDown(self):
 +        self.loop.close()
 +        self.loop = None
 +
 +    async def to_list(self, gen):
 +        res = []
 +        async for i in gen:
 +            res.append(i)
 +        return res
 +
 +    def test_async_gen_asyncio_01(self):
 +        async def gen():
 +            yield 1
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            yield 2
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            return
 +            yield 3
 +
 +        res = self.loop.run_until_complete(self.to_list(gen()))
 +        self.assertEqual(res, [1, 2])
 +
 +    def test_async_gen_asyncio_02(self):
 +        async def gen():
 +            yield 1
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            yield 2
 +            1 / 0
 +            yield 3
 +
 +        with self.assertRaises(ZeroDivisionError):
 +            self.loop.run_until_complete(self.to_list(gen()))
 +
 +    def test_async_gen_asyncio_03(self):
 +        loop = self.loop
 +
 +        class Gen:
 +            async def __aiter__(self):
 +                yield 1
 +                await asyncio.sleep(0.01, loop=loop)
 +                yield 2
 +
 +        res = loop.run_until_complete(self.to_list(Gen()))
 +        self.assertEqual(res, [1, 2])
 +
 +    def test_async_gen_asyncio_anext_04(self):
 +        async def foo():
 +            yield 1
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            try:
 +                yield 2
 +                yield 3
 +            except ZeroDivisionError:
 +                yield 1000
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            yield 4
 +
 +        async def run1():
 +            it = foo().__aiter__()
 +
 +            self.assertEqual(await it.__anext__(), 1)
 +            self.assertEqual(await it.__anext__(), 2)
 +            self.assertEqual(await it.__anext__(), 3)
 +            self.assertEqual(await it.__anext__(), 4)
 +            with self.assertRaises(StopAsyncIteration):
 +                await it.__anext__()
 +            with self.assertRaises(StopAsyncIteration):
 +                await it.__anext__()
 +
 +        async def run2():
 +            it = foo().__aiter__()
 +
 +            self.assertEqual(await it.__anext__(), 1)
 +            self.assertEqual(await it.__anext__(), 2)
 +            try:
 +                it.__anext__().throw(ZeroDivisionError)
 +            except StopIteration as ex:
 +                self.assertEqual(ex.args[0], 1000)
 +            else:
 +                self.fail('StopIteration was not raised')
 +            self.assertEqual(await it.__anext__(), 4)
 +            with self.assertRaises(StopAsyncIteration):
 +                await it.__anext__()
 +
 +        self.loop.run_until_complete(run1())
 +        self.loop.run_until_complete(run2())
 +
 +    def test_async_gen_asyncio_anext_05(self):
 +        async def foo():
 +            v = yield 1
 +            v = yield v
 +            yield v * 100
 +
 +        async def run():
 +            it = foo().__aiter__()
 +
 +            try:
 +                it.__anext__().send(None)
 +            except StopIteration as ex:
 +                self.assertEqual(ex.args[0], 1)
 +            else:
 +                self.fail('StopIteration was not raised')
 +
 +            try:
 +                it.__anext__().send(10)
 +            except StopIteration as ex:
 +                self.assertEqual(ex.args[0], 10)
 +            else:
 +                self.fail('StopIteration was not raised')
 +
 +            try:
 +                it.__anext__().send(12)
 +            except StopIteration as ex:
 +                self.assertEqual(ex.args[0], 1200)
 +            else:
 +                self.fail('StopIteration was not raised')
 +
 +            with self.assertRaises(StopAsyncIteration):
 +                await it.__anext__()
 +
 +        self.loop.run_until_complete(run())
 +
++    def test_async_gen_asyncio_anext_tuple(self):
++        async def foo():
++            try:
++                yield (1,)
++            except ZeroDivisionError:
++                yield (2,)
++
++        async def run():
++            it = foo().__aiter__()
++
++            self.assertEqual(await it.__anext__(), (1,))
++            with self.assertRaises(StopIteration) as cm:
++                it.__anext__().throw(ZeroDivisionError)
++            self.assertEqual(cm.exception.args[0], (2,))
++            with self.assertRaises(StopAsyncIteration):
++                await it.__anext__()
++
++        self.loop.run_until_complete(run())
++
++    def test_async_gen_asyncio_anext_stopiteration(self):
++        async def foo():
++            try:
++                yield StopIteration(1)
++            except ZeroDivisionError:
++                yield StopIteration(3)
++
++        async def run():
++            it = foo().__aiter__()
++
++            v = await it.__anext__()
++            self.assertIsInstance(v, StopIteration)
++            self.assertEqual(v.value, 1)
++            with self.assertRaises(StopIteration) as cm:
++                it.__anext__().throw(ZeroDivisionError)
++            v = cm.exception.args[0]
++            self.assertIsInstance(v, StopIteration)
++            self.assertEqual(v.value, 3)
++            with self.assertRaises(StopAsyncIteration):
++                await it.__anext__()
++
++        self.loop.run_until_complete(run())
++
 +    def test_async_gen_asyncio_aclose_06(self):
 +        async def foo():
 +            try:
 +                yield 1
 +                1 / 0
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                yield 12
 +
 +        async def run():
 +            gen = foo()
 +            it = gen.__aiter__()
 +            await it.__anext__()
 +            await gen.aclose()
 +
 +        with self.assertRaisesRegex(
 +                RuntimeError,
 +                "async generator ignored GeneratorExit"):
 +            self.loop.run_until_complete(run())
 +
 +    def test_async_gen_asyncio_aclose_07(self):
 +        DONE = 0
 +
 +        async def foo():
 +            nonlocal DONE
 +            try:
 +                yield 1
 +                1 / 0
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE += 1
 +            DONE += 1000
 +
 +        async def run():
 +            gen = foo()
 +            it = gen.__aiter__()
 +            await it.__anext__()
 +            await gen.aclose()
 +
 +        self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_aclose_08(self):
 +        DONE = 0
 +
 +        fut = asyncio.Future(loop=self.loop)
 +
 +        async def foo():
 +            nonlocal DONE
 +            try:
 +                yield 1
 +                await fut
 +                DONE += 1000
 +                yield 2
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE += 1
 +            DONE += 1000
 +
 +        async def run():
 +            gen = foo()
 +            it = gen.__aiter__()
 +            self.assertEqual(await it.__anext__(), 1)
 +            t = self.loop.create_task(it.__anext__())
 +            await asyncio.sleep(0.01, loop=self.loop)
 +            await gen.aclose()
 +            return t
 +
 +        t = self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +        # Silence ResourceWarnings
 +        fut.cancel()
 +        t.cancel()
 +        self.loop.run_until_complete(asyncio.sleep(0.01, loop=self.loop))
 +
 +    def test_async_gen_asyncio_gc_aclose_09(self):
 +        DONE = 0
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                while True:
 +                    yield 1
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +            await g.__anext__()
 +            await g.__anext__()
 +            del g
 +
 +            await asyncio.sleep(0.1, loop=self.loop)
 +
 +        self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_asend_01(self):
 +        DONE = 0
 +
 +        # Sanity check:
 +        def sgen():
 +            v = yield 1
 +            yield v * 2
 +        sg = sgen()
 +        v = sg.send(None)
 +        self.assertEqual(v, 1)
 +        v = sg.send(100)
 +        self.assertEqual(v, 200)
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                v = yield 1
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                yield v * 2
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                return
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +
 +            v = await g.asend(None)
 +            self.assertEqual(v, 1)
 +
 +            v = await g.asend(100)
 +            self.assertEqual(v, 200)
 +
 +            with self.assertRaises(StopAsyncIteration):
 +                await g.asend(None)
 +
 +        self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_asend_02(self):
 +        DONE = 0
 +
 +        async def sleep_n_crash(delay):
 +            await asyncio.sleep(delay, loop=self.loop)
 +            1 / 0
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                v = yield 1
 +                await sleep_n_crash(0.01)
 +                DONE += 1000
 +                yield v * 2
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +
 +            v = await g.asend(None)
 +            self.assertEqual(v, 1)
 +
 +            await g.asend(100)
 +
 +        with self.assertRaises(ZeroDivisionError):
 +            self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_asend_03(self):
 +        DONE = 0
 +
 +        async def sleep_n_crash(delay):
 +            fut = asyncio.ensure_future(asyncio.sleep(delay, loop=self.loop),
 +                                        loop=self.loop)
 +            self.loop.call_later(delay / 2, lambda: fut.cancel())
 +            return await fut
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                v = yield 1
 +                await sleep_n_crash(0.01)
 +                DONE += 1000
 +                yield v * 2
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +
 +            v = await g.asend(None)
 +            self.assertEqual(v, 1)
 +
 +            await g.asend(100)
 +
 +        with self.assertRaises(asyncio.CancelledError):
 +            self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_athrow_01(self):
 +        DONE = 0
 +
 +        class FooEr(Exception):
 +            pass
 +
 +        # Sanity check:
 +        def sgen():
 +            try:
 +                v = yield 1
 +            except FooEr:
 +                v = 1000
 +            yield v * 2
 +        sg = sgen()
 +        v = sg.send(None)
 +        self.assertEqual(v, 1)
 +        v = sg.throw(FooEr)
 +        self.assertEqual(v, 2000)
 +        with self.assertRaises(StopIteration):
 +            sg.send(None)
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                try:
 +                    v = yield 1
 +                except FooEr:
 +                    v = 1000
 +                    await asyncio.sleep(0.01, loop=self.loop)
 +                yield v * 2
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                # return
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +
 +            v = await g.asend(None)
 +            self.assertEqual(v, 1)
 +
 +            v = await g.athrow(FooEr)
 +            self.assertEqual(v, 2000)
 +
 +            with self.assertRaises(StopAsyncIteration):
 +                await g.asend(None)
 +
 +        self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
 +    def test_async_gen_asyncio_athrow_02(self):
 +        DONE = 0
 +
 +        class FooEr(Exception):
 +            pass
 +
 +        async def sleep_n_crash(delay):
 +            fut = asyncio.ensure_future(asyncio.sleep(delay, loop=self.loop),
 +                                        loop=self.loop)
 +            self.loop.call_later(delay / 2, lambda: fut.cancel())
 +            return await fut
 +
 +        async def gen():
 +            nonlocal DONE
 +            try:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                try:
 +                    v = yield 1
 +                except FooEr:
 +                    await sleep_n_crash(0.01)
 +                yield v * 2
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                # return
 +            finally:
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                await asyncio.sleep(0.01, loop=self.loop)
 +                DONE = 1
 +
 +        async def run():
 +            g = gen()
 +
 +            v = await g.asend(None)
 +            self.assertEqual(v, 1)
 +
 +            try:
 +                await g.athrow(FooEr)
 +            except asyncio.CancelledError:
 +                self.assertEqual(DONE, 1)
 +                raise
 +            else:
 +                self.fail('CancelledError was not raised')
 +
 +        with self.assertRaises(asyncio.CancelledError):
 +            self.loop.run_until_complete(run())
 +        self.assertEqual(DONE, 1)
 +
++    def test_async_gen_asyncio_athrow_tuple(self):
++        async def gen():
++            try:
++                yield 1
++            except ZeroDivisionError:
++                yield (2,)
++
++        async def run():
++            g = gen()
++            v = await g.asend(None)
++            self.assertEqual(v, 1)
++            v = await g.athrow(ZeroDivisionError)
++            self.assertEqual(v, (2,))
++            with self.assertRaises(StopAsyncIteration):
++                await g.asend(None)
++
++        self.loop.run_until_complete(run())
++
++    def test_async_gen_asyncio_athrow_stopiteration(self):
++        async def gen():
++            try:
++                yield 1
++            except ZeroDivisionError:
++                yield StopIteration(2)
++
++        async def run():
++            g = gen()
++            v = await g.asend(None)
++            self.assertEqual(v, 1)
++            v = await g.athrow(ZeroDivisionError)
++            self.assertIsInstance(v, StopIteration)
++            self.assertEqual(v.value, 2)
++            with self.assertRaises(StopAsyncIteration):
++                await g.asend(None)
++
++        self.loop.run_until_complete(run())
++
 +    def test_async_gen_asyncio_shutdown_01(self):
 +        finalized = 0
 +
 +        async def waiter(timeout):
 +            nonlocal finalized
 +            try:
 +                await asyncio.sleep(timeout, loop=self.loop)
 +                yield 1
 +            finally:
 +                await asyncio.sleep(0, loop=self.loop)
 +                finalized += 1
 +
 +        async def wait():
 +            async for _ in waiter(1):
 +                pass
 +
 +        t1 = self.loop.create_task(wait())
 +        t2 = self.loop.create_task(wait())
 +
 +        self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
 +
 +        self.loop.run_until_complete(self.loop.shutdown_asyncgens())
 +        self.assertEqual(finalized, 2)
 +
 +        # Silence warnings
 +        t1.cancel()
 +        t2.cancel()
 +        self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
 +
 +    def test_async_gen_asyncio_shutdown_02(self):
 +        logged = 0
 +
 +        def logger(loop, context):
 +            nonlocal logged
 +            self.assertIn('asyncgen', context)
 +            expected = 'an error occurred during closing of asynchronous'
 +            if expected in context['message']:
 +                logged += 1
 +
 +        async def waiter(timeout):
 +            try:
 +                await asyncio.sleep(timeout, loop=self.loop)
 +                yield 1
 +            finally:
 +                1 / 0
 +
 +        async def wait():
 +            async for _ in waiter(1):
 +                pass
 +
 +        t = self.loop.create_task(wait())
 +        self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
 +
 +        self.loop.set_exception_handler(logger)
 +        self.loop.run_until_complete(self.loop.shutdown_asyncgens())
 +
 +        self.assertEqual(logged, 1)
 +
 +        # Silence warnings
 +        t.cancel()
 +        self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop))
 +
 +if __name__ == "__main__":
 +    unittest.main()
index f2839a719a3ab766faec01c17672ee0bf51f932f,4a327b5ba9f16835e6a0de313ebcf461bb153b0a..50e439af45871e65908b0ac424fb0500925267f5
@@@ -1665,185 -1552,52 +1680,231 @@@ class CoroutineTest(unittest.TestCase)
                  warnings.simplefilter("error")
                  run_async(foo())
  
+     def test_for_tuple(self):
+         class Done(Exception): pass
+         class AIter(tuple):
+             i = 0
+             def __aiter__(self):
+                 return self
+             async def __anext__(self):
+                 if self.i >= len(self):
+                     raise StopAsyncIteration
+                 self.i += 1
+                 return self[self.i - 1]
+         result = []
+         async def foo():
+             async for i in AIter([42]):
+                 result.append(i)
+             raise Done
+         with self.assertRaises(Done):
+             foo().send(None)
+         self.assertEqual(result, [42])
+     def test_for_stop_iteration(self):
+         class Done(Exception): pass
+         class AIter(StopIteration):
+             i = 0
+             def __aiter__(self):
+                 return self
+             async def __anext__(self):
+                 if self.i:
+                     raise StopAsyncIteration
+                 self.i += 1
+                 return self.value
+         result = []
+         async def foo():
+             async for i in AIter(42):
+                 result.append(i)
+             raise Done
+         with self.assertRaises(Done):
+             foo().send(None)
+         self.assertEqual(result, [42])
 +    def test_comp_1(self):
 +        async def f(i):
 +            return i
 +
 +        async def run_list():
 +            return [await c for c in [f(1), f(41)]]
 +
 +        async def run_set():
 +            return {await c for c in [f(1), f(41)]}
 +
 +        async def run_dict1():
 +            return {await c: 'a' for c in [f(1), f(41)]}
 +
 +        async def run_dict2():
 +            return {i: await c for i, c in enumerate([f(1), f(41)])}
 +
 +        self.assertEqual(run_async(run_list()), ([], [1, 41]))
 +        self.assertEqual(run_async(run_set()), ([], {1, 41}))
 +        self.assertEqual(run_async(run_dict1()), ([], {1: 'a', 41: 'a'}))
 +        self.assertEqual(run_async(run_dict2()), ([], {0: 1, 1: 41}))
 +
 +    def test_comp_2(self):
 +        async def f(i):
 +            return i
 +
 +        async def run_list():
 +            return [s for c in [f(''), f('abc'), f(''), f(['de', 'fg'])]
 +                    for s in await c]
 +
 +        self.assertEqual(
 +            run_async(run_list()),
 +            ([], ['a', 'b', 'c', 'de', 'fg']))
 +
 +        async def run_set():
 +            return {d
 +                    for c in [f([f([10, 30]),
 +                                 f([20])])]
 +                    for s in await c
 +                    for d in await s}
 +
 +        self.assertEqual(
 +            run_async(run_set()),
 +            ([], {10, 20, 30}))
 +
 +        async def run_set2():
 +            return {await s
 +                    for c in [f([f(10), f(20)])]
 +                    for s in await c}
 +
 +        self.assertEqual(
 +            run_async(run_set2()),
 +            ([], {10, 20}))
 +
 +    def test_comp_3(self):
 +        async def f(it):
 +            for i in it:
 +                yield i
 +
 +        async def run_list():
 +            return [i + 1 async for i in f([10, 20])]
 +        self.assertEqual(
 +            run_async(run_list()),
 +            ([], [11, 21]))
 +
 +        async def run_set():
 +            return {i + 1 async for i in f([10, 20])}
 +        self.assertEqual(
 +            run_async(run_set()),
 +            ([], {11, 21}))
 +
 +        async def run_dict():
 +            return {i + 1: i + 2 async for i in f([10, 20])}
 +        self.assertEqual(
 +            run_async(run_dict()),
 +            ([], {11: 12, 21: 22}))
 +
 +        async def run_gen():
 +            gen = (i + 1 async for i in f([10, 20]))
 +            return [g + 100 async for g in gen]
 +        self.assertEqual(
 +            run_async(run_gen()),
 +            ([], [111, 121]))
 +
 +    def test_comp_4(self):
 +        async def f(it):
 +            for i in it:
 +                yield i
 +
 +        async def run_list():
 +            return [i + 1 async for i in f([10, 20]) if i > 10]
 +        self.assertEqual(
 +            run_async(run_list()),
 +            ([], [21]))
 +
 +        async def run_set():
 +            return {i + 1 async for i in f([10, 20]) if i > 10}
 +        self.assertEqual(
 +            run_async(run_set()),
 +            ([], {21}))
 +
 +        async def run_dict():
 +            return {i + 1: i + 2 async for i in f([10, 20]) if i > 10}
 +        self.assertEqual(
 +            run_async(run_dict()),
 +            ([], {21: 22}))
 +
 +        async def run_gen():
 +            gen = (i + 1 async for i in f([10, 20]) if i > 10)
 +            return [g + 100 async for g in gen]
 +        self.assertEqual(
 +            run_async(run_gen()),
 +            ([], [121]))
 +
 +    def test_comp_5(self):
 +        async def f(it):
 +            for i in it:
 +                yield i
 +
 +        async def run_list():
 +            return [i + 1 for pair in ([10, 20], [30, 40]) if pair[0] > 10
 +                    async for i in f(pair) if i > 30]
 +        self.assertEqual(
 +            run_async(run_list()),
 +            ([], [41]))
 +
 +    def test_comp_6(self):
 +        async def f(it):
 +            for i in it:
 +                yield i
 +
 +        async def run_list():
 +            return [i + 1 async for seq in f([(10, 20), (30,)])
 +                    for i in seq]
 +
 +        self.assertEqual(
 +            run_async(run_list()),
 +            ([], [11, 21, 31]))
 +
 +    def test_comp_7(self):
 +        async def f():
 +            yield 1
 +            yield 2
 +            raise Exception('aaa')
 +
 +        async def run_list():
 +            return [i async for i in f()]
 +
 +        with self.assertRaisesRegex(Exception, 'aaa'):
 +            run_async(run_list())
 +
 +    def test_comp_8(self):
 +        async def f():
 +            return [i for i in [1, 2, 3]]
 +
 +        self.assertEqual(
 +            run_async(f()),
 +            ([], [1, 2, 3]))
 +
 +    def test_comp_9(self):
 +        async def gen():
 +            yield 1
 +            yield 2
 +        async def f():
 +            l = [i async for i in gen()]
 +            return [i for i in l]
 +
 +        self.assertEqual(
 +            run_async(f()),
 +            ([], [1, 2]))
 +
 +    def test_comp_10(self):
 +        async def f():
 +            xx = {i for i in [1, 2, 3]}
 +            return {x: x for x in xx}
 +
 +        self.assertEqual(
 +            run_async(f()),
 +            ([], {1: 1, 2: 2, 3: 3}))
 +
      def test_copy(self):
          async def func(): pass
          coro = func()
Simple merge
index 23ffbed4472887b47738ec8bcfb6c9b33f13b97c,23ffbed4472887b47738ec8bcfb6c9b33f13b97c..7e9711eaf5c3cf4606987aa7c8bbcc92a0d1582a
@@@ -384,9 -384,9 +384,10 @@@ class TestPEP380Operation(unittest.Test
              trace.append("Starting g1")
              yield "g1 ham"
              ret = yield from g2()
--            trace.append("g2 returned %s" % (ret,))
--            ret = yield from g2(42)
--            trace.append("g2 returned %s" % (ret,))
++            trace.append("g2 returned %r" % (ret,))
++            for v in 1, (2,), StopIteration(3):
++                ret = yield from g2(v)
++                trace.append("g2 returned %r" % (ret,))
              yield "g1 eggs"
              trace.append("Finishing g1")
          def g2(v = None):
              "Yielded g2 spam",
              "Yielded g2 more spam",
              "Finishing g2",
--            "g2 returned 42",
++            "g2 returned 1",
++            "Starting g2",
++            "Yielded g2 spam",
++            "Yielded g2 more spam",
++            "Finishing g2",
++            "g2 returned (2,)",
++            "Starting g2",
++            "Yielded g2 spam",
++            "Yielded g2 more spam",
++            "Finishing g2",
++            "g2 returned StopIteration(3,)",
              "Yielded g1 eggs",
              "Finishing g1",
          ])
                  next(gi)
                  trace.append("f SHOULD NOT BE HERE")
              except StopIteration as e:
--                trace.append("f caught %s" % (repr(e),))
++                trace.append("f caught %r" % (e,))
          def g(r):
              trace.append("g starting")
              yield
--            trace.append("g returning %s" % (r,))
++            trace.append("g returning %r" % (r,))
              return r
          f(None)
--        f(42)
++        f(1)
++        f((2,))
++        f(StopIteration(3))
          self.assertEqual(trace,[
              "g starting",
              "f resuming g",
              "f caught StopIteration()",
              "g starting",
              "f resuming g",
--            "g returning 42",
--            "f caught StopIteration(42,)",
++            "g returning 1",
++            "f caught StopIteration(1,)",
++            "g starting",
++            "f resuming g",
++            "g returning (2,)",
++            "f caught StopIteration((2,),)",
++            "g starting",
++            "f resuming g",
++            "g returning StopIteration(3,)",
++            "f caught StopIteration(StopIteration(3,),)",
          ])
  
      def test_send_and_return_with_value(self):
          def g(r):
              trace.append("g starting")
              x = yield
--            trace.append("g received %s" % (x,))
--            trace.append("g returning %s" % (r,))
++            trace.append("g received %r" % (x,))
++            trace.append("g returning %r" % (r,))
              return r
          f(None)
--        f(42)
--        self.assertEqual(trace,[
++        f(1)
++        f((2,))
++        f(StopIteration(3))
++        self.assertEqual(trace, [
              "g starting",
              "f sending spam to g",
--            "g received spam",
++            "g received 'spam'",
              "g returning None",
              "f caught StopIteration()",
              "g starting",
              "f sending spam to g",
--            "g received spam",
--            "g returning 42",
--            "f caught StopIteration(42,)",
++            "g received 'spam'",
++            "g returning 1",
++            'f caught StopIteration(1,)',
++            'g starting',
++            'f sending spam to g',
++            "g received 'spam'",
++            'g returning (2,)',
++            'f caught StopIteration((2,),)',
++            'g starting',
++            'f sending spam to g',
++            "g received 'spam'",
++            'g returning StopIteration(3,)',
++            'f caught StopIteration(StopIteration(3,),)'
          ])
  
      def test_catching_exception_from_subgen_and_returning(self):
          Test catching an exception thrown into a
          subgenerator and returning a value
          """
--        trace = []
          def inner():
              try:
                  yield 1
              except ValueError:
                  trace.append("inner caught ValueError")
--            return 2
++            return value
  
          def outer():
              v = yield from inner()
--            trace.append("inner returned %r to outer" % v)
++            trace.append("inner returned %r to outer" % (v,))
              yield v
--        g = outer()
--        trace.append(next(g))
--        trace.append(g.throw(ValueError))
--        self.assertEqual(trace,[
--            1,
--            "inner caught ValueError",
--            "inner returned 2 to outer",
--            2,
--        ])
++
++        for value in 2, (2,), StopIteration(2):
++            trace = []
++            g = outer()
++            trace.append(next(g))
++            trace.append(repr(g.throw(ValueError)))
++            self.assertEqual(trace, [
++                1,
++                "inner caught ValueError",
++                "inner returned %r to outer" % (value,),
++                repr(value),
++            ])
  
      def test_throwing_GeneratorExit_into_subgen_that_returns(self):
          """
index 18dd37b5a8b9a9c324f3c40975f8607be5185e7a,0000000000000000000000000000000000000000..7df6fa5008b0a78d8ed6c07f6627a3d3d55d30fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,2460 -1,0 +1,2446 @@@
-         /* The result of the Future is not an exception.
-            We construct an exception instance manually with
-            PyObject_CallFunctionObjArgs and pass it to PyErr_SetObject
-            (similarly to what genobject.c does).
-            We do this to handle a situation when "res" is a tuple, in which
-            case PyErr_SetObject would set the value of StopIteration to
-            the first element of the tuple.
-            (See PyErr_SetObject/_PyErr_CreateException code for details.)
-         */
-         PyObject *e = PyObject_CallFunctionObjArgs(
-             PyExc_StopIteration, res, NULL);
-         Py_DECREF(res);
-         if (e == NULL) {
 +#include "Python.h"
 +#include "structmember.h"
 +
 +
 +/*[clinic input]
 +module _asyncio
 +[clinic start generated code]*/
 +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=8fd17862aa989c69]*/
 +
 +
 +/* identifiers used from some functions */
 +_Py_IDENTIFIER(add_done_callback);
 +_Py_IDENTIFIER(call_soon);
 +_Py_IDENTIFIER(cancel);
 +_Py_IDENTIFIER(send);
 +_Py_IDENTIFIER(throw);
 +_Py_IDENTIFIER(_step);
 +_Py_IDENTIFIER(_schedule_callbacks);
 +_Py_IDENTIFIER(_wakeup);
 +
 +
 +/* State of the _asyncio module */
 +static PyObject *all_tasks;
 +static PyObject *current_tasks;
 +static PyObject *traceback_extract_stack;
 +static PyObject *asyncio_get_event_loop;
 +static PyObject *asyncio_future_repr_info_func;
 +static PyObject *asyncio_task_repr_info_func;
 +static PyObject *asyncio_task_get_stack_func;
 +static PyObject *asyncio_task_print_stack_func;
 +static PyObject *asyncio_InvalidStateError;
 +static PyObject *asyncio_CancelledError;
 +static PyObject *inspect_isgenerator;
 +
 +
 +typedef enum {
 +    STATE_PENDING,
 +    STATE_CANCELLED,
 +    STATE_FINISHED
 +} fut_state;
 +
 +#define FutureObj_HEAD(prefix)                                              \
 +    PyObject_HEAD                                                           \
 +    PyObject *prefix##_loop;                                                \
 +    PyObject *prefix##_callbacks;                                           \
 +    PyObject *prefix##_exception;                                           \
 +    PyObject *prefix##_result;                                              \
 +    PyObject *prefix##_source_tb;                                           \
 +    fut_state prefix##_state;                                               \
 +    int prefix##_log_tb;                                                    \
 +    int prefix##_blocking;                                                  \
 +    PyObject *dict;                                                         \
 +    PyObject *prefix##_weakreflist;
 +
 +typedef struct {
 +    FutureObj_HEAD(fut)
 +} FutureObj;
 +
 +typedef struct {
 +    FutureObj_HEAD(task)
 +    PyObject *task_fut_waiter;
 +    PyObject *task_coro;
 +    int task_must_cancel;
 +    int task_log_destroy_pending;
 +} TaskObj;
 +
 +typedef struct {
 +    PyObject_HEAD
 +    TaskObj *sw_task;
 +    PyObject *sw_arg;
 +} TaskSendMethWrapper;
 +
 +typedef struct {
 +    PyObject_HEAD
 +    TaskObj *ww_task;
 +} TaskWakeupMethWrapper;
 +
 +
 +#include "clinic/_asynciomodule.c.h"
 +
 +
 +/*[clinic input]
 +class _asyncio.Future "FutureObj *" "&Future_Type"
 +[clinic start generated code]*/
 +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=00d3e4abca711e0f]*/
 +
 +/* Get FutureIter from Future */
 +static PyObject* future_new_iter(PyObject *);
 +static inline int future_call_schedule_callbacks(FutureObj *);
 +
 +static int
 +future_schedule_callbacks(FutureObj *fut)
 +{
 +    Py_ssize_t len;
 +    PyObject* iters;
 +    int i;
 +
 +    if (fut->fut_callbacks == NULL) {
 +        PyErr_SetString(PyExc_RuntimeError, "NULL callbacks");
 +        return -1;
 +    }
 +
 +    len = PyList_GET_SIZE(fut->fut_callbacks);
 +    if (len == 0) {
 +        return 0;
 +    }
 +
 +    iters = PyList_GetSlice(fut->fut_callbacks, 0, len);
 +    if (iters == NULL) {
 +        return -1;
 +    }
 +    if (PyList_SetSlice(fut->fut_callbacks, 0, len, NULL) < 0) {
 +        Py_DECREF(iters);
 +        return -1;
 +    }
 +
 +    for (i = 0; i < len; i++) {
 +        PyObject *handle = NULL;
 +        PyObject *cb = PyList_GET_ITEM(iters, i);
 +
 +        handle = _PyObject_CallMethodId(
 +            fut->fut_loop, &PyId_call_soon, "OO", cb, fut, NULL);
 +
 +        if (handle == NULL) {
 +            Py_DECREF(iters);
 +            return -1;
 +        }
 +        else {
 +            Py_DECREF(handle);
 +        }
 +    }
 +
 +    Py_DECREF(iters);
 +    return 0;
 +}
 +
 +static int
 +future_init(FutureObj *fut, PyObject *loop)
 +{
 +    PyObject *res = NULL;
 +    _Py_IDENTIFIER(get_debug);
 +
 +    if (loop == NULL || loop == Py_None) {
 +        loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
 +        if (loop == NULL) {
 +            return -1;
 +        }
 +    }
 +    else {
 +        Py_INCREF(loop);
 +    }
 +    Py_CLEAR(fut->fut_loop);
 +    fut->fut_loop = loop;
 +
 +    res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, NULL);
 +    if (res == NULL) {
 +        return -1;
 +    }
 +    if (PyObject_IsTrue(res)) {
 +        Py_CLEAR(res);
 +        fut->fut_source_tb = PyObject_CallObject(traceback_extract_stack, NULL);
 +        if (fut->fut_source_tb == NULL) {
 +            return -1;
 +        }
 +    }
 +    else {
 +        Py_CLEAR(res);
 +    }
 +
 +    fut->fut_callbacks = PyList_New(0);
 +    if (fut->fut_callbacks == NULL) {
 +        return -1;
 +    }
 +
 +    return 0;
 +}
 +
 +static PyObject *
 +future_set_result(FutureObj *fut, PyObject *res)
 +{
 +    if (fut->fut_state != STATE_PENDING) {
 +        PyErr_SetString(asyncio_InvalidStateError, "invalid state");
 +        return NULL;
 +    }
 +
 +    Py_INCREF(res);
 +    fut->fut_result = res;
 +    fut->fut_state = STATE_FINISHED;
 +
 +    if (future_call_schedule_callbacks(fut) == -1) {
 +        return NULL;
 +    }
 +    Py_RETURN_NONE;
 +}
 +
 +static PyObject *
 +future_set_exception(FutureObj *fut, PyObject *exc)
 +{
 +    PyObject *exc_val = NULL;
 +
 +    if (fut->fut_state != STATE_PENDING) {
 +        PyErr_SetString(asyncio_InvalidStateError, "invalid state");
 +        return NULL;
 +    }
 +
 +    if (PyExceptionClass_Check(exc)) {
 +        exc_val = PyObject_CallObject(exc, NULL);
 +        if (exc_val == NULL) {
 +            return NULL;
 +        }
 +    }
 +    else {
 +        exc_val = exc;
 +        Py_INCREF(exc_val);
 +    }
 +    if (!PyExceptionInstance_Check(exc_val)) {
 +        Py_DECREF(exc_val);
 +        PyErr_SetString(PyExc_TypeError, "invalid exception object");
 +        return NULL;
 +    }
 +    if ((PyObject*)Py_TYPE(exc_val) == PyExc_StopIteration) {
 +        Py_DECREF(exc_val);
 +        PyErr_SetString(PyExc_TypeError,
 +                        "StopIteration interacts badly with generators "
 +                        "and cannot be raised into a Future");
 +        return NULL;
 +    }
 +
 +    fut->fut_exception = exc_val;
 +    fut->fut_state = STATE_FINISHED;
 +
 +    if (future_call_schedule_callbacks(fut) == -1) {
 +        return NULL;
 +    }
 +
 +    fut->fut_log_tb = 1;
 +    Py_RETURN_NONE;
 +}
 +
 +static int
 +future_get_result(FutureObj *fut, PyObject **result)
 +{
 +    PyObject *exc;
 +
 +    if (fut->fut_state == STATE_CANCELLED) {
 +        exc = _PyObject_CallNoArg(asyncio_CancelledError);
 +        if (exc == NULL) {
 +            return -1;
 +        }
 +        *result = exc;
 +        return 1;
 +    }
 +
 +    if (fut->fut_state != STATE_FINISHED) {
 +        PyObject *msg = PyUnicode_FromString("Result is not ready.");
 +        if (msg == NULL) {
 +            return -1;
 +        }
 +
 +        exc = _PyObject_CallArg1(asyncio_InvalidStateError, msg);
 +        Py_DECREF(msg);
 +        if (exc == NULL) {
 +            return -1;
 +        }
 +
 +        *result = exc;
 +        return 1;
 +    }
 +
 +    fut->fut_log_tb = 0;
 +    if (fut->fut_exception != NULL) {
 +        Py_INCREF(fut->fut_exception);
 +        *result = fut->fut_exception;
 +        return 1;
 +    }
 +
 +    Py_INCREF(fut->fut_result);
 +    *result = fut->fut_result;
 +    return 0;
 +}
 +
 +static PyObject *
 +future_add_done_callback(FutureObj *fut, PyObject *arg)
 +{
 +    if (fut->fut_state != STATE_PENDING) {
 +        PyObject *handle = _PyObject_CallMethodId(
 +            fut->fut_loop, &PyId_call_soon, "OO", arg, fut, NULL);
 +
 +        if (handle == NULL) {
 +            return NULL;
 +        }
 +        else {
 +            Py_DECREF(handle);
 +        }
 +    }
 +    else {
 +        int err = PyList_Append(fut->fut_callbacks, arg);
 +        if (err != 0) {
 +            return NULL;
 +        }
 +    }
 +    Py_RETURN_NONE;
 +}
 +
 +static PyObject *
 +future_cancel(FutureObj *fut)
 +{
 +    if (fut->fut_state != STATE_PENDING) {
 +        Py_RETURN_FALSE;
 +    }
 +    fut->fut_state = STATE_CANCELLED;
 +
 +    if (future_call_schedule_callbacks(fut) == -1) {
 +        return NULL;
 +    }
 +
 +    Py_RETURN_TRUE;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.__init__
 +
 +    *
 +    loop: 'O' = NULL
 +
 +This class is *almost* compatible with concurrent.futures.Future.
 +
 +    Differences:
 +
 +    - result() and exception() do not take a timeout argument and
 +      raise an exception when the future isn't done yet.
 +
 +    - Callbacks registered with add_done_callback() are always called
 +      via the event loop's call_soon_threadsafe().
 +
 +    - This class is not compatible with the wait() and as_completed()
 +      methods in the concurrent.futures package.
 +[clinic start generated code]*/
 +
 +static int
 +_asyncio_Future___init___impl(FutureObj *self, PyObject *loop)
 +/*[clinic end generated code: output=9ed75799eaccb5d6 input=8e1681f23605be2d]*/
 +
 +{
 +    return future_init(self, loop);
 +}
 +
 +static int
 +FutureObj_clear(FutureObj *fut)
 +{
 +    Py_CLEAR(fut->fut_loop);
 +    Py_CLEAR(fut->fut_callbacks);
 +    Py_CLEAR(fut->fut_result);
 +    Py_CLEAR(fut->fut_exception);
 +    Py_CLEAR(fut->fut_source_tb);
 +    Py_CLEAR(fut->dict);
 +    return 0;
 +}
 +
 +static int
 +FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg)
 +{
 +    Py_VISIT(fut->fut_loop);
 +    Py_VISIT(fut->fut_callbacks);
 +    Py_VISIT(fut->fut_result);
 +    Py_VISIT(fut->fut_exception);
 +    Py_VISIT(fut->fut_source_tb);
 +    Py_VISIT(fut->dict);
 +    return 0;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.result
 +
 +Return the result this future represents.
 +
 +If the future has been cancelled, raises CancelledError.  If the
 +future's result isn't yet available, raises InvalidStateError.  If
 +the future is done and has an exception set, this exception is raised.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_result_impl(FutureObj *self)
 +/*[clinic end generated code: output=f35f940936a4b1e5 input=49ecf9cf5ec50dc5]*/
 +{
 +    PyObject *result;
 +    int res = future_get_result(self, &result);
 +
 +    if (res == -1) {
 +        return NULL;
 +    }
 +
 +    if (res == 0) {
 +        return result;
 +    }
 +
 +    assert(res == 1);
 +
 +    PyErr_SetObject(PyExceptionInstance_Class(result), result);
 +    Py_DECREF(result);
 +    return NULL;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.exception
 +
 +Return the exception that was set on this future.
 +
 +The exception (or None if no exception was set) is returned only if
 +the future is done.  If the future has been cancelled, raises
 +CancelledError.  If the future isn't done yet, raises
 +InvalidStateError.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_exception_impl(FutureObj *self)
 +/*[clinic end generated code: output=88b20d4f855e0710 input=733547a70c841c68]*/
 +{
 +    if (self->fut_state == STATE_CANCELLED) {
 +        PyErr_SetString(asyncio_CancelledError, "");
 +        return NULL;
 +    }
 +
 +    if (self->fut_state != STATE_FINISHED) {
 +        PyErr_SetString(asyncio_InvalidStateError, "Result is not ready.");
 +        return NULL;
 +    }
 +
 +    if (self->fut_exception != NULL) {
 +        self->fut_log_tb = 0;
 +        Py_INCREF(self->fut_exception);
 +        return self->fut_exception;
 +    }
 +
 +    Py_RETURN_NONE;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.set_result
 +
 +    res: 'O'
 +    /
 +
 +Mark the future done and set its result.
 +
 +If the future is already done when this method is called, raises
 +InvalidStateError.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_set_result(FutureObj *self, PyObject *res)
 +/*[clinic end generated code: output=a620abfc2796bfb6 input=8619565e0503357e]*/
 +{
 +    return future_set_result(self, res);
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.set_exception
 +
 +    exception: 'O'
 +    /
 +
 +Mark the future done and set an exception.
 +
 +If the future is already done when this method is called, raises
 +InvalidStateError.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_set_exception(FutureObj *self, PyObject *exception)
 +/*[clinic end generated code: output=f1c1b0cd321be360 input=1377dbe15e6ea186]*/
 +{
 +    return future_set_exception(self, exception);
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.add_done_callback
 +
 +    fn: 'O'
 +    /
 +
 +Add a callback to be run when the future becomes done.
 +
 +The callback is called with a single argument - the future object. If
 +the future is already done when this is called, the callback is
 +scheduled with call_soon.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_add_done_callback(FutureObj *self, PyObject *fn)
 +/*[clinic end generated code: output=819e09629b2ec2b5 input=8cce187e32cec6a8]*/
 +{
 +    return future_add_done_callback(self, fn);
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.remove_done_callback
 +
 +    fn: 'O'
 +    /
 +
 +Remove all instances of a callback from the "call when done" list.
 +
 +Returns the number of callbacks removed.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_remove_done_callback(FutureObj *self, PyObject *fn)
 +/*[clinic end generated code: output=5ab1fb52b24ef31f input=3fedb73e1409c31c]*/
 +{
 +    PyObject *newlist;
 +    Py_ssize_t len, i, j=0;
 +
 +    len = PyList_GET_SIZE(self->fut_callbacks);
 +    if (len == 0) {
 +        return PyLong_FromSsize_t(0);
 +    }
 +
 +    newlist = PyList_New(len);
 +    if (newlist == NULL) {
 +        return NULL;
 +    }
 +
 +    for (i = 0; i < len; i++) {
 +        int ret;
 +        PyObject *item = PyList_GET_ITEM(self->fut_callbacks, i);
 +
 +        if ((ret = PyObject_RichCompareBool(fn, item, Py_EQ)) < 0) {
 +            goto fail;
 +        }
 +        if (ret == 0) {
 +            Py_INCREF(item);
 +            PyList_SET_ITEM(newlist, j, item);
 +            j++;
 +        }
 +    }
 +
 +    if (PyList_SetSlice(newlist, j, len, NULL) < 0) {
 +        goto fail;
 +    }
 +    if (PyList_SetSlice(self->fut_callbacks, 0, len, newlist) < 0) {
 +        goto fail;
 +    }
 +    Py_DECREF(newlist);
 +    return PyLong_FromSsize_t(len - j);
 +
 +fail:
 +    Py_DECREF(newlist);
 +    return NULL;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.cancel
 +
 +Cancel the future and schedule callbacks.
 +
 +If the future is already done or cancelled, return False.  Otherwise,
 +change the future's state to cancelled, schedule the callbacks and
 +return True.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_cancel_impl(FutureObj *self)
 +/*[clinic end generated code: output=e45b932ba8bd68a1 input=515709a127995109]*/
 +{
 +    return future_cancel(self);
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.cancelled
 +
 +Return True if the future was cancelled.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_cancelled_impl(FutureObj *self)
 +/*[clinic end generated code: output=145197ced586357d input=943ab8b7b7b17e45]*/
 +{
 +    if (self->fut_state == STATE_CANCELLED) {
 +        Py_RETURN_TRUE;
 +    }
 +    else {
 +        Py_RETURN_FALSE;
 +    }
 +}
 +
 +/*[clinic input]
 +_asyncio.Future.done
 +
 +Return True if the future is done.
 +
 +Done means either that a result / exception are available, or that the
 +future was cancelled.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future_done_impl(FutureObj *self)
 +/*[clinic end generated code: output=244c5ac351145096 input=28d7b23fdb65d2ac]*/
 +{
 +    if (self->fut_state == STATE_PENDING) {
 +        Py_RETURN_FALSE;
 +    }
 +    else {
 +        Py_RETURN_TRUE;
 +    }
 +}
 +
 +static PyObject *
 +FutureObj_get_blocking(FutureObj *fut)
 +{
 +    if (fut->fut_blocking) {
 +        Py_RETURN_TRUE;
 +    }
 +    else {
 +        Py_RETURN_FALSE;
 +    }
 +}
 +
 +static int
 +FutureObj_set_blocking(FutureObj *fut, PyObject *val)
 +{
 +    int is_true = PyObject_IsTrue(val);
 +    if (is_true < 0) {
 +        return -1;
 +    }
 +    fut->fut_blocking = is_true;
 +    return 0;
 +}
 +
 +static PyObject *
 +FutureObj_get_log_traceback(FutureObj *fut)
 +{
 +    if (fut->fut_log_tb) {
 +        Py_RETURN_TRUE;
 +    }
 +    else {
 +        Py_RETURN_FALSE;
 +    }
 +}
 +
 +static PyObject *
 +FutureObj_get_loop(FutureObj *fut)
 +{
 +    if (fut->fut_loop == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    Py_INCREF(fut->fut_loop);
 +    return fut->fut_loop;
 +}
 +
 +static PyObject *
 +FutureObj_get_callbacks(FutureObj *fut)
 +{
 +    if (fut->fut_callbacks == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    Py_INCREF(fut->fut_callbacks);
 +    return fut->fut_callbacks;
 +}
 +
 +static PyObject *
 +FutureObj_get_result(FutureObj *fut)
 +{
 +    if (fut->fut_result == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    Py_INCREF(fut->fut_result);
 +    return fut->fut_result;
 +}
 +
 +static PyObject *
 +FutureObj_get_exception(FutureObj *fut)
 +{
 +    if (fut->fut_exception == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    Py_INCREF(fut->fut_exception);
 +    return fut->fut_exception;
 +}
 +
 +static PyObject *
 +FutureObj_get_source_traceback(FutureObj *fut)
 +{
 +    if (fut->fut_source_tb == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    Py_INCREF(fut->fut_source_tb);
 +    return fut->fut_source_tb;
 +}
 +
 +static PyObject *
 +FutureObj_get_state(FutureObj *fut)
 +{
 +    _Py_IDENTIFIER(PENDING);
 +    _Py_IDENTIFIER(CANCELLED);
 +    _Py_IDENTIFIER(FINISHED);
 +    PyObject *ret = NULL;
 +
 +    switch (fut->fut_state) {
 +    case STATE_PENDING:
 +        ret = _PyUnicode_FromId(&PyId_PENDING);
 +        break;
 +    case STATE_CANCELLED:
 +        ret = _PyUnicode_FromId(&PyId_CANCELLED);
 +        break;
 +    case STATE_FINISHED:
 +        ret = _PyUnicode_FromId(&PyId_FINISHED);
 +        break;
 +    default:
 +        assert (0);
 +    }
 +    Py_INCREF(ret);
 +    return ret;
 +}
 +
 +/*[clinic input]
 +_asyncio.Future._repr_info
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future__repr_info_impl(FutureObj *self)
 +/*[clinic end generated code: output=fa69e901bd176cfb input=f21504d8e2ae1ca2]*/
 +{
 +    return PyObject_CallFunctionObjArgs(
 +        asyncio_future_repr_info_func, self, NULL);
 +}
 +
 +/*[clinic input]
 +_asyncio.Future._schedule_callbacks
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Future__schedule_callbacks_impl(FutureObj *self)
 +/*[clinic end generated code: output=5e8958d89ea1c5dc input=4f5f295f263f4a88]*/
 +{
 +    int ret = future_schedule_callbacks(self);
 +    if (ret == -1) {
 +        return NULL;
 +    }
 +    Py_RETURN_NONE;
 +}
 +
 +static PyObject *
 +FutureObj_repr(FutureObj *fut)
 +{
 +    _Py_IDENTIFIER(_repr_info);
 +
 +    PyObject *_repr_info = _PyUnicode_FromId(&PyId__repr_info);  // borrowed
 +    if (_repr_info == NULL) {
 +        return NULL;
 +    }
 +
 +    PyObject *rinfo = PyObject_CallMethodObjArgs((PyObject*)fut, _repr_info,
 +                                                 NULL);
 +    if (rinfo == NULL) {
 +        return NULL;
 +    }
 +
 +    PyObject *sp = PyUnicode_FromString(" ");
 +    if (sp == NULL) {
 +        Py_DECREF(rinfo);
 +        return NULL;
 +    }
 +
 +    PyObject *rinfo_s = PyUnicode_Join(sp, rinfo);
 +    Py_DECREF(sp);
 +    Py_DECREF(rinfo);
 +    if (rinfo_s == NULL) {
 +        return NULL;
 +    }
 +
 +    PyObject *rstr = NULL;
 +    PyObject *type_name = PyObject_GetAttrString((PyObject*)Py_TYPE(fut),
 +                                                 "__name__");
 +    if (type_name != NULL) {
 +        rstr = PyUnicode_FromFormat("<%S %S>", type_name, rinfo_s);
 +        Py_DECREF(type_name);
 +    }
 +    Py_DECREF(rinfo_s);
 +    return rstr;
 +}
 +
 +static void
 +FutureObj_finalize(FutureObj *fut)
 +{
 +    _Py_IDENTIFIER(call_exception_handler);
 +    _Py_IDENTIFIER(message);
 +    _Py_IDENTIFIER(exception);
 +    _Py_IDENTIFIER(future);
 +    _Py_IDENTIFIER(source_traceback);
 +
 +    if (!fut->fut_log_tb) {
 +        return;
 +    }
 +    assert(fut->fut_exception != NULL);
 +    fut->fut_log_tb = 0;;
 +
 +    PyObject *error_type, *error_value, *error_traceback;
 +    /* Save the current exception, if any. */
 +    PyErr_Fetch(&error_type, &error_value, &error_traceback);
 +
 +    PyObject *context = NULL;
 +    PyObject *type_name = NULL;
 +    PyObject *message = NULL;
 +    PyObject *func = NULL;
 +    PyObject *res = NULL;
 +
 +    context = PyDict_New();
 +    if (context == NULL) {
 +        goto finally;
 +    }
 +
 +    type_name = PyObject_GetAttrString((PyObject*)Py_TYPE(fut), "__name__");
 +    if (type_name == NULL) {
 +        goto finally;
 +    }
 +
 +    message = PyUnicode_FromFormat(
 +        "%S exception was never retrieved", type_name);
 +    if (message == NULL) {
 +        goto finally;
 +    }
 +
 +    if (_PyDict_SetItemId(context, &PyId_message, message) < 0 ||
 +        _PyDict_SetItemId(context, &PyId_exception, fut->fut_exception) < 0 ||
 +        _PyDict_SetItemId(context, &PyId_future, (PyObject*)fut) < 0) {
 +        goto finally;
 +    }
 +    if (fut->fut_source_tb != NULL) {
 +        if (_PyDict_SetItemId(context, &PyId_source_traceback,
 +                              fut->fut_source_tb) < 0) {
 +            goto finally;
 +        }
 +    }
 +
 +    func = _PyObject_GetAttrId(fut->fut_loop, &PyId_call_exception_handler);
 +    if (func != NULL) {
 +        res = _PyObject_CallArg1(func, context);
 +        if (res == NULL) {
 +            PyErr_WriteUnraisable(func);
 +        }
 +    }
 +
 +finally:
 +    Py_CLEAR(context);
 +    Py_CLEAR(type_name);
 +    Py_CLEAR(message);
 +    Py_CLEAR(func);
 +    Py_CLEAR(res);
 +
 +    /* Restore the saved exception. */
 +    PyErr_Restore(error_type, error_value, error_traceback);
 +}
 +
 +
 +static PyAsyncMethods FutureType_as_async = {
 +    (unaryfunc)future_new_iter,         /* am_await */
 +    0,                                  /* am_aiter */
 +    0                                   /* am_anext */
 +};
 +
 +static PyMethodDef FutureType_methods[] = {
 +    _ASYNCIO_FUTURE_RESULT_METHODDEF
 +    _ASYNCIO_FUTURE_EXCEPTION_METHODDEF
 +    _ASYNCIO_FUTURE_SET_RESULT_METHODDEF
 +    _ASYNCIO_FUTURE_SET_EXCEPTION_METHODDEF
 +    _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF
 +    _ASYNCIO_FUTURE_REMOVE_DONE_CALLBACK_METHODDEF
 +    _ASYNCIO_FUTURE_CANCEL_METHODDEF
 +    _ASYNCIO_FUTURE_CANCELLED_METHODDEF
 +    _ASYNCIO_FUTURE_DONE_METHODDEF
 +    _ASYNCIO_FUTURE__REPR_INFO_METHODDEF
 +    _ASYNCIO_FUTURE__SCHEDULE_CALLBACKS_METHODDEF
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +#define FUTURE_COMMON_GETSETLIST                                              \
 +    {"_state", (getter)FutureObj_get_state, NULL, NULL},                      \
 +    {"_asyncio_future_blocking", (getter)FutureObj_get_blocking,              \
 +                                 (setter)FutureObj_set_blocking, NULL},       \
 +    {"_loop", (getter)FutureObj_get_loop, NULL, NULL},                        \
 +    {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL},              \
 +    {"_result", (getter)FutureObj_get_result, NULL, NULL},                    \
 +    {"_exception", (getter)FutureObj_get_exception, NULL, NULL},              \
 +    {"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL},      \
 +    {"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL},
 +
 +static PyGetSetDef FutureType_getsetlist[] = {
 +    FUTURE_COMMON_GETSETLIST
 +    {NULL} /* Sentinel */
 +};
 +
 +static void FutureObj_dealloc(PyObject *self);
 +
 +static PyTypeObject FutureType = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "_asyncio.Future",
 +    sizeof(FutureObj),                       /* tp_basicsize */
 +    .tp_dealloc = FutureObj_dealloc,
 +    .tp_as_async = &FutureType_as_async,
 +    .tp_repr = (reprfunc)FutureObj_repr,
 +    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
 +        | Py_TPFLAGS_HAVE_FINALIZE,
 +    .tp_doc = _asyncio_Future___init____doc__,
 +    .tp_traverse = (traverseproc)FutureObj_traverse,
 +    .tp_clear = (inquiry)FutureObj_clear,
 +    .tp_weaklistoffset = offsetof(FutureObj, fut_weakreflist),
 +    .tp_iter = (getiterfunc)future_new_iter,
 +    .tp_methods = FutureType_methods,
 +    .tp_getset = FutureType_getsetlist,
 +    .tp_dictoffset = offsetof(FutureObj, dict),
 +    .tp_init = (initproc)_asyncio_Future___init__,
 +    .tp_new = PyType_GenericNew,
 +    .tp_finalize = (destructor)FutureObj_finalize,
 +};
 +
 +#define Future_CheckExact(obj) (Py_TYPE(obj) == &FutureType)
 +
 +static inline int
 +future_call_schedule_callbacks(FutureObj *fut)
 +{
 +    if (Future_CheckExact(fut)) {
 +        return future_schedule_callbacks(fut);
 +    }
 +    else {
 +        /* `fut` is a subclass of Future */
 +        PyObject *ret = _PyObject_CallMethodId(
 +            (PyObject*)fut, &PyId__schedule_callbacks, NULL);
 +        if (ret == NULL) {
 +            return -1;
 +        }
 +
 +        Py_DECREF(ret);
 +        return 0;
 +    }
 +}
 +
 +static void
 +FutureObj_dealloc(PyObject *self)
 +{
 +    FutureObj *fut = (FutureObj *)self;
 +
 +    if (Future_CheckExact(fut)) {
 +        /* When fut is subclass of Future, finalizer is called from
 +         * subtype_dealloc.
 +         */
 +        if (PyObject_CallFinalizerFromDealloc(self) < 0) {
 +            // resurrected.
 +            return;
 +        }
 +    }
 +
 +    if (fut->fut_weakreflist != NULL) {
 +        PyObject_ClearWeakRefs(self);
 +    }
 +
 +    (void)FutureObj_clear(fut);
 +    Py_TYPE(fut)->tp_free(fut);
 +}
 +
 +
 +/*********************** Future Iterator **************************/
 +
 +typedef struct {
 +    PyObject_HEAD
 +    FutureObj *future;
 +} futureiterobject;
 +
 +static void
 +FutureIter_dealloc(futureiterobject *it)
 +{
 +    PyObject_GC_UnTrack(it);
 +    Py_XDECREF(it->future);
 +    PyObject_GC_Del(it);
 +}
 +
 +static PyObject *
 +FutureIter_iternext(futureiterobject *it)
 +{
 +    PyObject *res;
 +    FutureObj *fut = it->future;
 +
 +    if (fut == NULL) {
 +        return NULL;
 +    }
 +
 +    if (fut->fut_state == STATE_PENDING) {
 +        if (!fut->fut_blocking) {
 +            fut->fut_blocking = 1;
 +            Py_INCREF(fut);
 +            return (PyObject *)fut;
 +        }
 +        PyErr_Format(PyExc_AssertionError,
 +                     "yield from wasn't used with future");
 +        return NULL;
 +    }
 +
 +    res = _asyncio_Future_result_impl(fut);
 +    if (res != NULL) {
-         PyErr_SetObject(PyExc_StopIteration, e);
-         Py_DECREF(e);
++        /* The result of the Future is not an exception. */
++        if (_PyGen_SetStopIterationValue(res) < 0) {
++            Py_DECREF(res);
 +            return NULL;
 +        }
++        Py_DECREF(res);
 +    }
 +
 +    it->future = NULL;
 +    Py_DECREF(fut);
 +    return NULL;
 +}
 +
 +static PyObject *
 +FutureIter_send(futureiterobject *self, PyObject *unused)
 +{
 +    /* Future.__iter__ doesn't care about values that are pushed to the
 +     * generator, it just returns "self.result().
 +     */
 +    return FutureIter_iternext(self);
 +}
 +
 +static PyObject *
 +FutureIter_throw(futureiterobject *self, PyObject *args)
 +{
 +    PyObject *type=NULL, *val=NULL, *tb=NULL;
 +    if (!PyArg_ParseTuple(args, "O|OO", &type, &val, &tb))
 +        return NULL;
 +
 +    if (val == Py_None) {
 +        val = NULL;
 +    }
 +    if (tb == Py_None) {
 +        tb = NULL;
 +    }
 +
 +    Py_CLEAR(self->future);
 +
 +    if (tb != NULL) {
 +        PyErr_Restore(type, val, tb);
 +    }
 +    else if (val != NULL) {
 +        PyErr_SetObject(type, val);
 +    }
 +    else {
 +        if (PyExceptionClass_Check(type)) {
 +            val = PyObject_CallObject(type, NULL);
 +        }
 +        else {
 +            val = type;
 +            assert (PyExceptionInstance_Check(val));
 +            type = (PyObject*)Py_TYPE(val);
 +            assert (PyExceptionClass_Check(type));
 +        }
 +        PyErr_SetObject(type, val);
 +    }
 +    return FutureIter_iternext(self);
 +}
 +
 +static PyObject *
 +FutureIter_close(futureiterobject *self, PyObject *arg)
 +{
 +    Py_CLEAR(self->future);
 +    Py_RETURN_NONE;
 +}
 +
 +static int
 +FutureIter_traverse(futureiterobject *it, visitproc visit, void *arg)
 +{
 +    Py_VISIT(it->future);
 +    return 0;
 +}
 +
 +static PyMethodDef FutureIter_methods[] = {
 +    {"send",  (PyCFunction)FutureIter_send, METH_O, NULL},
 +    {"throw", (PyCFunction)FutureIter_throw, METH_VARARGS, NULL},
 +    {"close", (PyCFunction)FutureIter_close, METH_NOARGS, NULL},
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +static PyTypeObject FutureIterType = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "_asyncio.FutureIter",
 +    .tp_basicsize = sizeof(futureiterobject),
 +    .tp_itemsize = 0,
 +    .tp_dealloc = (destructor)FutureIter_dealloc,
 +    .tp_getattro = PyObject_GenericGetAttr,
 +    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
 +    .tp_traverse = (traverseproc)FutureIter_traverse,
 +    .tp_iter = PyObject_SelfIter,
 +    .tp_iternext = (iternextfunc)FutureIter_iternext,
 +    .tp_methods = FutureIter_methods,
 +};
 +
 +static PyObject *
 +future_new_iter(PyObject *fut)
 +{
 +    futureiterobject *it;
 +
 +    if (!PyObject_TypeCheck(fut, &FutureType)) {
 +        PyErr_BadInternalCall();
 +        return NULL;
 +    }
 +    it = PyObject_GC_New(futureiterobject, &FutureIterType);
 +    if (it == NULL) {
 +        return NULL;
 +    }
 +    Py_INCREF(fut);
 +    it->future = (FutureObj*)fut;
 +    PyObject_GC_Track(it);
 +    return (PyObject*)it;
 +}
 +
 +
 +/*********************** Task **************************/
 +
 +
 +/*[clinic input]
 +class _asyncio.Task "TaskObj *" "&Task_Type"
 +[clinic start generated code]*/
 +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=719dcef0fcc03b37]*/
 +
 +static int task_call_step_soon(TaskObj *, PyObject *);
 +static inline PyObject * task_call_wakeup(TaskObj *, PyObject *);
 +static inline PyObject * task_call_step(TaskObj *, PyObject *);
 +static PyObject * task_wakeup(TaskObj *, PyObject *);
 +static PyObject * task_step(TaskObj *, PyObject *);
 +
 +/* ----- Task._step wrapper */
 +
 +static int
 +TaskSendMethWrapper_clear(TaskSendMethWrapper *o)
 +{
 +    Py_CLEAR(o->sw_task);
 +    Py_CLEAR(o->sw_arg);
 +    return 0;
 +}
 +
 +static void
 +TaskSendMethWrapper_dealloc(TaskSendMethWrapper *o)
 +{
 +    PyObject_GC_UnTrack(o);
 +    (void)TaskSendMethWrapper_clear(o);
 +    Py_TYPE(o)->tp_free(o);
 +}
 +
 +static PyObject *
 +TaskSendMethWrapper_call(TaskSendMethWrapper *o,
 +                         PyObject *args, PyObject *kwds)
 +{
 +    return task_call_step(o->sw_task, o->sw_arg);
 +}
 +
 +static int
 +TaskSendMethWrapper_traverse(TaskSendMethWrapper *o,
 +                             visitproc visit, void *arg)
 +{
 +    Py_VISIT(o->sw_task);
 +    Py_VISIT(o->sw_arg);
 +    return 0;
 +}
 +
 +static PyObject *
 +TaskSendMethWrapper_get___self__(TaskSendMethWrapper *o)
 +{
 +    if (o->sw_task) {
 +        Py_INCREF(o->sw_task);
 +        return (PyObject*)o->sw_task;
 +    }
 +    Py_RETURN_NONE;
 +}
 +
 +static PyGetSetDef TaskSendMethWrapper_getsetlist[] = {
 +    {"__self__", (getter)TaskSendMethWrapper_get___self__, NULL, NULL},
 +    {NULL} /* Sentinel */
 +};
 +
 +PyTypeObject TaskSendMethWrapper_Type = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "TaskSendMethWrapper",
 +    .tp_basicsize = sizeof(TaskSendMethWrapper),
 +    .tp_itemsize = 0,
 +    .tp_getset = TaskSendMethWrapper_getsetlist,
 +    .tp_dealloc = (destructor)TaskSendMethWrapper_dealloc,
 +    .tp_call = (ternaryfunc)TaskSendMethWrapper_call,
 +    .tp_getattro = PyObject_GenericGetAttr,
 +    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
 +    .tp_traverse = (traverseproc)TaskSendMethWrapper_traverse,
 +    .tp_clear = (inquiry)TaskSendMethWrapper_clear,
 +};
 +
 +static PyObject *
 +TaskSendMethWrapper_new(TaskObj *task, PyObject *arg)
 +{
 +    TaskSendMethWrapper *o;
 +    o = PyObject_GC_New(TaskSendMethWrapper, &TaskSendMethWrapper_Type);
 +    if (o == NULL) {
 +        return NULL;
 +    }
 +
 +    Py_INCREF(task);
 +    o->sw_task = task;
 +
 +    Py_XINCREF(arg);
 +    o->sw_arg = arg;
 +
 +    PyObject_GC_Track(o);
 +    return (PyObject*) o;
 +}
 +
 +/* ----- Task._wakeup wrapper */
 +
 +static PyObject *
 +TaskWakeupMethWrapper_call(TaskWakeupMethWrapper *o,
 +                           PyObject *args, PyObject *kwds)
 +{
 +    PyObject *fut;
 +
 +    if (!PyArg_ParseTuple(args, "O|", &fut)) {
 +        return NULL;
 +    }
 +
 +    return task_call_wakeup(o->ww_task, fut);
 +}
 +
 +static int
 +TaskWakeupMethWrapper_clear(TaskWakeupMethWrapper *o)
 +{
 +    Py_CLEAR(o->ww_task);
 +    return 0;
 +}
 +
 +static int
 +TaskWakeupMethWrapper_traverse(TaskWakeupMethWrapper *o,
 +                               visitproc visit, void *arg)
 +{
 +    Py_VISIT(o->ww_task);
 +    return 0;
 +}
 +
 +static void
 +TaskWakeupMethWrapper_dealloc(TaskWakeupMethWrapper *o)
 +{
 +    PyObject_GC_UnTrack(o);
 +    (void)TaskWakeupMethWrapper_clear(o);
 +    Py_TYPE(o)->tp_free(o);
 +}
 +
 +PyTypeObject TaskWakeupMethWrapper_Type = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "TaskWakeupMethWrapper",
 +    .tp_basicsize = sizeof(TaskWakeupMethWrapper),
 +    .tp_itemsize = 0,
 +    .tp_dealloc = (destructor)TaskWakeupMethWrapper_dealloc,
 +    .tp_call = (ternaryfunc)TaskWakeupMethWrapper_call,
 +    .tp_getattro = PyObject_GenericGetAttr,
 +    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
 +    .tp_traverse = (traverseproc)TaskWakeupMethWrapper_traverse,
 +    .tp_clear = (inquiry)TaskWakeupMethWrapper_clear,
 +};
 +
 +static PyObject *
 +TaskWakeupMethWrapper_new(TaskObj *task)
 +{
 +    TaskWakeupMethWrapper *o;
 +    o = PyObject_GC_New(TaskWakeupMethWrapper, &TaskWakeupMethWrapper_Type);
 +    if (o == NULL) {
 +        return NULL;
 +    }
 +
 +    Py_INCREF(task);
 +    o->ww_task = task;
 +
 +    PyObject_GC_Track(o);
 +    return (PyObject*) o;
 +}
 +
 +/* ----- Task */
 +
 +/*[clinic input]
 +_asyncio.Task.__init__
 +
 +    coro: 'O'
 +    *
 +    loop: 'O' = NULL
 +
 +A coroutine wrapped in a Future.
 +[clinic start generated code]*/
 +
 +static int
 +_asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
 +/*[clinic end generated code: output=9f24774c2287fc2f input=71d8d28c201a18cd]*/
 +{
 +    PyObject *res;
 +    _Py_IDENTIFIER(add);
 +
 +    if (future_init((FutureObj*)self, loop)) {
 +        return -1;
 +    }
 +
 +    self->task_fut_waiter = NULL;
 +    self->task_must_cancel = 0;
 +    self->task_log_destroy_pending = 1;
 +
 +    Py_INCREF(coro);
 +    self->task_coro = coro;
 +
 +    if (task_call_step_soon(self, NULL)) {
 +        return -1;
 +    }
 +
 +    res = _PyObject_CallMethodId(all_tasks, &PyId_add, "O", self, NULL);
 +    if (res == NULL) {
 +        return -1;
 +    }
 +    Py_DECREF(res);
 +
 +    return 0;
 +}
 +
 +static int
 +TaskObj_clear(TaskObj *task)
 +{
 +    (void)FutureObj_clear((FutureObj*) task);
 +    Py_CLEAR(task->task_coro);
 +    Py_CLEAR(task->task_fut_waiter);
 +    return 0;
 +}
 +
 +static int
 +TaskObj_traverse(TaskObj *task, visitproc visit, void *arg)
 +{
 +    Py_VISIT(task->task_coro);
 +    Py_VISIT(task->task_fut_waiter);
 +    (void)FutureObj_traverse((FutureObj*) task, visit, arg);
 +    return 0;
 +}
 +
 +static PyObject *
 +TaskObj_get_log_destroy_pending(TaskObj *task)
 +{
 +    if (task->task_log_destroy_pending) {
 +        Py_RETURN_TRUE;
 +    }
 +    else {
 +        Py_RETURN_FALSE;
 +    }
 +}
 +
 +static int
 +TaskObj_set_log_destroy_pending(TaskObj *task, PyObject *val)
 +{
 +    int is_true = PyObject_IsTrue(val);
 +    if (is_true < 0) {
 +        return -1;
 +    }
 +    task->task_log_destroy_pending = is_true;
 +    return 0;
 +}
 +
 +static PyObject *
 +TaskObj_get_must_cancel(TaskObj *task)
 +{
 +    if (task->task_must_cancel) {
 +        Py_RETURN_TRUE;
 +    }
 +    else {
 +        Py_RETURN_FALSE;
 +    }
 +}
 +
 +static PyObject *
 +TaskObj_get_coro(TaskObj *task)
 +{
 +    if (task->task_coro) {
 +        Py_INCREF(task->task_coro);
 +        return task->task_coro;
 +    }
 +
 +    Py_RETURN_NONE;
 +}
 +
 +static PyObject *
 +TaskObj_get_fut_waiter(TaskObj *task)
 +{
 +    if (task->task_fut_waiter) {
 +        Py_INCREF(task->task_fut_waiter);
 +        return task->task_fut_waiter;
 +    }
 +
 +    Py_RETURN_NONE;
 +}
 +
 +/*[clinic input]
 +@classmethod
 +_asyncio.Task.current_task
 +
 +    loop: 'O' = NULL
 +
 +Return the currently running task in an event loop or None.
 +
 +By default the current task for the current event loop is returned.
 +
 +None is returned when called not in the context of a Task.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task_current_task_impl(PyTypeObject *type, PyObject *loop)
 +/*[clinic end generated code: output=99fbe7332c516e03 input=cd784537f02cf833]*/
 +{
 +    PyObject *res;
 +
 +    if (loop == NULL) {
 +        loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
 +        if (loop == NULL) {
 +            return NULL;
 +        }
 +
 +        res = PyDict_GetItem(current_tasks, loop);
 +        Py_DECREF(loop);
 +    }
 +    else {
 +        res = PyDict_GetItem(current_tasks, loop);
 +    }
 +
 +    if (res == NULL) {
 +        Py_RETURN_NONE;
 +    }
 +    else {
 +        Py_INCREF(res);
 +        return res;
 +    }
 +}
 +
 +static PyObject *
 +task_all_tasks(PyObject *loop)
 +{
 +    PyObject *task;
 +    PyObject *task_loop;
 +    PyObject *set;
 +    PyObject *iter;
 +
 +    assert(loop != NULL);
 +
 +    set = PySet_New(NULL);
 +    if (set == NULL) {
 +        return NULL;
 +    }
 +
 +    iter = PyObject_GetIter(all_tasks);
 +    if (iter == NULL) {
 +        goto fail;
 +    }
 +
 +    while ((task = PyIter_Next(iter))) {
 +        task_loop = PyObject_GetAttrString(task, "_loop");
 +        if (task_loop == NULL) {
 +            Py_DECREF(task);
 +            goto fail;
 +        }
 +        if (task_loop == loop) {
 +            if (PySet_Add(set, task) == -1) {
 +                Py_DECREF(task_loop);
 +                Py_DECREF(task);
 +                goto fail;
 +            }
 +        }
 +        Py_DECREF(task_loop);
 +        Py_DECREF(task);
 +    }
 +
 +    Py_DECREF(iter);
 +    return set;
 +
 +fail:
 +    Py_XDECREF(set);
 +    Py_XDECREF(iter);
 +    return NULL;
 +}
 +
 +/*[clinic input]
 +@classmethod
 +_asyncio.Task.all_tasks
 +
 +    loop: 'O' = NULL
 +
 +Return a set of all tasks for an event loop.
 +
 +By default all tasks for the current event loop are returned.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task_all_tasks_impl(PyTypeObject *type, PyObject *loop)
 +/*[clinic end generated code: output=11f9b20749ccca5d input=cd64aa5f88bd5c49]*/
 +{
 +    PyObject *res;
 +
 +    if (loop == NULL) {
 +        loop = PyObject_CallObject(asyncio_get_event_loop, NULL);
 +        if (loop == NULL) {
 +            return NULL;
 +        }
 +
 +        res = task_all_tasks(loop);
 +        Py_DECREF(loop);
 +    }
 +    else {
 +        res = task_all_tasks(loop);
 +    }
 +
 +    return res;
 +}
 +
 +/*[clinic input]
 +_asyncio.Task._repr_info
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task__repr_info_impl(TaskObj *self)
 +/*[clinic end generated code: output=6a490eb66d5ba34b input=3c6d051ed3ddec8b]*/
 +{
 +    return PyObject_CallFunctionObjArgs(
 +        asyncio_task_repr_info_func, self, NULL);
 +}
 +
 +/*[clinic input]
 +_asyncio.Task.cancel
 +
 +Request that this task cancel itself.
 +
 +This arranges for a CancelledError to be thrown into the
 +wrapped coroutine on the next cycle through the event loop.
 +The coroutine then has a chance to clean up or even deny
 +the request using try/except/finally.
 +
 +Unlike Future.cancel, this does not guarantee that the
 +task will be cancelled: the exception might be caught and
 +acted upon, delaying cancellation of the task or preventing
 +cancellation completely.  The task may also return a value or
 +raise a different exception.
 +
 +Immediately after this method is called, Task.cancelled() will
 +not return True (unless the task was already cancelled).  A
 +task will be marked as cancelled when the wrapped coroutine
 +terminates with a CancelledError exception (even if cancel()
 +was not called).
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task_cancel_impl(TaskObj *self)
 +/*[clinic end generated code: output=6bfc0479da9d5757 input=13f9bf496695cb52]*/
 +{
 +    if (self->task_state != STATE_PENDING) {
 +        Py_RETURN_FALSE;
 +    }
 +
 +    if (self->task_fut_waiter) {
 +        PyObject *res;
 +        int is_true;
 +
 +        res = _PyObject_CallMethodId(
 +            self->task_fut_waiter, &PyId_cancel, NULL);
 +        if (res == NULL) {
 +            return NULL;
 +        }
 +
 +        is_true = PyObject_IsTrue(res);
 +        Py_DECREF(res);
 +        if (is_true < 0) {
 +            return NULL;
 +        }
 +
 +        if (is_true) {
 +            Py_RETURN_TRUE;
 +        }
 +    }
 +
 +    self->task_must_cancel = 1;
 +    Py_RETURN_TRUE;
 +}
 +
 +/*[clinic input]
 +_asyncio.Task.get_stack
 +
 +    *
 +    limit: 'O' = None
 +
 +Return the list of stack frames for this task's coroutine.
 +
 +If the coroutine is not done, this returns the stack where it is
 +suspended.  If the coroutine has completed successfully or was
 +cancelled, this returns an empty list.  If the coroutine was
 +terminated by an exception, this returns the list of traceback
 +frames.
 +
 +The frames are always ordered from oldest to newest.
 +
 +The optional limit gives the maximum number of frames to
 +return; by default all available frames are returned.  Its
 +meaning differs depending on whether a stack or a traceback is
 +returned: the newest frames of a stack are returned, but the
 +oldest frames of a traceback are returned.  (This matches the
 +behavior of the traceback module.)
 +
 +For reasons beyond our control, only one stack frame is
 +returned for a suspended coroutine.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task_get_stack_impl(TaskObj *self, PyObject *limit)
 +/*[clinic end generated code: output=c9aeeeebd1e18118 input=b1920230a766d17a]*/
 +{
 +    return PyObject_CallFunctionObjArgs(
 +        asyncio_task_get_stack_func, self, limit, NULL);
 +}
 +
 +/*[clinic input]
 +_asyncio.Task.print_stack
 +
 +    *
 +    limit: 'O' = None
 +    file: 'O' = None
 +
 +Print the stack or traceback for this task's coroutine.
 +
 +This produces output similar to that of the traceback module,
 +for the frames retrieved by get_stack().  The limit argument
 +is passed to get_stack().  The file argument is an I/O stream
 +to which the output is written; by default output is written
 +to sys.stderr.
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task_print_stack_impl(TaskObj *self, PyObject *limit,
 +                               PyObject *file)
 +/*[clinic end generated code: output=7339e10314cd3f4d input=19f1e99ab5400bc3]*/
 +{
 +    return PyObject_CallFunctionObjArgs(
 +        asyncio_task_print_stack_func, self, limit, file, NULL);
 +}
 +
 +/*[clinic input]
 +_asyncio.Task._step
 +
 +    exc: 'O' = NULL
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task__step_impl(TaskObj *self, PyObject *exc)
 +/*[clinic end generated code: output=7ed23f0cefd5ae42 input=ada4b2324e5370af]*/
 +{
 +    return task_step(self, exc == Py_None ? NULL : exc);
 +}
 +
 +/*[clinic input]
 +_asyncio.Task._wakeup
 +
 +    fut: 'O'
 +[clinic start generated code]*/
 +
 +static PyObject *
 +_asyncio_Task__wakeup_impl(TaskObj *self, PyObject *fut)
 +/*[clinic end generated code: output=75cb341c760fd071 input=11ee4918a5bdbf21]*/
 +{
 +    return task_wakeup(self, fut);
 +}
 +
 +static void
 +TaskObj_finalize(TaskObj *task)
 +{
 +    _Py_IDENTIFIER(call_exception_handler);
 +    _Py_IDENTIFIER(task);
 +    _Py_IDENTIFIER(message);
 +    _Py_IDENTIFIER(source_traceback);
 +
 +    PyObject *message = NULL;
 +    PyObject *context = NULL;
 +    PyObject *func = NULL;
 +    PyObject *res = NULL;
 +
 +    PyObject *error_type, *error_value, *error_traceback;
 +
 +    if (task->task_state != STATE_PENDING || !task->task_log_destroy_pending) {
 +        goto done;
 +    }
 +
 +    /* Save the current exception, if any. */
 +    PyErr_Fetch(&error_type, &error_value, &error_traceback);
 +
 +    context = PyDict_New();
 +    if (context == NULL) {
 +        goto finally;
 +    }
 +
 +    message = PyUnicode_FromString("Task was destroyed but it is pending!");
 +    if (message == NULL) {
 +        goto finally;
 +    }
 +
 +    if (_PyDict_SetItemId(context, &PyId_message, message) < 0 ||
 +        _PyDict_SetItemId(context, &PyId_task, (PyObject*)task) < 0)
 +    {
 +        goto finally;
 +    }
 +
 +    if (task->task_source_tb != NULL) {
 +        if (_PyDict_SetItemId(context, &PyId_source_traceback,
 +                              task->task_source_tb) < 0)
 +        {
 +            goto finally;
 +        }
 +    }
 +
 +    func = _PyObject_GetAttrId(task->task_loop, &PyId_call_exception_handler);
 +    if (func != NULL) {
 +        res = _PyObject_CallArg1(func, context);
 +        if (res == NULL) {
 +            PyErr_WriteUnraisable(func);
 +        }
 +    }
 +
 +finally:
 +    Py_CLEAR(context);
 +    Py_CLEAR(message);
 +    Py_CLEAR(func);
 +    Py_CLEAR(res);
 +
 +    /* Restore the saved exception. */
 +    PyErr_Restore(error_type, error_value, error_traceback);
 +
 +done:
 +    FutureObj_finalize((FutureObj*)task);
 +}
 +
 +static void TaskObj_dealloc(PyObject *);  /* Needs Task_CheckExact */
 +
 +static PyMethodDef TaskType_methods[] = {
 +    _ASYNCIO_FUTURE_RESULT_METHODDEF
 +    _ASYNCIO_FUTURE_EXCEPTION_METHODDEF
 +    _ASYNCIO_FUTURE_SET_RESULT_METHODDEF
 +    _ASYNCIO_FUTURE_SET_EXCEPTION_METHODDEF
 +    _ASYNCIO_FUTURE_ADD_DONE_CALLBACK_METHODDEF
 +    _ASYNCIO_FUTURE_REMOVE_DONE_CALLBACK_METHODDEF
 +    _ASYNCIO_FUTURE_CANCELLED_METHODDEF
 +    _ASYNCIO_FUTURE_DONE_METHODDEF
 +    _ASYNCIO_TASK_CURRENT_TASK_METHODDEF
 +    _ASYNCIO_TASK_ALL_TASKS_METHODDEF
 +    _ASYNCIO_TASK_CANCEL_METHODDEF
 +    _ASYNCIO_TASK_GET_STACK_METHODDEF
 +    _ASYNCIO_TASK_PRINT_STACK_METHODDEF
 +    _ASYNCIO_TASK__WAKEUP_METHODDEF
 +    _ASYNCIO_TASK__STEP_METHODDEF
 +    _ASYNCIO_TASK__REPR_INFO_METHODDEF
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +static PyGetSetDef TaskType_getsetlist[] = {
 +    FUTURE_COMMON_GETSETLIST
 +    {"_log_destroy_pending", (getter)TaskObj_get_log_destroy_pending,
 +                             (setter)TaskObj_set_log_destroy_pending, NULL},
 +    {"_must_cancel", (getter)TaskObj_get_must_cancel, NULL, NULL},
 +    {"_coro", (getter)TaskObj_get_coro, NULL, NULL},
 +    {"_fut_waiter", (getter)TaskObj_get_fut_waiter, NULL, NULL},
 +    {NULL} /* Sentinel */
 +};
 +
 +static PyTypeObject TaskType = {
 +    PyVarObject_HEAD_INIT(NULL, 0)
 +    "_asyncio.Task",
 +    sizeof(TaskObj),                       /* tp_basicsize */
 +    .tp_base = &FutureType,
 +    .tp_dealloc = TaskObj_dealloc,
 +    .tp_as_async = &FutureType_as_async,
 +    .tp_repr = (reprfunc)FutureObj_repr,
 +    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE
 +        | Py_TPFLAGS_HAVE_FINALIZE,
 +    .tp_doc = _asyncio_Task___init____doc__,
 +    .tp_traverse = (traverseproc)TaskObj_traverse,
 +    .tp_clear = (inquiry)TaskObj_clear,
 +    .tp_weaklistoffset = offsetof(TaskObj, task_weakreflist),
 +    .tp_iter = (getiterfunc)future_new_iter,
 +    .tp_methods = TaskType_methods,
 +    .tp_getset = TaskType_getsetlist,
 +    .tp_dictoffset = offsetof(TaskObj, dict),
 +    .tp_init = (initproc)_asyncio_Task___init__,
 +    .tp_new = PyType_GenericNew,
 +    .tp_finalize = (destructor)TaskObj_finalize,
 +};
 +
 +#define Task_CheckExact(obj) (Py_TYPE(obj) == &TaskType)
 +
 +static void
 +TaskObj_dealloc(PyObject *self)
 +{
 +    TaskObj *task = (TaskObj *)self;
 +
 +    if (Task_CheckExact(self)) {
 +        /* When fut is subclass of Task, finalizer is called from
 +         * subtype_dealloc.
 +         */
 +        if (PyObject_CallFinalizerFromDealloc(self) < 0) {
 +            // resurrected.
 +            return;
 +        }
 +    }
 +
 +    if (task->task_weakreflist != NULL) {
 +        PyObject_ClearWeakRefs(self);
 +    }
 +
 +    (void)TaskObj_clear(task);
 +    Py_TYPE(task)->tp_free(task);
 +}
 +
 +static inline PyObject *
 +task_call_wakeup(TaskObj *task, PyObject *fut)
 +{
 +    if (Task_CheckExact(task)) {
 +        return task_wakeup(task, fut);
 +    }
 +    else {
 +        /* `task` is a subclass of Task */
 +        return _PyObject_CallMethodId(
 +            (PyObject*)task, &PyId__wakeup, "O", fut, NULL);
 +    }
 +}
 +
 +static inline PyObject *
 +task_call_step(TaskObj *task, PyObject *arg)
 +{
 +    if (Task_CheckExact(task)) {
 +        return task_step(task, arg);
 +    }
 +    else {
 +        /* `task` is a subclass of Task */
 +        if (arg == NULL) {
 +            arg = Py_None;
 +        }
 +        return _PyObject_CallMethodId(
 +            (PyObject*)task, &PyId__step, "O", arg, NULL);
 +    }
 +}
 +
 +static int
 +task_call_step_soon(TaskObj *task, PyObject *arg)
 +{
 +    PyObject *handle;
 +
 +    PyObject *cb = TaskSendMethWrapper_new(task, arg);
 +    if (cb == NULL) {
 +        return -1;
 +    }
 +
 +    handle = _PyObject_CallMethodId(
 +        task->task_loop, &PyId_call_soon, "O", cb, NULL);
 +    Py_DECREF(cb);
 +    if (handle == NULL) {
 +        return -1;
 +    }
 +
 +    Py_DECREF(handle);
 +    return 0;
 +}
 +
 +static PyObject *
 +task_set_error_soon(TaskObj *task, PyObject *et, const char *format, ...)
 +{
 +    PyObject* msg;
 +
 +    va_list vargs;
 +#ifdef HAVE_STDARG_PROTOTYPES
 +    va_start(vargs, format);
 +#else
 +    va_start(vargs);
 +#endif
 +    msg = PyUnicode_FromFormatV(format, vargs);
 +    va_end(vargs);
 +
 +    if (msg == NULL) {
 +        return NULL;
 +    }
 +
 +    PyObject *e = PyObject_CallFunctionObjArgs(et, msg, NULL);
 +    Py_DECREF(msg);
 +    if (e == NULL) {
 +        return NULL;
 +    }
 +
 +    if (task_call_step_soon(task, e) == -1) {
 +        Py_DECREF(e);
 +        return NULL;
 +    }
 +
 +    Py_DECREF(e);
 +    Py_RETURN_NONE;
 +}
 +
 +static PyObject *
 +task_step_impl(TaskObj *task, PyObject *exc)
 +{
 +    int res;
 +    int clear_exc = 0;
 +    PyObject *result = NULL;
 +    PyObject *coro = task->task_coro;
 +    PyObject *o;
 +
 +    if (task->task_state != STATE_PENDING) {
 +        PyErr_Format(PyExc_AssertionError,
 +                     "_step(): already done: %R %R",
 +                     task,
 +                     exc ? exc : Py_None);
 +        goto fail;
 +    }
 +
 +    if (task->task_must_cancel) {
 +        assert(exc != Py_None);
 +
 +        if (exc) {
 +            /* Check if exc is a CancelledError */
 +            res = PyObject_IsInstance(exc, asyncio_CancelledError);
 +            if (res == -1) {
 +                /* An error occurred, abort */
 +                goto fail;
 +            }
 +            if (res == 0) {
 +                /* exc is not CancelledError; reset it to NULL */
 +                exc = NULL;
 +            }
 +        }
 +
 +        if (!exc) {
 +            /* exc was not a CancelledError */
 +            exc = PyObject_CallFunctionObjArgs(asyncio_CancelledError, NULL);
 +            if (!exc) {
 +                goto fail;
 +            }
 +            clear_exc = 1;
 +        }
 +
 +        task->task_must_cancel = 0;
 +    }
 +
 +    Py_CLEAR(task->task_fut_waiter);
 +
 +    if (exc == NULL) {
 +        if (PyGen_CheckExact(coro) || PyCoro_CheckExact(coro)) {
 +            result = _PyGen_Send((PyGenObject*)coro, Py_None);
 +        }
 +        else {
 +            result = _PyObject_CallMethodIdObjArgs(
 +                coro, &PyId_send, Py_None, NULL);
 +        }
 +    }
 +    else {
 +        result = _PyObject_CallMethodIdObjArgs(
 +            coro, &PyId_throw, exc, NULL);
 +        if (clear_exc) {
 +            /* We created 'exc' during this call */
 +            Py_CLEAR(exc);
 +        }
 +    }
 +
 +    if (result == NULL) {
 +        PyObject *et, *ev, *tb;
 +
 +        if (_PyGen_FetchStopIterationValue(&o) == 0) {
 +            /* The error is StopIteration and that means that
 +               the underlying coroutine has resolved */
 +            PyObject *res = future_set_result((FutureObj*)task, o);
 +            Py_DECREF(o);
 +            if (res == NULL) {
 +                return NULL;
 +            }
 +            Py_DECREF(res);
 +            Py_RETURN_NONE;
 +        }
 +
 +        if (PyErr_ExceptionMatches(asyncio_CancelledError)) {
 +            /* CancelledError */
 +            PyErr_Clear();
 +            return future_cancel((FutureObj*)task);
 +        }
 +
 +        /* Some other exception; pop it and call Task.set_exception() */
 +        PyErr_Fetch(&et, &ev, &tb);
 +        assert(et);
 +        if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
 +            PyErr_NormalizeException(&et, &ev, &tb);
 +        }
 +        o = future_set_exception((FutureObj*)task, ev);
 +        if (!o) {
 +            /* An exception in Task.set_exception() */
 +            Py_XDECREF(et);
 +            Py_XDECREF(tb);
 +            Py_XDECREF(ev);
 +            goto fail;
 +        }
 +        assert(o == Py_None);
 +        Py_CLEAR(o);
 +
 +        if (!PyErr_GivenExceptionMatches(et, PyExc_Exception)) {
 +            /* We've got a BaseException; re-raise it */
 +            PyErr_Restore(et, ev, tb);
 +            goto fail;
 +        }
 +
 +        Py_XDECREF(et);
 +        Py_XDECREF(tb);
 +        Py_XDECREF(ev);
 +
 +        Py_RETURN_NONE;
 +    }
 +
 +    if (result == (PyObject*)task) {
 +        /* We have a task that wants to await on itself */
 +        goto self_await;
 +    }
 +
 +    /* Check if `result` is FutureObj or TaskObj (and not a subclass) */
 +    if (Future_CheckExact(result) || Task_CheckExact(result)) {
 +        PyObject *wrapper;
 +        PyObject *res;
 +        FutureObj *fut = (FutureObj*)result;
 +
 +        /* Check if `result` future is attached to a different loop */
 +        if (fut->fut_loop != task->task_loop) {
 +            goto different_loop;
 +        }
 +
 +        if (fut->fut_blocking) {
 +            fut->fut_blocking = 0;
 +
 +            /* result.add_done_callback(task._wakeup) */
 +            wrapper = TaskWakeupMethWrapper_new(task);
 +            if (wrapper == NULL) {
 +                goto fail;
 +            }
 +            res = future_add_done_callback((FutureObj*)result, wrapper);
 +            Py_DECREF(wrapper);
 +            if (res == NULL) {
 +                goto fail;
 +            }
 +            Py_DECREF(res);
 +
 +            /* task._fut_waiter = result */
 +            task->task_fut_waiter = result;  /* no incref is necessary */
 +
 +            if (task->task_must_cancel) {
 +                PyObject *r;
 +                r = future_cancel(fut);
 +                if (r == NULL) {
 +                    return NULL;
 +                }
 +                if (r == Py_True) {
 +                    task->task_must_cancel = 0;
 +                }
 +                Py_DECREF(r);
 +            }
 +
 +            Py_RETURN_NONE;
 +        }
 +        else {
 +            goto yield_insteadof_yf;
 +        }
 +    }
 +
 +    /* Check if `result` is a Future-compatible object */
 +    o = PyObject_GetAttrString(result, "_asyncio_future_blocking");
 +    if (o == NULL) {
 +        if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
 +            PyErr_Clear();
 +        }
 +        else {
 +            goto fail;
 +        }
 +    }
 +    else {
 +        if (o == Py_None) {
 +            Py_CLEAR(o);
 +        }
 +        else {
 +            /* `result` is a Future-compatible object */
 +            PyObject *wrapper;
 +            PyObject *res;
 +
 +            int blocking = PyObject_IsTrue(o);
 +            Py_CLEAR(o);
 +            if (blocking < 0) {
 +                goto fail;
 +            }
 +
 +            /* Check if `result` future is attached to a different loop */
 +            PyObject *oloop = PyObject_GetAttrString(result, "_loop");
 +            if (oloop == NULL) {
 +                goto fail;
 +            }
 +            if (oloop != task->task_loop) {
 +                Py_DECREF(oloop);
 +                goto different_loop;
 +            }
 +            else {
 +                Py_DECREF(oloop);
 +            }
 +
 +            if (blocking) {
 +                /* result._asyncio_future_blocking = False */
 +                if (PyObject_SetAttrString(
 +                        result, "_asyncio_future_blocking", Py_False) == -1) {
 +                    goto fail;
 +                }
 +
 +                /* result.add_done_callback(task._wakeup) */
 +                wrapper = TaskWakeupMethWrapper_new(task);
 +                if (wrapper == NULL) {
 +                    goto fail;
 +                }
 +                res = _PyObject_CallMethodId(
 +                    result, &PyId_add_done_callback, "O", wrapper, NULL);
 +                Py_DECREF(wrapper);
 +                if (res == NULL) {
 +                    goto fail;
 +                }
 +                Py_DECREF(res);
 +
 +                /* task._fut_waiter = result */
 +                task->task_fut_waiter = result;  /* no incref is necessary */
 +
 +                if (task->task_must_cancel) {
 +                    PyObject *r;
 +                    int is_true;
 +                    r = _PyObject_CallMethodId(result, &PyId_cancel, NULL);
 +                    if (r == NULL) {
 +                        return NULL;
 +                    }
 +                    is_true = PyObject_IsTrue(r);
 +                    Py_DECREF(r);
 +                    if (is_true < 0) {
 +                        return NULL;
 +                    }
 +                    else if (is_true) {
 +                        task->task_must_cancel = 0;
 +                    }
 +                }
 +
 +                Py_RETURN_NONE;
 +            }
 +            else {
 +                goto yield_insteadof_yf;
 +            }
 +        }
 +    }
 +
 +    /* Check if `result` is None */
 +    if (result == Py_None) {
 +        /* Bare yield relinquishes control for one event loop iteration. */
 +        if (task_call_step_soon(task, NULL)) {
 +            goto fail;
 +        }
 +        return result;
 +    }
 +
 +    /* Check if `result` is a generator */
 +    o = PyObject_CallFunctionObjArgs(inspect_isgenerator, result, NULL);
 +    if (o == NULL) {
 +        /* An exception in inspect.isgenerator */
 +        goto fail;
 +    }
 +    res = PyObject_IsTrue(o);
 +    Py_CLEAR(o);
 +    if (res == -1) {
 +        /* An exception while checking if 'val' is True */
 +        goto fail;
 +    }
 +    if (res == 1) {
 +        /* `result` is a generator */
 +        PyObject *ret;
 +        ret = task_set_error_soon(
 +            task, PyExc_RuntimeError,
 +            "yield was used instead of yield from for "
 +            "generator in task %R with %S", task, result);
 +        Py_DECREF(result);
 +        return ret;
 +    }
 +
 +    /* The `result` is none of the above */
 +    Py_DECREF(result);
 +    return task_set_error_soon(
 +        task, PyExc_RuntimeError, "Task got bad yield: %R", result);
 +
 +self_await:
 +    o = task_set_error_soon(
 +        task, PyExc_RuntimeError,
 +        "Task cannot await on itself: %R", task);
 +    Py_DECREF(result);
 +    return o;
 +
 +yield_insteadof_yf:
 +    o = task_set_error_soon(
 +        task, PyExc_RuntimeError,
 +        "yield was used instead of yield from "
 +        "in task %R with %R",
 +        task, result);
 +    Py_DECREF(result);
 +    return o;
 +
 +different_loop:
 +    o = task_set_error_soon(
 +        task, PyExc_RuntimeError,
 +        "Task %R got Future %R attached to a different loop",
 +        task, result);
 +    Py_DECREF(result);
 +    return o;
 +
 +fail:
 +    Py_XDECREF(result);
 +    return NULL;
 +}
 +
 +static PyObject *
 +task_step(TaskObj *task, PyObject *exc)
 +{
 +    PyObject *res;
 +    PyObject *ot;
 +
 +    if (PyDict_SetItem(current_tasks,
 +                       task->task_loop, (PyObject*)task) == -1)
 +    {
 +        return NULL;
 +    }
 +
 +    res = task_step_impl(task, exc);
 +
 +    if (res == NULL) {
 +        PyObject *et, *ev, *tb;
 +        PyErr_Fetch(&et, &ev, &tb);
 +        ot = _PyDict_Pop(current_tasks, task->task_loop, NULL);
 +        if (ot == NULL) {
 +            Py_XDECREF(et);
 +            Py_XDECREF(tb);
 +            Py_XDECREF(ev);
 +            return NULL;
 +        }
 +        Py_DECREF(ot);
 +        PyErr_Restore(et, ev, tb);
 +        return NULL;
 +    }
 +    else {
 +        ot = _PyDict_Pop(current_tasks, task->task_loop, NULL);
 +        if (ot == NULL) {
 +            Py_DECREF(res);
 +            return NULL;
 +        }
 +        else {
 +            Py_DECREF(ot);
 +            return res;
 +        }
 +    }
 +}
 +
 +static PyObject *
 +task_wakeup(TaskObj *task, PyObject *o)
 +{
 +    assert(o);
 +
 +    if (Future_CheckExact(o) || Task_CheckExact(o)) {
 +        PyObject *fut_result = NULL;
 +        int res = future_get_result((FutureObj*)o, &fut_result);
 +        PyObject *result;
 +
 +        switch(res) {
 +        case -1:
 +            assert(fut_result == NULL);
 +            return NULL;
 +        case 0:
 +            Py_DECREF(fut_result);
 +            return task_call_step(task, NULL);
 +        default:
 +            assert(res == 1);
 +            result = task_call_step(task, fut_result);
 +            Py_DECREF(fut_result);
 +            return result;
 +        }
 +    }
 +
 +    PyObject *fut_result = PyObject_CallMethod(o, "result", NULL);
 +    if (fut_result == NULL) {
 +        PyObject *et, *ev, *tb;
 +        PyObject *res;
 +
 +        PyErr_Fetch(&et, &ev, &tb);
 +        if (!ev || !PyObject_TypeCheck(ev, (PyTypeObject *) et)) {
 +            PyErr_NormalizeException(&et, &ev, &tb);
 +        }
 +
 +        res = task_call_step(task, ev);
 +
 +        Py_XDECREF(et);
 +        Py_XDECREF(tb);
 +        Py_XDECREF(ev);
 +
 +        return res;
 +    }
 +    else {
 +        Py_DECREF(fut_result);
 +        return task_call_step(task, NULL);
 +    }
 +}
 +
 +
 +/*********************** Module **************************/
 +
 +
 +static void
 +module_free(void *m)
 +{
 +    Py_CLEAR(current_tasks);
 +    Py_CLEAR(all_tasks);
 +    Py_CLEAR(traceback_extract_stack);
 +    Py_CLEAR(asyncio_get_event_loop);
 +    Py_CLEAR(asyncio_future_repr_info_func);
 +    Py_CLEAR(asyncio_task_repr_info_func);
 +    Py_CLEAR(asyncio_task_get_stack_func);
 +    Py_CLEAR(asyncio_task_print_stack_func);
 +    Py_CLEAR(asyncio_InvalidStateError);
 +    Py_CLEAR(asyncio_CancelledError);
 +    Py_CLEAR(inspect_isgenerator);
 +}
 +
 +static int
 +module_init(void)
 +{
 +    PyObject *module = NULL;
 +    PyObject *cls;
 +
 +#define WITH_MOD(NAME) \
 +    Py_CLEAR(module); \
 +    module = PyImport_ImportModule(NAME); \
 +    if (module == NULL) { \
 +        return -1; \
 +    }
 +
 +#define GET_MOD_ATTR(VAR, NAME) \
 +    VAR = PyObject_GetAttrString(module, NAME); \
 +    if (VAR == NULL) { \
 +        goto fail; \
 +    }
 +
 +    WITH_MOD("asyncio.events")
 +    GET_MOD_ATTR(asyncio_get_event_loop, "get_event_loop")
 +
 +    WITH_MOD("asyncio.base_futures")
 +    GET_MOD_ATTR(asyncio_future_repr_info_func, "_future_repr_info")
 +    GET_MOD_ATTR(asyncio_InvalidStateError, "InvalidStateError")
 +    GET_MOD_ATTR(asyncio_CancelledError, "CancelledError")
 +
 +    WITH_MOD("asyncio.base_tasks")
 +    GET_MOD_ATTR(asyncio_task_repr_info_func, "_task_repr_info")
 +    GET_MOD_ATTR(asyncio_task_get_stack_func, "_task_get_stack")
 +    GET_MOD_ATTR(asyncio_task_print_stack_func, "_task_print_stack")
 +
 +    WITH_MOD("inspect")
 +    GET_MOD_ATTR(inspect_isgenerator, "isgenerator")
 +
 +    WITH_MOD("traceback")
 +    GET_MOD_ATTR(traceback_extract_stack, "extract_stack")
 +
 +    WITH_MOD("weakref")
 +    GET_MOD_ATTR(cls, "WeakSet")
 +    all_tasks = PyObject_CallObject(cls, NULL);
 +    Py_CLEAR(cls);
 +    if (all_tasks == NULL) {
 +        goto fail;
 +    }
 +
 +    current_tasks = PyDict_New();
 +    if (current_tasks == NULL) {
 +        goto fail;
 +    }
 +
 +    Py_CLEAR(module);
 +    return 0;
 +
 +fail:
 +    Py_CLEAR(module);
 +    module_free(NULL);
 +    return -1;
 +
 +#undef WITH_MOD
 +#undef GET_MOD_ATTR
 +}
 +
 +PyDoc_STRVAR(module_doc, "Accelerator module for asyncio");
 +
 +static struct PyModuleDef _asynciomodule = {
 +    PyModuleDef_HEAD_INIT,      /* m_base */
 +    "_asyncio",                 /* m_name */
 +    module_doc,                 /* m_doc */
 +    -1,                         /* m_size */
 +    NULL,                       /* m_methods */
 +    NULL,                       /* m_slots */
 +    NULL,                       /* m_traverse */
 +    NULL,                       /* m_clear */
 +    (freefunc)module_free       /* m_free */
 +};
 +
 +
 +PyMODINIT_FUNC
 +PyInit__asyncio(void)
 +{
 +    if (module_init() < 0) {
 +        return NULL;
 +    }
 +    if (PyType_Ready(&FutureType) < 0) {
 +        return NULL;
 +    }
 +    if (PyType_Ready(&FutureIterType) < 0) {
 +        return NULL;
 +    }
 +    if (PyType_Ready(&TaskSendMethWrapper_Type) < 0) {
 +        return NULL;
 +    }
 +    if(PyType_Ready(&TaskWakeupMethWrapper_Type) < 0) {
 +        return NULL;
 +    }
 +    if (PyType_Ready(&TaskType) < 0) {
 +        return NULL;
 +    }
 +
 +    PyObject *m = PyModule_Create(&_asynciomodule);
 +    if (m == NULL) {
 +        return NULL;
 +    }
 +
 +    Py_INCREF(&FutureType);
 +    if (PyModule_AddObject(m, "Future", (PyObject *)&FutureType) < 0) {
 +        Py_DECREF(&FutureType);
 +        return NULL;
 +    }
 +
 +    Py_INCREF(&TaskType);
 +    if (PyModule_AddObject(m, "Task", (PyObject *)&TaskType) < 0) {
 +        Py_DECREF(&TaskType);
 +        return NULL;
 +    }
 +
 +    return m;
 +}
index 7bcf016c34cb3abf6e1cd5d1af28ad6a199a02ca,0d5d54fdbafcd4bbc87b7615122ca6906c3e0b51..70d6e1492a4e42141dc0757e2f8c04dcbad85856
@@@ -200,24 -152,9 +200,17 @@@ gen_send_ex(PyGenObject *gen, PyObject 
      if (result && f->f_stacktop == NULL) {
          if (result == Py_None) {
              /* Delay exception instantiation if we can */
 -            PyErr_SetNone(PyExc_StopIteration);
 -        } else {
 +            if (PyAsyncGen_CheckExact(gen)) {
 +                PyErr_SetNone(PyExc_StopAsyncIteration);
 +            }
 +            else {
 +                PyErr_SetNone(PyExc_StopIteration);
 +            }
 +        }
 +        else {
-             PyObject *e = PyObject_CallFunctionObjArgs(
-                                PyExc_StopIteration, result, NULL);
 +            /* Async generators cannot return anything but None */
 +            assert(!PyAsyncGen_CheckExact(gen));
-             if (e != NULL) {
-                 PyErr_SetObject(PyExc_StopIteration, e);
-                 Py_DECREF(e);
-             }
+             _PyGen_SetStopIterationValue(result);
          }
          Py_CLEAR(result);
      }
@@@ -1106,7 -1052,7 +1144,7 @@@ typedef struct 
  static PyObject *
  aiter_wrapper_iternext(PyAIterWrapper *aw)
  {
-     PyErr_SetObject(PyExc_StopIteration, aw->ags_aiter);
 -    _PyGen_SetStopIterationValue(aw->aw_aiter);
++    _PyGen_SetStopIterationValue(aw->ags_aiter);
      return NULL;
  }
  
@@@ -1187,813 -1133,3 +1225,805 @@@ _PyAIterWrapper_New(PyObject *aiter
      _PyObject_GC_TRACK(aw);
      return (PyObject *)aw;
  }
-         PyObject *e = PyObject_CallFunctionObjArgs(
-             PyExc_StopIteration,
-             ((_PyAsyncGenWrappedValue*)result)->agw_val,
-             NULL);
 +
 +
 +/* ========= Asynchronous Generators ========= */
 +
 +
 +typedef enum {
 +    AWAITABLE_STATE_INIT,   /* new awaitable, has not yet been iterated */
 +    AWAITABLE_STATE_ITER,   /* being iterated */
 +    AWAITABLE_STATE_CLOSED, /* closed */
 +} AwaitableState;
 +
 +
 +typedef struct {
 +    PyObject_HEAD
 +    PyAsyncGenObject *ags_gen;
 +
 +    /* Can be NULL, when in the __anext__() mode
 +       (equivalent of "asend(None)") */
 +    PyObject *ags_sendval;
 +
 +    AwaitableState ags_state;
 +} PyAsyncGenASend;
 +
 +
 +typedef struct {
 +    PyObject_HEAD
 +    PyAsyncGenObject *agt_gen;
 +
 +    /* Can be NULL, when in the "aclose()" mode
 +       (equivalent of "athrow(GeneratorExit)") */
 +    PyObject *agt_args;
 +
 +    AwaitableState agt_state;
 +} PyAsyncGenAThrow;
 +
 +
 +typedef struct {
 +    PyObject_HEAD
 +    PyObject *agw_val;
 +} _PyAsyncGenWrappedValue;
 +
 +
 +#ifndef _PyAsyncGen_MAXFREELIST
 +#define _PyAsyncGen_MAXFREELIST 80
 +#endif
 +
 +/* Freelists boost performance 6-10%; they also reduce memory
 +   fragmentation, as _PyAsyncGenWrappedValue and PyAsyncGenASend
 +   are short-living objects that are instantiated for every
 +   __anext__ call.
 +*/
 +
 +static _PyAsyncGenWrappedValue *ag_value_freelist[_PyAsyncGen_MAXFREELIST];
 +static int ag_value_freelist_free = 0;
 +
 +static PyAsyncGenASend *ag_asend_freelist[_PyAsyncGen_MAXFREELIST];
 +static int ag_asend_freelist_free = 0;
 +
 +#define _PyAsyncGenWrappedValue_CheckExact(o) \
 +                    (Py_TYPE(o) == &_PyAsyncGenWrappedValue_Type)
 +
 +#define PyAsyncGenASend_CheckExact(o) \
 +                    (Py_TYPE(o) == &_PyAsyncGenASend_Type)
 +
 +
 +static int
 +async_gen_traverse(PyAsyncGenObject *gen, visitproc visit, void *arg)
 +{
 +    Py_VISIT(gen->ag_finalizer);
 +    return gen_traverse((PyGenObject*)gen, visit, arg);
 +}
 +
 +
 +static PyObject *
 +async_gen_repr(PyAsyncGenObject *o)
 +{
 +    return PyUnicode_FromFormat("<async_generator object %S at %p>",
 +                                o->ag_qualname, o);
 +}
 +
 +
 +static int
 +async_gen_init_hooks(PyAsyncGenObject *o)
 +{
 +    PyThreadState *tstate;
 +    PyObject *finalizer;
 +    PyObject *firstiter;
 +
 +    if (o->ag_hooks_inited) {
 +        return 0;
 +    }
 +
 +    o->ag_hooks_inited = 1;
 +
 +    tstate = PyThreadState_GET();
 +
 +    finalizer = tstate->async_gen_finalizer;
 +    if (finalizer) {
 +        Py_INCREF(finalizer);
 +        o->ag_finalizer = finalizer;
 +    }
 +
 +    firstiter = tstate->async_gen_firstiter;
 +    if (firstiter) {
 +        PyObject *res;
 +
 +        Py_INCREF(firstiter);
 +        res = PyObject_CallFunction(firstiter, "O", o);
 +        Py_DECREF(firstiter);
 +        if (res == NULL) {
 +            return 1;
 +        }
 +        Py_DECREF(res);
 +    }
 +
 +    return 0;
 +}
 +
 +
 +static PyObject *
 +async_gen_anext(PyAsyncGenObject *o)
 +{
 +    if (async_gen_init_hooks(o)) {
 +        return NULL;
 +    }
 +    return async_gen_asend_new(o, NULL);
 +}
 +
 +
 +static PyObject *
 +async_gen_asend(PyAsyncGenObject *o, PyObject *arg)
 +{
 +    if (async_gen_init_hooks(o)) {
 +        return NULL;
 +    }
 +    return async_gen_asend_new(o, arg);
 +}
 +
 +
 +static PyObject *
 +async_gen_aclose(PyAsyncGenObject *o, PyObject *arg)
 +{
 +    if (async_gen_init_hooks(o)) {
 +        return NULL;
 +    }
 +    return async_gen_athrow_new(o, NULL);
 +}
 +
 +static PyObject *
 +async_gen_athrow(PyAsyncGenObject *o, PyObject *args)
 +{
 +    if (async_gen_init_hooks(o)) {
 +        return NULL;
 +    }
 +    return async_gen_athrow_new(o, args);
 +}
 +
 +
 +static PyGetSetDef async_gen_getsetlist[] = {
 +    {"__name__", (getter)gen_get_name, (setter)gen_set_name,
 +     PyDoc_STR("name of the async generator")},
 +    {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname,
 +     PyDoc_STR("qualified name of the async generator")},
 +    {"ag_await", (getter)coro_get_cr_await, NULL,
 +     PyDoc_STR("object being awaited on, or None")},
 +    {NULL} /* Sentinel */
 +};
 +
 +static PyMemberDef async_gen_memberlist[] = {
 +    {"ag_frame",   T_OBJECT, offsetof(PyAsyncGenObject, ag_frame),   READONLY},
 +    {"ag_running", T_BOOL,   offsetof(PyAsyncGenObject, ag_running), READONLY},
 +    {"ag_code",    T_OBJECT, offsetof(PyAsyncGenObject, ag_code),    READONLY},
 +    {NULL}      /* Sentinel */
 +};
 +
 +PyDoc_STRVAR(async_aclose_doc,
 +"aclose() -> raise GeneratorExit inside generator.");
 +
 +PyDoc_STRVAR(async_asend_doc,
 +"asend(v) -> send 'v' in generator.");
 +
 +PyDoc_STRVAR(async_athrow_doc,
 +"athrow(typ[,val[,tb]]) -> raise exception in generator.");
 +
 +static PyMethodDef async_gen_methods[] = {
 +    {"asend", (PyCFunction)async_gen_asend, METH_O, async_asend_doc},
 +    {"athrow",(PyCFunction)async_gen_athrow, METH_VARARGS, async_athrow_doc},
 +    {"aclose", (PyCFunction)async_gen_aclose, METH_NOARGS, async_aclose_doc},
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +
 +static PyAsyncMethods async_gen_as_async = {
 +    0,                                          /* am_await */
 +    PyObject_SelfIter,                          /* am_aiter */
 +    (unaryfunc)async_gen_anext                  /* am_anext */
 +};
 +
 +
 +PyTypeObject PyAsyncGen_Type = {
 +    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 +    "async_generator",                          /* tp_name */
 +    sizeof(PyAsyncGenObject),                   /* tp_basicsize */
 +    0,                                          /* tp_itemsize */
 +    /* methods */
 +    (destructor)gen_dealloc,                    /* tp_dealloc */
 +    0,                                          /* tp_print */
 +    0,                                          /* tp_getattr */
 +    0,                                          /* tp_setattr */
 +    &async_gen_as_async,                        /* tp_as_async */
 +    (reprfunc)async_gen_repr,                   /* tp_repr */
 +    0,                                          /* tp_as_number */
 +    0,                                          /* tp_as_sequence */
 +    0,                                          /* tp_as_mapping */
 +    0,                                          /* tp_hash */
 +    0,                                          /* tp_call */
 +    0,                                          /* tp_str */
 +    PyObject_GenericGetAttr,                    /* tp_getattro */
 +    0,                                          /* tp_setattro */
 +    0,                                          /* tp_as_buffer */
 +    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
 +        Py_TPFLAGS_HAVE_FINALIZE,               /* tp_flags */
 +    0,                                          /* tp_doc */
 +    (traverseproc)async_gen_traverse,           /* tp_traverse */
 +    0,                                          /* tp_clear */
 +    0,                                          /* tp_richcompare */
 +    offsetof(PyAsyncGenObject, ag_weakreflist), /* tp_weaklistoffset */
 +    0,                                          /* tp_iter */
 +    0,                                          /* tp_iternext */
 +    async_gen_methods,                          /* tp_methods */
 +    async_gen_memberlist,                       /* tp_members */
 +    async_gen_getsetlist,                       /* tp_getset */
 +    0,                                          /* tp_base */
 +    0,                                          /* tp_dict */
 +    0,                                          /* tp_descr_get */
 +    0,                                          /* tp_descr_set */
 +    0,                                          /* tp_dictoffset */
 +    0,                                          /* tp_init */
 +    0,                                          /* tp_alloc */
 +    0,                                          /* tp_new */
 +    0,                                          /* tp_free */
 +    0,                                          /* tp_is_gc */
 +    0,                                          /* tp_bases */
 +    0,                                          /* tp_mro */
 +    0,                                          /* tp_cache */
 +    0,                                          /* tp_subclasses */
 +    0,                                          /* tp_weaklist */
 +    0,                                          /* tp_del */
 +    0,                                          /* tp_version_tag */
 +    _PyGen_Finalize,                            /* tp_finalize */
 +};
 +
 +
 +PyObject *
 +PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
 +{
 +    PyAsyncGenObject *o;
 +    o = (PyAsyncGenObject *)gen_new_with_qualname(
 +        &PyAsyncGen_Type, f, name, qualname);
 +    if (o == NULL) {
 +        return NULL;
 +    }
 +    o->ag_finalizer = NULL;
 +    o->ag_closed = 0;
 +    o->ag_hooks_inited = 0;
 +    return (PyObject*)o;
 +}
 +
 +
 +int
 +PyAsyncGen_ClearFreeLists(void)
 +{
 +    int ret = ag_value_freelist_free + ag_asend_freelist_free;
 +
 +    while (ag_value_freelist_free) {
 +        _PyAsyncGenWrappedValue *o;
 +        o = ag_value_freelist[--ag_value_freelist_free];
 +        assert(_PyAsyncGenWrappedValue_CheckExact(o));
 +        PyObject_Del(o);
 +    }
 +
 +    while (ag_asend_freelist_free) {
 +        PyAsyncGenASend *o;
 +        o = ag_asend_freelist[--ag_asend_freelist_free];
 +        assert(Py_TYPE(o) == &_PyAsyncGenASend_Type);
 +        PyObject_Del(o);
 +    }
 +
 +    return ret;
 +}
 +
 +void
 +PyAsyncGen_Fini(void)
 +{
 +    PyAsyncGen_ClearFreeLists();
 +}
 +
 +
 +static PyObject *
 +async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
 +{
 +    if (result == NULL) {
 +        if (!PyErr_Occurred()) {
 +            PyErr_SetNone(PyExc_StopAsyncIteration);
 +        }
 +
 +        if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)
 +            || PyErr_ExceptionMatches(PyExc_GeneratorExit)
 +        ) {
 +            gen->ag_closed = 1;
 +        }
 +
 +        return NULL;
 +    }
 +
 +    if (_PyAsyncGenWrappedValue_CheckExact(result)) {
 +        /* async yield */
-         if (e == NULL) {
-             return NULL;
-         }
-         PyErr_SetObject(PyExc_StopIteration, e);
-         Py_DECREF(e);
++        _PyGen_SetStopIterationValue(((_PyAsyncGenWrappedValue*)result)->agw_val);
 +        Py_DECREF(result);
 +        return NULL;
 +    }
 +
 +    return result;
 +}
 +
 +
 +/* ---------- Async Generator ASend Awaitable ------------ */
 +
 +
 +static void
 +async_gen_asend_dealloc(PyAsyncGenASend *o)
 +{
 +    Py_CLEAR(o->ags_gen);
 +    Py_CLEAR(o->ags_sendval);
 +    if (ag_asend_freelist_free < _PyAsyncGen_MAXFREELIST) {
 +        assert(PyAsyncGenASend_CheckExact(o));
 +        ag_asend_freelist[ag_asend_freelist_free++] = o;
 +    } else {
 +        PyObject_Del(o);
 +    }
 +}
 +
 +
 +static PyObject *
 +async_gen_asend_send(PyAsyncGenASend *o, PyObject *arg)
 +{
 +    PyObject *result;
 +
 +    if (o->ags_state == AWAITABLE_STATE_CLOSED) {
 +        PyErr_SetNone(PyExc_StopIteration);
 +        return NULL;
 +    }
 +
 +    if (o->ags_state == AWAITABLE_STATE_INIT) {
 +        if (arg == NULL || arg == Py_None) {
 +            arg = o->ags_sendval;
 +        }
 +        o->ags_state = AWAITABLE_STATE_ITER;
 +    }
 +
 +    result = gen_send_ex((PyGenObject*)o->ags_gen, arg, 0, 0);
 +    result = async_gen_unwrap_value(o->ags_gen, result);
 +
 +    if (result == NULL) {
 +        o->ags_state = AWAITABLE_STATE_CLOSED;
 +    }
 +
 +    return result;
 +}
 +
 +
 +static PyObject *
 +async_gen_asend_iternext(PyAsyncGenASend *o)
 +{
 +    return async_gen_asend_send(o, NULL);
 +}
 +
 +
 +static PyObject *
 +async_gen_asend_throw(PyAsyncGenASend *o, PyObject *args)
 +{
 +    PyObject *result;
 +
 +    if (o->ags_state == AWAITABLE_STATE_CLOSED) {
 +        PyErr_SetNone(PyExc_StopIteration);
 +        return NULL;
 +    }
 +
 +    result = gen_throw((PyGenObject*)o->ags_gen, args);
 +    result = async_gen_unwrap_value(o->ags_gen, result);
 +
 +    if (result == NULL) {
 +        o->ags_state = AWAITABLE_STATE_CLOSED;
 +    }
 +
 +    return result;
 +}
 +
 +
 +static PyObject *
 +async_gen_asend_close(PyAsyncGenASend *o, PyObject *args)
 +{
 +    o->ags_state = AWAITABLE_STATE_CLOSED;
 +    Py_RETURN_NONE;
 +}
 +
 +
 +static PyMethodDef async_gen_asend_methods[] = {
 +    {"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc},
 +    {"throw", (PyCFunction)async_gen_asend_throw, METH_VARARGS, throw_doc},
 +    {"close", (PyCFunction)async_gen_asend_close, METH_NOARGS, close_doc},
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +
 +static PyAsyncMethods async_gen_asend_as_async = {
 +    PyObject_SelfIter,                          /* am_await */
 +    0,                                          /* am_aiter */
 +    0                                           /* am_anext */
 +};
 +
 +
 +PyTypeObject _PyAsyncGenASend_Type = {
 +    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 +    "async_generator_asend",                    /* tp_name */
 +    sizeof(PyAsyncGenASend),                    /* tp_basicsize */
 +    0,                                          /* tp_itemsize */
 +    /* methods */
 +    (destructor)async_gen_asend_dealloc,        /* tp_dealloc */
 +    0,                                          /* tp_print */
 +    0,                                          /* tp_getattr */
 +    0,                                          /* tp_setattr */
 +    &async_gen_asend_as_async,                  /* tp_as_async */
 +    0,                                          /* tp_repr */
 +    0,                                          /* tp_as_number */
 +    0,                                          /* tp_as_sequence */
 +    0,                                          /* tp_as_mapping */
 +    0,                                          /* tp_hash */
 +    0,                                          /* tp_call */
 +    0,                                          /* tp_str */
 +    PyObject_GenericGetAttr,                    /* tp_getattro */
 +    0,                                          /* tp_setattro */
 +    0,                                          /* tp_as_buffer */
 +    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
 +    0,                                          /* tp_doc */
 +    0,                                          /* tp_traverse */
 +    0,                                          /* tp_clear */
 +    0,                                          /* tp_richcompare */
 +    0,                                          /* tp_weaklistoffset */
 +    PyObject_SelfIter,                          /* tp_iter */
 +    (iternextfunc)async_gen_asend_iternext,     /* tp_iternext */
 +    async_gen_asend_methods,                    /* tp_methods */
 +    0,                                          /* tp_members */
 +    0,                                          /* tp_getset */
 +    0,                                          /* tp_base */
 +    0,                                          /* tp_dict */
 +    0,                                          /* tp_descr_get */
 +    0,                                          /* tp_descr_set */
 +    0,                                          /* tp_dictoffset */
 +    0,                                          /* tp_init */
 +    0,                                          /* tp_alloc */
 +    0,                                          /* tp_new */
 +};
 +
 +
 +static PyObject *
 +async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval)
 +{
 +    PyAsyncGenASend *o;
 +    if (ag_asend_freelist_free) {
 +        ag_asend_freelist_free--;
 +        o = ag_asend_freelist[ag_asend_freelist_free];
 +        _Py_NewReference((PyObject *)o);
 +    } else {
 +        o = PyObject_New(PyAsyncGenASend, &_PyAsyncGenASend_Type);
 +        if (o == NULL) {
 +            return NULL;
 +        }
 +    }
 +
 +    Py_INCREF(gen);
 +    o->ags_gen = gen;
 +
 +    Py_XINCREF(sendval);
 +    o->ags_sendval = sendval;
 +
 +    o->ags_state = AWAITABLE_STATE_INIT;
 +    return (PyObject*)o;
 +}
 +
 +
 +/* ---------- Async Generator Value Wrapper ------------ */
 +
 +
 +static void
 +async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
 +{
 +    Py_CLEAR(o->agw_val);
 +    if (ag_value_freelist_free < _PyAsyncGen_MAXFREELIST) {
 +        assert(_PyAsyncGenWrappedValue_CheckExact(o));
 +        ag_value_freelist[ag_value_freelist_free++] = o;
 +    } else {
 +        PyObject_Del(o);
 +    }
 +}
 +
 +
 +PyTypeObject _PyAsyncGenWrappedValue_Type = {
 +    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 +    "async_generator_wrapped_value",            /* tp_name */
 +    sizeof(_PyAsyncGenWrappedValue),            /* tp_basicsize */
 +    0,                                          /* tp_itemsize */
 +    /* methods */
 +    (destructor)async_gen_wrapped_val_dealloc,  /* tp_dealloc */
 +    0,                                          /* tp_print */
 +    0,                                          /* tp_getattr */
 +    0,                                          /* tp_setattr */
 +    0,                                          /* tp_as_async */
 +    0,                                          /* tp_repr */
 +    0,                                          /* tp_as_number */
 +    0,                                          /* tp_as_sequence */
 +    0,                                          /* tp_as_mapping */
 +    0,                                          /* tp_hash */
 +    0,                                          /* tp_call */
 +    0,                                          /* tp_str */
 +    PyObject_GenericGetAttr,                    /* tp_getattro */
 +    0,                                          /* tp_setattro */
 +    0,                                          /* tp_as_buffer */
 +    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
 +    0,                                          /* tp_doc */
 +    0,                                          /* tp_traverse */
 +    0,                                          /* tp_clear */
 +    0,                                          /* tp_richcompare */
 +    0,                                          /* tp_weaklistoffset */
 +    0,                                          /* tp_iter */
 +    0,                                          /* tp_iternext */
 +    0,                                          /* tp_methods */
 +    0,                                          /* tp_members */
 +    0,                                          /* tp_getset */
 +    0,                                          /* tp_base */
 +    0,                                          /* tp_dict */
 +    0,                                          /* tp_descr_get */
 +    0,                                          /* tp_descr_set */
 +    0,                                          /* tp_dictoffset */
 +    0,                                          /* tp_init */
 +    0,                                          /* tp_alloc */
 +    0,                                          /* tp_new */
 +};
 +
 +
 +PyObject *
 +_PyAsyncGenValueWrapperNew(PyObject *val)
 +{
 +    _PyAsyncGenWrappedValue *o;
 +    assert(val);
 +
 +    if (ag_value_freelist_free) {
 +        ag_value_freelist_free--;
 +        o = ag_value_freelist[ag_value_freelist_free];
 +        assert(_PyAsyncGenWrappedValue_CheckExact(o));
 +        _Py_NewReference((PyObject*)o);
 +    } else {
 +        o = PyObject_New(_PyAsyncGenWrappedValue, &_PyAsyncGenWrappedValue_Type);
 +        if (o == NULL) {
 +            return NULL;
 +        }
 +    }
 +    o->agw_val = val;
 +    Py_INCREF(val);
 +    return (PyObject*)o;
 +}
 +
 +
 +/* ---------- Async Generator AThrow awaitable ------------ */
 +
 +
 +static void
 +async_gen_athrow_dealloc(PyAsyncGenAThrow *o)
 +{
 +    Py_CLEAR(o->agt_gen);
 +    Py_CLEAR(o->agt_args);
 +    PyObject_Del(o);
 +}
 +
 +
 +static PyObject *
 +async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
 +{
 +    PyGenObject *gen = (PyGenObject*)o->agt_gen;
 +    PyFrameObject *f = gen->gi_frame;
 +    PyObject *retval;
 +
 +    if (f == NULL || f->f_stacktop == NULL ||
 +            o->agt_state == AWAITABLE_STATE_CLOSED) {
 +        PyErr_SetNone(PyExc_StopIteration);
 +        return NULL;
 +    }
 +
 +    if (o->agt_state == AWAITABLE_STATE_INIT) {
 +        if (o->agt_gen->ag_closed) {
 +            PyErr_SetNone(PyExc_StopIteration);
 +            return NULL;
 +        }
 +
 +        if (arg != Py_None) {
 +            PyErr_SetString(PyExc_RuntimeError, NON_INIT_CORO_MSG);
 +            return NULL;
 +        }
 +
 +        o->agt_state = AWAITABLE_STATE_ITER;
 +
 +        if (o->agt_args == NULL) {
 +            /* aclose() mode */
 +            o->agt_gen->ag_closed = 1;
 +
 +            retval = _gen_throw((PyGenObject *)gen,
 +                                0,  /* Do not close generator when
 +                                       PyExc_GeneratorExit is passed */
 +                                PyExc_GeneratorExit, NULL, NULL);
 +
 +            if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
 +                Py_DECREF(retval);
 +                goto yield_close;
 +            }
 +        } else {
 +            PyObject *typ;
 +            PyObject *tb = NULL;
 +            PyObject *val = NULL;
 +
 +            if (!PyArg_UnpackTuple(o->agt_args, "athrow", 1, 3,
 +                                   &typ, &val, &tb)) {
 +                return NULL;
 +            }
 +
 +            retval = _gen_throw((PyGenObject *)gen,
 +                                0,  /* Do not close generator when
 +                                       PyExc_GeneratorExit is passed */
 +                                typ, val, tb);
 +            retval = async_gen_unwrap_value(o->agt_gen, retval);
 +        }
 +        if (retval == NULL) {
 +            goto check_error;
 +        }
 +        return retval;
 +    }
 +
 +    assert(o->agt_state == AWAITABLE_STATE_ITER);
 +
 +    retval = gen_send_ex((PyGenObject *)gen, arg, 0, 0);
 +    if (o->agt_args) {
 +        return async_gen_unwrap_value(o->agt_gen, retval);
 +    } else {
 +        /* aclose() mode */
 +        if (retval) {
 +            if (_PyAsyncGenWrappedValue_CheckExact(retval)) {
 +                Py_DECREF(retval);
 +                goto yield_close;
 +            }
 +            else {
 +                return retval;
 +            }
 +        }
 +        else {
 +            goto check_error;
 +        }
 +    }
 +
 +yield_close:
 +    PyErr_SetString(
 +        PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
 +    return NULL;
 +
 +check_error:
 +    if (PyErr_ExceptionMatches(PyExc_StopAsyncIteration)
 +        || PyErr_ExceptionMatches(PyExc_GeneratorExit)
 +    ) {
 +        o->agt_state = AWAITABLE_STATE_CLOSED;
 +        PyErr_Clear();          /* ignore these errors */
 +        PyErr_SetNone(PyExc_StopIteration);
 +    }
 +    return NULL;
 +}
 +
 +
 +static PyObject *
 +async_gen_athrow_throw(PyAsyncGenAThrow *o, PyObject *args)
 +{
 +    PyObject *retval;
 +
 +    if (o->agt_state == AWAITABLE_STATE_INIT) {
 +        PyErr_SetString(PyExc_RuntimeError, NON_INIT_CORO_MSG);
 +        return NULL;
 +    }
 +
 +    if (o->agt_state == AWAITABLE_STATE_CLOSED) {
 +        PyErr_SetNone(PyExc_StopIteration);
 +        return NULL;
 +    }
 +
 +    retval = gen_throw((PyGenObject*)o->agt_gen, args);
 +    if (o->agt_args) {
 +        return async_gen_unwrap_value(o->agt_gen, retval);
 +    } else {
 +        /* aclose() mode */
 +        if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) {
 +            Py_DECREF(retval);
 +            PyErr_SetString(PyExc_RuntimeError, ASYNC_GEN_IGNORED_EXIT_MSG);
 +            return NULL;
 +        }
 +        return retval;
 +    }
 +}
 +
 +
 +static PyObject *
 +async_gen_athrow_iternext(PyAsyncGenAThrow *o)
 +{
 +    return async_gen_athrow_send(o, Py_None);
 +}
 +
 +
 +static PyObject *
 +async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args)
 +{
 +    o->agt_state = AWAITABLE_STATE_CLOSED;
 +    Py_RETURN_NONE;
 +}
 +
 +
 +static PyMethodDef async_gen_athrow_methods[] = {
 +    {"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc},
 +    {"throw", (PyCFunction)async_gen_athrow_throw, METH_VARARGS, throw_doc},
 +    {"close", (PyCFunction)async_gen_athrow_close, METH_NOARGS, close_doc},
 +    {NULL, NULL}        /* Sentinel */
 +};
 +
 +
 +static PyAsyncMethods async_gen_athrow_as_async = {
 +    PyObject_SelfIter,                          /* am_await */
 +    0,                                          /* am_aiter */
 +    0                                           /* am_anext */
 +};
 +
 +
 +PyTypeObject _PyAsyncGenAThrow_Type = {
 +    PyVarObject_HEAD_INIT(&PyType_Type, 0)
 +    "async_generator_athrow",                   /* tp_name */
 +    sizeof(PyAsyncGenAThrow),                   /* tp_basicsize */
 +    0,                                          /* tp_itemsize */
 +    /* methods */
 +    (destructor)async_gen_athrow_dealloc,       /* tp_dealloc */
 +    0,                                          /* tp_print */
 +    0,                                          /* tp_getattr */
 +    0,                                          /* tp_setattr */
 +    &async_gen_athrow_as_async,                 /* tp_as_async */
 +    0,                                          /* tp_repr */
 +    0,                                          /* tp_as_number */
 +    0,                                          /* tp_as_sequence */
 +    0,                                          /* tp_as_mapping */
 +    0,                                          /* tp_hash */
 +    0,                                          /* tp_call */
 +    0,                                          /* tp_str */
 +    PyObject_GenericGetAttr,                    /* tp_getattro */
 +    0,                                          /* tp_setattro */
 +    0,                                          /* tp_as_buffer */
 +    Py_TPFLAGS_DEFAULT,                         /* tp_flags */
 +    0,                                          /* tp_doc */
 +    0,                                          /* tp_traverse */
 +    0,                                          /* tp_clear */
 +    0,                                          /* tp_richcompare */
 +    0,                                          /* tp_weaklistoffset */
 +    PyObject_SelfIter,                          /* tp_iter */
 +    (iternextfunc)async_gen_athrow_iternext,    /* tp_iternext */
 +    async_gen_athrow_methods,                   /* tp_methods */
 +    0,                                          /* tp_members */
 +    0,                                          /* tp_getset */
 +    0,                                          /* tp_base */
 +    0,                                          /* tp_dict */
 +    0,                                          /* tp_descr_get */
 +    0,                                          /* tp_descr_set */
 +    0,                                          /* tp_dictoffset */
 +    0,                                          /* tp_init */
 +    0,                                          /* tp_alloc */
 +    0,                                          /* tp_new */
 +};
 +
 +
 +static PyObject *
 +async_gen_athrow_new(PyAsyncGenObject *gen, PyObject *args)
 +{
 +    PyAsyncGenAThrow *o;
 +    o = PyObject_New(PyAsyncGenAThrow, &_PyAsyncGenAThrow_Type);
 +    if (o == NULL) {
 +        return NULL;
 +    }
 +    o->agt_gen = gen;
 +    o->agt_args = args;
 +    o->agt_state = AWAITABLE_STATE_INIT;
 +    Py_INCREF(gen);
 +    Py_XINCREF(args);
 +    return (PyObject*)o;
 +}