PEP 0492 -- Coroutines with async and await syntax. Issue #24017.

This commit is contained in:
Yury Selivanov 2015-05-11 22:57:16 -04:00
parent 4e6bf4b3da
commit 7544508f02
72 changed files with 9261 additions and 5739 deletions

View file

@ -1926,11 +1926,133 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
goto fast_block_end;
}
TARGET(GET_AITER) {
getaiterfunc getter = NULL;
PyObject *iter = NULL;
PyObject *awaitable = NULL;
PyObject *obj = TOP();
PyTypeObject *type = Py_TYPE(obj);
if (type->tp_as_async != NULL)
getter = type->tp_as_async->am_aiter;
if (getter != NULL) {
iter = (*getter)(obj);
Py_DECREF(obj);
if (iter == NULL) {
SET_TOP(NULL);
goto error;
}
}
else {
SET_TOP(NULL);
PyErr_Format(
PyExc_TypeError,
"'async for' requires an object with "
"__aiter__ method, got %.100s",
type->tp_name);
Py_DECREF(obj);
goto error;
}
awaitable = _PyGen_GetAwaitableIter(iter);
if (awaitable == NULL) {
SET_TOP(NULL);
PyErr_Format(
PyExc_TypeError,
"'async for' received an invalid object "
"from __aiter__: %.100s",
Py_TYPE(iter)->tp_name);
Py_DECREF(iter);
goto error;
} else
Py_DECREF(iter);
SET_TOP(awaitable);
DISPATCH();
}
TARGET(GET_ANEXT) {
aiternextfunc getter = NULL;
PyObject *next_iter = NULL;
PyObject *awaitable = NULL;
PyObject *aiter = TOP();
PyTypeObject *type = Py_TYPE(aiter);
if (type->tp_as_async != NULL)
getter = type->tp_as_async->am_anext;
if (getter != NULL) {
next_iter = (*getter)(aiter);
if (next_iter == NULL) {
goto error;
}
}
else {
PyErr_Format(
PyExc_TypeError,
"'async for' requires an iterator with "
"__anext__ method, got %.100s",
type->tp_name);
goto error;
}
awaitable = _PyGen_GetAwaitableIter(next_iter);
if (awaitable == NULL) {
PyErr_Format(
PyExc_TypeError,
"'async for' received an invalid object "
"from __anext__: %.100s",
Py_TYPE(next_iter)->tp_name);
Py_DECREF(next_iter);
goto error;
} else
Py_DECREF(next_iter);
PUSH(awaitable);
DISPATCH();
}
TARGET(GET_AWAITABLE) {
PyObject *iterable = TOP();
PyObject *iter = _PyGen_GetAwaitableIter(iterable);
Py_DECREF(iterable);
SET_TOP(iter); /* Even if it's NULL */
if (iter == NULL) {
goto error;
}
DISPATCH();
}
TARGET(YIELD_FROM) {
PyObject *v = POP();
PyObject *reciever = TOP();
int err;
if (PyGen_CheckExact(reciever)) {
if (
(((PyCodeObject*) \
((PyGenObject*)reciever)->gi_code)->co_flags &
CO_COROUTINE)
&& !(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)))
{
/* If we're yielding-from a coroutine object from a regular
generator object - raise an error. */
Py_CLEAR(v);
Py_CLEAR(reciever);
SET_TOP(NULL);
PyErr_SetString(PyExc_TypeError,
"cannot 'yield from' a coroutine object "
"from a generator");
goto error;
}
retval = _PyGen_Send((PyGenObject *)reciever, v);
} else {
_Py_IDENTIFIER(send);
@ -2822,11 +2944,26 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
TARGET(GET_ITER) {
/* before: [obj]; after [getiter(obj)] */
PyObject *iterable = TOP();
PyObject *iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
SET_TOP(iter);
if (iter == NULL)
goto error;
PyObject *iter;
/* If we have a generator object on top -- keep it there,
it's already an iterator.
This is needed to allow use of 'async def' coroutines
in 'yield from' expression from generator-based coroutines
(decorated with types.coroutine()).
'yield from' is compiled to GET_ITER..YIELD_FROM combination,
but since coroutines raise TypeError in their 'tp_iter' we
need a way for them to "pass through" the GET_ITER.
*/
if (!PyGen_CheckExact(iterable)) {
/* `iterable` is not a generator. */
iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
SET_TOP(iter);
if (iter == NULL)
goto error;
}
PREDICT(FOR_ITER);
DISPATCH();
}
@ -2883,6 +3020,39 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
DISPATCH();
}
TARGET(BEFORE_ASYNC_WITH) {
_Py_IDENTIFIER(__aexit__);
_Py_IDENTIFIER(__aenter__);
PyObject *mgr = TOP();
PyObject *exit = special_lookup(mgr, &PyId___aexit__),
*enter;
PyObject *res;
if (exit == NULL)
goto error;
SET_TOP(exit);
enter = special_lookup(mgr, &PyId___aenter__);
Py_DECREF(mgr);
if (enter == NULL)
goto error;
res = PyObject_CallFunctionObjArgs(enter, NULL);
Py_DECREF(enter);
if (res == NULL)
goto error;
PUSH(res);
DISPATCH();
}
TARGET(SETUP_ASYNC_WITH) {
PyObject *res = POP();
/* Setup the finally block before pushing the result
of __aenter__ on the stack. */
PyFrame_BlockSetup(f, SETUP_FINALLY, INSTR_OFFSET() + oparg,
STACK_LEVEL());
PUSH(res);
DISPATCH();
}
TARGET(SETUP_WITH) {
_Py_IDENTIFIER(__exit__);
_Py_IDENTIFIER(__enter__);
@ -2909,7 +3079,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
DISPATCH();
}
TARGET(WITH_CLEANUP) {
TARGET(WITH_CLEANUP_START) {
/* At the top of the stack are 1-6 values indicating
how/why we entered the finally clause:
- TOP = None
@ -2937,7 +3107,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
PyObject *exit_func;
PyObject *exc = TOP(), *val = Py_None, *tb = Py_None, *res;
int err;
if (exc == Py_None) {
(void)POP();
exit_func = TOP();
@ -2987,10 +3156,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
if (res == NULL)
goto error;
PUSH(exc);
PUSH(res);
PREDICT(WITH_CLEANUP_FINISH);
DISPATCH();
}
PREDICTED(WITH_CLEANUP_FINISH);
TARGET(WITH_CLEANUP_FINISH) {
PyObject *res = POP();
PyObject *exc = POP();
int err;
if (exc != Py_None)
err = PyObject_IsTrue(res);
else
err = 0;
Py_DECREF(res);
if (err < 0)
@ -3751,6 +3933,9 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
}
if (co->co_flags & CO_GENERATOR) {
PyObject *gen;
PyObject *coroutine_wrapper;
/* Don't need to keep the reference to f_back, it will be set
* when the generator is resumed. */
Py_CLEAR(f->f_back);
@ -3759,7 +3944,19 @@ _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
/* Create a new generator that owns the ready to run frame
* and return that as the value. */
return PyGen_NewWithQualName(f, name, qualname);
gen = PyGen_NewWithQualName(f, name, qualname);
if (gen == NULL)
return NULL;
if (co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE)) {
coroutine_wrapper = PyEval_GetCoroutineWrapper();
if (coroutine_wrapper != NULL) {
PyObject *wrapped =
PyObject_CallFunction(coroutine_wrapper, "N", gen);
gen = wrapped;
}
}
return gen;
}
retval = PyEval_EvalFrameEx(f,0);
@ -4205,6 +4402,24 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
|| (tstate->c_profilefunc != NULL));
}
void
PyEval_SetCoroutineWrapper(PyObject *wrapper)
{
PyThreadState *tstate = PyThreadState_GET();
Py_CLEAR(tstate->coroutine_wrapper);
Py_XINCREF(wrapper);
tstate->coroutine_wrapper = wrapper;
}
PyObject *
PyEval_GetCoroutineWrapper()
{
PyThreadState *tstate = PyThreadState_GET();
return tstate->coroutine_wrapper;
}
PyObject *
PyEval_GetBuiltins(void)
{