Issue #27243: Fix __aiter__ protocol

This commit is contained in:
Yury Selivanov 2016-06-09 15:08:31 -04:00
parent ebe95fdabb
commit a6f6edbda8
13 changed files with 292 additions and 33 deletions

View file

@ -1933,8 +1933,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
PyObject *obj = TOP();
PyTypeObject *type = Py_TYPE(obj);
if (type->tp_as_async != NULL)
if (type->tp_as_async != NULL) {
getter = type->tp_as_async->am_aiter;
}
if (getter != NULL) {
iter = (*getter)(obj);
@ -1955,6 +1956,27 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
goto error;
}
if (Py_TYPE(iter)->tp_as_async != NULL &&
Py_TYPE(iter)->tp_as_async->am_anext != NULL) {
/* Starting with CPython 3.5.2 __aiter__ should return
asynchronous iterators directly (not awaitables that
resolve to asynchronous iterators.)
Therefore, we check if the object that was returned
from __aiter__ has an __anext__ method. If it does,
we wrap it in an awaitable that resolves to `iter`.
See http://bugs.python.org/issue27243 for more
details.
*/
PyObject *wrapper = _PyAIterWrapper_New(iter);
Py_DECREF(iter);
SET_TOP(wrapper);
DISPATCH();
}
awaitable = _PyCoro_GetAwaitableIter(iter);
if (awaitable == NULL) {
SET_TOP(NULL);
@ -1966,9 +1988,23 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
Py_DECREF(iter);
goto error;
} else
} else {
Py_DECREF(iter);
if (PyErr_WarnFormat(
PyExc_PendingDeprecationWarning, 1,
"'%.100s' implements legacy __aiter__ protocol; "
"__aiter__ should return an asynchronous "
"iterator, not awaitable",
type->tp_name))
{
/* Warning was converted to an error. */
Py_DECREF(awaitable);
SET_TOP(NULL);
goto error;
}
}
SET_TOP(awaitable);
DISPATCH();
}