coroutines: Error when awaiting on coroutine that's being awaited

Issue #25888
This commit is contained in:
Yury Selivanov 2016-03-02 11:30:46 -05:00
parent e076ffb068
commit c724bae51c
4 changed files with 40 additions and 6 deletions

View File

@ -43,6 +43,7 @@ PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *,
PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *);
PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **);
PyObject *_PyGen_Send(PyGenObject *, PyObject *); PyObject *_PyGen_Send(PyGenObject *, PyObject *);
PyObject *_PyGen_yf(PyGenObject *);
PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
#ifndef Py_LIMITED_API #ifndef Py_LIMITED_API

View File

@ -942,6 +942,24 @@ class CoroutineTest(unittest.TestCase):
with self.assertRaises(Marker): with self.assertRaises(Marker):
c.throw(ZeroDivisionError) c.throw(ZeroDivisionError)
def test_await_15(self):
@types.coroutine
def nop():
yield
async def coroutine():
await nop()
async def waiter(coro):
await coro
coro = coroutine()
coro.send(None)
with self.assertRaisesRegex(RuntimeError,
"coroutine is being awaited already"):
waiter(coro).send(None)
def test_with_1(self): def test_with_1(self):
class Manager: class Manager:
def __init__(self, name): def __init__(self, name):

View File

@ -267,8 +267,8 @@ gen_close_iter(PyObject *yf)
return 0; return 0;
} }
static PyObject * PyObject *
gen_yf(PyGenObject *gen) _PyGen_yf(PyGenObject *gen)
{ {
PyObject *yf = NULL; PyObject *yf = NULL;
PyFrameObject *f = gen->gi_frame; PyFrameObject *f = gen->gi_frame;
@ -290,7 +290,7 @@ static PyObject *
gen_close(PyGenObject *gen, PyObject *args) gen_close(PyGenObject *gen, PyObject *args)
{ {
PyObject *retval; PyObject *retval;
PyObject *yf = gen_yf(gen); PyObject *yf = _PyGen_yf(gen);
int err = 0; int err = 0;
if (yf) { if (yf) {
@ -330,7 +330,7 @@ gen_throw(PyGenObject *gen, PyObject *args)
PyObject *typ; PyObject *typ;
PyObject *tb = NULL; PyObject *tb = NULL;
PyObject *val = NULL; PyObject *val = NULL;
PyObject *yf = gen_yf(gen); PyObject *yf = _PyGen_yf(gen);
_Py_IDENTIFIER(throw); _Py_IDENTIFIER(throw);
if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb)) if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb))
@ -564,7 +564,7 @@ gen_set_qualname(PyGenObject *op, PyObject *value)
static PyObject * static PyObject *
gen_getyieldfrom(PyGenObject *gen) gen_getyieldfrom(PyGenObject *gen)
{ {
PyObject *yf = gen_yf(gen); PyObject *yf = _PyGen_yf(gen);
if (yf == NULL) if (yf == NULL)
Py_RETURN_NONE; Py_RETURN_NONE;
return yf; return yf;
@ -799,7 +799,7 @@ coro_await(PyCoroObject *coro)
static PyObject * static PyObject *
coro_get_cr_await(PyCoroObject *coro) coro_get_cr_await(PyCoroObject *coro)
{ {
PyObject *yf = gen_yf((PyGenObject *) coro); PyObject *yf = _PyGen_yf((PyGenObject *) coro);
if (yf == NULL) if (yf == NULL)
Py_RETURN_NONE; Py_RETURN_NONE;
return yf; return yf;

View File

@ -2021,6 +2021,21 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
Py_DECREF(iterable); Py_DECREF(iterable);
if (iter != NULL && PyCoro_CheckExact(iter)) {
PyObject *yf = _PyGen_yf((PyGenObject*)iter);
if (yf != NULL) {
/* `iter` is a coroutine object that is being
awaited, `yf` is a pointer to the current awaitable
being awaited on. */
Py_DECREF(yf);
Py_CLEAR(iter);
PyErr_SetString(
PyExc_RuntimeError,
"coroutine is being awaited already");
/* The code below jumps to `error` if `iter` is NULL. */
}
}
SET_TOP(iter); /* Even if it's NULL */ SET_TOP(iter); /* Even if it's NULL */
if (iter == NULL) { if (iter == NULL) {