diff -r cca2ed4e8b41 Lib/test/test_coroutines.py --- a/Lib/test/test_coroutines.py Thu Dec 17 10:35:05 2015 +0000 +++ b/Lib/test/test_coroutines.py Thu Dec 17 13:10:29 2015 -0500 @@ -569,6 +569,97 @@ "coroutine ignored GeneratorExit"): c.close() + def test_func_15(self): + # See http://bugs.python.org/issue25887 for details + + async def send(): + return 'spam' + async def reader(coro): + return await coro + + spammer = send() + + with self.assertRaisesRegex(StopIteration, 'spam'): + reader(spammer).send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + reader(spammer).send(None) + + def test_func_16(self): + # See http://bugs.python.org/issue25887 for details + + @types.coroutine + def nop(): + yield + async def send(): + await nop() + return 'spam' + async def read(coro): + await nop() + return await coro + + spammer = send() + + reader = read(spammer) + reader.send(None) + reader.send(None) + with self.assertRaisesRegex(Exception, 'ham'): + reader.throw(Exception('ham')) + + reader = read(spammer) + reader.send(None) + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + reader.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + reader.throw(Exception('wat')) + + def test_func_17(self): + # See http://bugs.python.org/issue25887 for details + + async def coroutine(): + return 'spam' + + coro = coroutine() + with self.assertRaisesRegex(StopIteration, 'spam'): + coro.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + coro.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + coro.throw(Exception('wat')) + + def test_func_18(self): + # See http://bugs.python.org/issue25887 for details + + async def coroutine(): + return 'spam' + + coro = coroutine() + awaitable = coro.__await__() + it = iter(awaitable) + + with self.assertRaisesRegex(StopIteration, 'spam'): + it.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + it.send(None) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + next(it) + + with self.assertRaisesRegex(RuntimeError, + 'coroutine was already awaited'): + it.throw(Exception('wat')) + def test_cr_await(self): @types.coroutine def a(): diff -r cca2ed4e8b41 Objects/genobject.c --- a/Objects/genobject.c Thu Dec 17 10:35:05 2015 +0000 +++ b/Objects/genobject.c Thu Dec 17 13:10:29 2015 -0500 @@ -92,9 +92,16 @@ return NULL; } if (f == NULL || f->f_stacktop == NULL) { - /* Only set exception if called from send() */ - if (arg && !exc) + if (PyCoro_CheckExact(gen)) { + /* `gen` is an exhausted coroutine: always raise an error. */ + PyErr_SetString( + PyExc_RuntimeError, + "coroutine was already awaited"); + } else if (arg && !exc) { + /* `gen` is an exhausted generator: + only set exception if called from send(). */ PyErr_SetNone(PyExc_StopIteration); + } return NULL; }