bpo-31709: Drop support for asynchronous __aiter__. (#3903)
This commit is contained in:
parent
86566702f3
commit
faa135acbf
|
@ -2520,9 +2520,8 @@ generators, coroutines do not directly support iteration.
|
||||||
Asynchronous Iterators
|
Asynchronous Iterators
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
An *asynchronous iterable* is able to call asynchronous code in its
|
An *asynchronous iterator* can call asynchronous code in
|
||||||
``__aiter__`` implementation, and an *asynchronous iterator* can call
|
its ``__anext__`` method.
|
||||||
asynchronous code in its ``__anext__`` method.
|
|
||||||
|
|
||||||
Asynchronous iterators can be used in an :keyword:`async for` statement.
|
Asynchronous iterators can be used in an :keyword:`async for` statement.
|
||||||
|
|
||||||
|
@ -2552,48 +2551,14 @@ An example of an asynchronous iterable object::
|
||||||
|
|
||||||
.. versionadded:: 3.5
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
.. note::
|
.. versionchanged:: 3.7
|
||||||
|
Prior to Python 3.7, ``__aiter__`` could return an *awaitable*
|
||||||
|
that would resolve to an
|
||||||
|
:term:`asynchronous iterator <asynchronous iterator>`.
|
||||||
|
|
||||||
.. versionchanged:: 3.5.2
|
Starting with Python 3.7, ``__aiter__`` must return an
|
||||||
Starting with CPython 3.5.2, ``__aiter__`` can directly return
|
asynchronous iterator object. Returning anything else
|
||||||
:term:`asynchronous iterators <asynchronous iterator>`. Returning
|
will result in a :exc:`TypeError` error.
|
||||||
an :term:`awaitable` object will result in a
|
|
||||||
:exc:`PendingDeprecationWarning`.
|
|
||||||
|
|
||||||
The recommended way of writing backwards compatible code in
|
|
||||||
CPython 3.5.x is to continue returning awaitables from
|
|
||||||
``__aiter__``. If you want to avoid the PendingDeprecationWarning
|
|
||||||
and keep the code backwards compatible, the following decorator
|
|
||||||
can be used::
|
|
||||||
|
|
||||||
import functools
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if sys.version_info < (3, 5, 2):
|
|
||||||
def aiter_compat(func):
|
|
||||||
@functools.wraps(func)
|
|
||||||
async def wrapper(self):
|
|
||||||
return func(self)
|
|
||||||
return wrapper
|
|
||||||
else:
|
|
||||||
def aiter_compat(func):
|
|
||||||
return func
|
|
||||||
|
|
||||||
Example::
|
|
||||||
|
|
||||||
class AsyncIterator:
|
|
||||||
|
|
||||||
@aiter_compat
|
|
||||||
def __aiter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
async def __anext__(self):
|
|
||||||
...
|
|
||||||
|
|
||||||
Starting with CPython 3.6, the :exc:`PendingDeprecationWarning`
|
|
||||||
will be replaced with the :exc:`DeprecationWarning`.
|
|
||||||
In CPython 3.7, returning an awaitable from ``__aiter__`` will
|
|
||||||
result in a :exc:`RuntimeError`.
|
|
||||||
|
|
||||||
|
|
||||||
.. _async-context-managers:
|
.. _async-context-managers:
|
||||||
|
|
|
@ -56,7 +56,6 @@ PyAPI_DATA(PyTypeObject) PyCoro_Type;
|
||||||
PyAPI_DATA(PyTypeObject) _PyCoroWrapper_Type;
|
PyAPI_DATA(PyTypeObject) _PyCoroWrapper_Type;
|
||||||
|
|
||||||
PyAPI_DATA(PyTypeObject) _PyAIterWrapper_Type;
|
PyAPI_DATA(PyTypeObject) _PyAIterWrapper_Type;
|
||||||
PyObject *_PyAIterWrapper_New(PyObject *aiter);
|
|
||||||
|
|
||||||
#define PyCoro_CheckExact(op) (Py_TYPE(op) == &PyCoro_Type)
|
#define PyCoro_CheckExact(op) (Py_TYPE(op) == &PyCoro_Type)
|
||||||
PyObject *_PyCoro_GetAwaitableIter(PyObject *o);
|
PyObject *_PyCoro_GetAwaitableIter(PyObject *o);
|
||||||
|
|
|
@ -676,20 +676,12 @@ class StreamReader:
|
||||||
self._maybe_resume_transport()
|
self._maybe_resume_transport()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
if compat.PY35:
|
def __aiter__(self):
|
||||||
@coroutine
|
return self
|
||||||
def __aiter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
@coroutine
|
@coroutine
|
||||||
def __anext__(self):
|
def __anext__(self):
|
||||||
val = yield from self.readline()
|
val = yield from self.readline()
|
||||||
if val == b'':
|
if val == b'':
|
||||||
raise StopAsyncIteration
|
raise StopAsyncIteration
|
||||||
return val
|
return val
|
||||||
|
|
||||||
if compat.PY352:
|
|
||||||
# In Python 3.5.2 and greater, __aiter__ should return
|
|
||||||
# the asynchronous iterator directly.
|
|
||||||
def __aiter__(self):
|
|
||||||
return self
|
|
||||||
|
|
|
@ -660,7 +660,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
||||||
|
|
||||||
def test_AsyncIterable(self):
|
def test_AsyncIterable(self):
|
||||||
class AI:
|
class AI:
|
||||||
async def __aiter__(self):
|
def __aiter__(self):
|
||||||
return self
|
return self
|
||||||
self.assertTrue(isinstance(AI(), AsyncIterable))
|
self.assertTrue(isinstance(AI(), AsyncIterable))
|
||||||
self.assertTrue(issubclass(AI, AsyncIterable))
|
self.assertTrue(issubclass(AI, AsyncIterable))
|
||||||
|
@ -674,7 +674,7 @@ class TestOneTrickPonyABCs(ABCTestCase):
|
||||||
|
|
||||||
def test_AsyncIterator(self):
|
def test_AsyncIterator(self):
|
||||||
class AI:
|
class AI:
|
||||||
async def __aiter__(self):
|
def __aiter__(self):
|
||||||
return self
|
return self
|
||||||
async def __anext__(self):
|
async def __anext__(self):
|
||||||
raise StopAsyncIteration
|
raise StopAsyncIteration
|
||||||
|
|
|
@ -1382,7 +1382,7 @@ class CoroutineTest(unittest.TestCase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.i = 0
|
self.i = 0
|
||||||
|
|
||||||
async def __aiter__(self):
|
def __aiter__(self):
|
||||||
nonlocal aiter_calls
|
nonlocal aiter_calls
|
||||||
aiter_calls += 1
|
aiter_calls += 1
|
||||||
return self
|
return self
|
||||||
|
@ -1401,9 +1401,8 @@ class CoroutineTest(unittest.TestCase):
|
||||||
|
|
||||||
buffer = []
|
buffer = []
|
||||||
async def test1():
|
async def test1():
|
||||||
with self.assertWarnsRegex(DeprecationWarning, "legacy"):
|
async for i1, i2 in AsyncIter():
|
||||||
async for i1, i2 in AsyncIter():
|
buffer.append(i1 + i2)
|
||||||
buffer.append(i1 + i2)
|
|
||||||
|
|
||||||
yielded, _ = run_async(test1())
|
yielded, _ = run_async(test1())
|
||||||
# Make sure that __aiter__ was called only once
|
# Make sure that __aiter__ was called only once
|
||||||
|
@ -1415,13 +1414,12 @@ class CoroutineTest(unittest.TestCase):
|
||||||
buffer = []
|
buffer = []
|
||||||
async def test2():
|
async def test2():
|
||||||
nonlocal buffer
|
nonlocal buffer
|
||||||
with self.assertWarnsRegex(DeprecationWarning, "legacy"):
|
async for i in AsyncIter():
|
||||||
async for i in AsyncIter():
|
buffer.append(i[0])
|
||||||
buffer.append(i[0])
|
if i[0] == 20:
|
||||||
if i[0] == 20:
|
break
|
||||||
break
|
else:
|
||||||
else:
|
buffer.append('what?')
|
||||||
buffer.append('what?')
|
|
||||||
buffer.append('end')
|
buffer.append('end')
|
||||||
|
|
||||||
yielded, _ = run_async(test2())
|
yielded, _ = run_async(test2())
|
||||||
|
@ -1434,13 +1432,12 @@ class CoroutineTest(unittest.TestCase):
|
||||||
buffer = []
|
buffer = []
|
||||||
async def test3():
|
async def test3():
|
||||||
nonlocal buffer
|
nonlocal buffer
|
||||||
with self.assertWarnsRegex(DeprecationWarning, "legacy"):
|
async for i in AsyncIter():
|
||||||
async for i in AsyncIter():
|
if i[0] > 20:
|
||||||
if i[0] > 20:
|
continue
|
||||||
continue
|
buffer.append(i[0])
|
||||||
buffer.append(i[0])
|
else:
|
||||||
else:
|
buffer.append('what?')
|
||||||
buffer.append('what?')
|
|
||||||
buffer.append('end')
|
buffer.append('end')
|
||||||
|
|
||||||
yielded, _ = run_async(test3())
|
yielded, _ = run_async(test3())
|
||||||
|
@ -1479,7 +1476,7 @@ class CoroutineTest(unittest.TestCase):
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
TypeError,
|
TypeError,
|
||||||
r"async for' received an invalid object.*__aiter.*\: I"):
|
r"that does not implement __anext__"):
|
||||||
|
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
|
||||||
|
@ -1508,25 +1505,6 @@ class CoroutineTest(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(sys.getrefcount(aiter), refs_before)
|
self.assertEqual(sys.getrefcount(aiter), refs_before)
|
||||||
|
|
||||||
def test_for_5(self):
|
|
||||||
class I:
|
|
||||||
async def __aiter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __anext__(self):
|
|
||||||
return 123
|
|
||||||
|
|
||||||
async def foo():
|
|
||||||
with self.assertWarnsRegex(DeprecationWarning, "legacy"):
|
|
||||||
async for i in I():
|
|
||||||
print('never going to happen')
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
|
||||||
TypeError,
|
|
||||||
"async for' received an invalid object.*__anext.*int"):
|
|
||||||
|
|
||||||
run_async(foo())
|
|
||||||
|
|
||||||
def test_for_6(self):
|
def test_for_6(self):
|
||||||
I = 0
|
I = 0
|
||||||
|
|
||||||
|
@ -1622,13 +1600,12 @@ class CoroutineTest(unittest.TestCase):
|
||||||
def test_for_7(self):
|
def test_for_7(self):
|
||||||
CNT = 0
|
CNT = 0
|
||||||
class AI:
|
class AI:
|
||||||
async def __aiter__(self):
|
def __aiter__(self):
|
||||||
1/0
|
1/0
|
||||||
async def foo():
|
async def foo():
|
||||||
nonlocal CNT
|
nonlocal CNT
|
||||||
with self.assertWarnsRegex(DeprecationWarning, "legacy"):
|
async for i in AI():
|
||||||
async for i in AI():
|
CNT += 1
|
||||||
CNT += 1
|
|
||||||
CNT += 10
|
CNT += 10
|
||||||
with self.assertRaises(ZeroDivisionError):
|
with self.assertRaises(ZeroDivisionError):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
@ -1652,37 +1629,6 @@ class CoroutineTest(unittest.TestCase):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
self.assertEqual(CNT, 0)
|
self.assertEqual(CNT, 0)
|
||||||
|
|
||||||
def test_for_9(self):
|
|
||||||
# Test that DeprecationWarning can safely be converted into
|
|
||||||
# an exception (__aiter__ should not have a chance to raise
|
|
||||||
# a ZeroDivisionError.)
|
|
||||||
class AI:
|
|
||||||
async def __aiter__(self):
|
|
||||||
1/0
|
|
||||||
async def foo():
|
|
||||||
async for i in AI():
|
|
||||||
pass
|
|
||||||
|
|
||||||
with self.assertRaises(DeprecationWarning):
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("error")
|
|
||||||
run_async(foo())
|
|
||||||
|
|
||||||
def test_for_10(self):
|
|
||||||
# Test that DeprecationWarning can safely be converted into
|
|
||||||
# an exception.
|
|
||||||
class AI:
|
|
||||||
async def __aiter__(self):
|
|
||||||
pass
|
|
||||||
async def foo():
|
|
||||||
async for i in AI():
|
|
||||||
pass
|
|
||||||
|
|
||||||
with self.assertRaises(DeprecationWarning):
|
|
||||||
with warnings.catch_warnings():
|
|
||||||
warnings.simplefilter("error")
|
|
||||||
run_async(foo())
|
|
||||||
|
|
||||||
def test_for_11(self):
|
def test_for_11(self):
|
||||||
class F:
|
class F:
|
||||||
def __aiter__(self):
|
def __aiter__(self):
|
||||||
|
@ -1703,24 +1649,6 @@ class CoroutineTest(unittest.TestCase):
|
||||||
err = c.exception
|
err = c.exception
|
||||||
self.assertIsInstance(err.__cause__, ZeroDivisionError)
|
self.assertIsInstance(err.__cause__, ZeroDivisionError)
|
||||||
|
|
||||||
def test_for_12(self):
|
|
||||||
class F:
|
|
||||||
def __aiter__(self):
|
|
||||||
return self
|
|
||||||
def __await__(self):
|
|
||||||
1 / 0
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
async for _ in F():
|
|
||||||
pass
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(TypeError,
|
|
||||||
'an invalid object from __aiter__') as c:
|
|
||||||
main().send(None)
|
|
||||||
|
|
||||||
err = c.exception
|
|
||||||
self.assertIsInstance(err.__cause__, ZeroDivisionError)
|
|
||||||
|
|
||||||
def test_for_tuple(self):
|
def test_for_tuple(self):
|
||||||
class Done(Exception): pass
|
class Done(Exception): pass
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Drop support of asynchronous __aiter__.
|
|
@ -1141,100 +1141,6 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* __aiter__ wrapper; see http://bugs.python.org/issue27243 for details. */
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
PyObject *ags_aiter;
|
|
||||||
} PyAIterWrapper;
|
|
||||||
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
aiter_wrapper_iternext(PyAIterWrapper *aw)
|
|
||||||
{
|
|
||||||
_PyGen_SetStopIterationValue(aw->ags_aiter);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
aiter_wrapper_traverse(PyAIterWrapper *aw, visitproc visit, void *arg)
|
|
||||||
{
|
|
||||||
Py_VISIT((PyObject *)aw->ags_aiter);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
aiter_wrapper_dealloc(PyAIterWrapper *aw)
|
|
||||||
{
|
|
||||||
_PyObject_GC_UNTRACK((PyObject *)aw);
|
|
||||||
Py_CLEAR(aw->ags_aiter);
|
|
||||||
PyObject_GC_Del(aw);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PyAsyncMethods aiter_wrapper_as_async = {
|
|
||||||
PyObject_SelfIter, /* am_await */
|
|
||||||
0, /* am_aiter */
|
|
||||||
0 /* am_anext */
|
|
||||||
};
|
|
||||||
|
|
||||||
PyTypeObject _PyAIterWrapper_Type = {
|
|
||||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
|
||||||
"aiter_wrapper",
|
|
||||||
sizeof(PyAIterWrapper), /* tp_basicsize */
|
|
||||||
0, /* tp_itemsize */
|
|
||||||
(destructor)aiter_wrapper_dealloc, /* destructor tp_dealloc */
|
|
||||||
0, /* tp_print */
|
|
||||||
0, /* tp_getattr */
|
|
||||||
0, /* tp_setattr */
|
|
||||||
&aiter_wrapper_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 | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
||||||
"A wrapper object for __aiter__ bakwards compatibility.",
|
|
||||||
(traverseproc)aiter_wrapper_traverse, /* tp_traverse */
|
|
||||||
0, /* tp_clear */
|
|
||||||
0, /* tp_richcompare */
|
|
||||||
0, /* tp_weaklistoffset */
|
|
||||||
PyObject_SelfIter, /* tp_iter */
|
|
||||||
(iternextfunc)aiter_wrapper_iternext, /* 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 */
|
|
||||||
0, /* tp_free */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
_PyAIterWrapper_New(PyObject *aiter)
|
|
||||||
{
|
|
||||||
PyAIterWrapper *aw = PyObject_GC_New(PyAIterWrapper,
|
|
||||||
&_PyAIterWrapper_Type);
|
|
||||||
if (aw == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Py_INCREF(aiter);
|
|
||||||
aw->ags_aiter = aiter;
|
|
||||||
_PyObject_GC_TRACK(aw);
|
|
||||||
return (PyObject *)aw;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ========= Asynchronous Generators ========= */
|
/* ========= Asynchronous Generators ========= */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1708,7 +1708,6 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
TARGET(GET_AITER) {
|
TARGET(GET_AITER) {
|
||||||
unaryfunc getter = NULL;
|
unaryfunc getter = NULL;
|
||||||
PyObject *iter = NULL;
|
PyObject *iter = NULL;
|
||||||
PyObject *awaitable = NULL;
|
|
||||||
PyObject *obj = TOP();
|
PyObject *obj = TOP();
|
||||||
PyTypeObject *type = Py_TYPE(obj);
|
PyTypeObject *type = Py_TYPE(obj);
|
||||||
|
|
||||||
|
@ -1735,57 +1734,20 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Py_TYPE(iter)->tp_as_async != NULL &&
|
if (Py_TYPE(iter)->tp_as_async == NULL ||
|
||||||
Py_TYPE(iter)->tp_as_async->am_anext != 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) {
|
|
||||||
_PyErr_FormatFromCause(
|
|
||||||
PyExc_TypeError,
|
|
||||||
"'async for' received an invalid object "
|
|
||||||
"from __aiter__: %.100s",
|
|
||||||
Py_TYPE(iter)->tp_name);
|
|
||||||
|
|
||||||
SET_TOP(NULL);
|
SET_TOP(NULL);
|
||||||
|
PyErr_Format(
|
||||||
|
PyExc_TypeError,
|
||||||
|
"'async for' received an object from __aiter__ "
|
||||||
|
"that does not implement __anext__: %.100s",
|
||||||
|
Py_TYPE(iter)->tp_name);
|
||||||
Py_DECREF(iter);
|
Py_DECREF(iter);
|
||||||
goto error;
|
goto error;
|
||||||
} else {
|
|
||||||
Py_DECREF(iter);
|
|
||||||
|
|
||||||
if (PyErr_WarnFormat(
|
|
||||||
PyExc_DeprecationWarning, 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);
|
SET_TOP(iter);
|
||||||
PREDICT(LOAD_CONST);
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2298,8 +2298,6 @@ compiler_async_for(struct compiler *c, stmt_ty s)
|
||||||
|
|
||||||
VISIT(c, expr, s->v.AsyncFor.iter);
|
VISIT(c, expr, s->v.AsyncFor.iter);
|
||||||
ADDOP(c, GET_AITER);
|
ADDOP(c, GET_AITER);
|
||||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
|
||||||
ADDOP(c, YIELD_FROM);
|
|
||||||
|
|
||||||
compiler_use_next_block(c, try);
|
compiler_use_next_block(c, try);
|
||||||
|
|
||||||
|
@ -3867,8 +3865,6 @@ compiler_async_comprehension_generator(struct compiler *c,
|
||||||
/* Sub-iter - calculate on the fly */
|
/* Sub-iter - calculate on the fly */
|
||||||
VISIT(c, expr, gen->iter);
|
VISIT(c, expr, gen->iter);
|
||||||
ADDOP(c, GET_AITER);
|
ADDOP(c, GET_AITER);
|
||||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
|
||||||
ADDOP(c, YIELD_FROM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compiler_use_next_block(c, try);
|
compiler_use_next_block(c, try);
|
||||||
|
@ -4033,8 +4029,6 @@ compiler_comprehension(struct compiler *c, expr_ty e, int type,
|
||||||
|
|
||||||
if (outermost->is_async) {
|
if (outermost->is_async) {
|
||||||
ADDOP(c, GET_AITER);
|
ADDOP(c, GET_AITER);
|
||||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
|
||||||
ADDOP(c, YIELD_FROM);
|
|
||||||
} else {
|
} else {
|
||||||
ADDOP(c, GET_ITER);
|
ADDOP(c, GET_ITER);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue