diff -r 38f75ebfe4cd Include/genobject.h
--- a/Include/genobject.h Tue Jun 09 18:42:36 2015 +0300
+++ b/Include/genobject.h Tue Jun 09 18:05:37 2015 -0400
@@ -38,12 +38,6 @@
#define PyGen_Check(op) PyObject_TypeCheck(op, &PyGen_Type)
#define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type)
-#define PyGen_CheckCoroutineExact(op) (PyGen_CheckExact(op) && \
- (((PyCodeObject*) \
- ((PyGenObject*)op)->gi_code) \
- ->co_flags & (CO_ITERABLE_COROUTINE | \
- CO_COROUTINE)))
-
PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *);
PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *,
PyObject *name, PyObject *qualname);
@@ -52,7 +46,13 @@
PyObject *_PyGen_Send(PyGenObject *, PyObject *);
PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
-PyObject *_PyGen_GetAwaitableIter(PyObject *o);
+#ifndef Py_LIMITED_API
+PyAPI_DATA(PyTypeObject) PyCoro_Type;
+#define PyCoro_CheckExact(op) (Py_TYPE(op) == &PyCoro_Type)
+PyAPI_FUNC(PyObject *) _PyCoro_GetAwaitableIter(PyObject *o);
+PyAPI_FUNC(PyObject *) PyCoro_New(struct _frame *,
+ PyObject *name, PyObject *qualname);
+#endif
#ifdef __cplusplus
}
diff -r 38f75ebfe4cd Include/opcode.h
--- a/Include/opcode.h Tue Jun 09 18:42:36 2015 +0300
+++ b/Include/opcode.h Tue Jun 09 18:05:37 2015 -0400
@@ -45,6 +45,7 @@
#define BINARY_OR 66
#define INPLACE_POWER 67
#define GET_ITER 68
+#define GET_YIELD_FROM_ITER 69
#define PRINT_EXPR 70
#define LOAD_BUILD_CLASS 71
#define YIELD_FROM 72
diff -r 38f75ebfe4cd Lib/_collections_abc.py
--- a/Lib/_collections_abc.py Tue Jun 09 18:42:36 2015 +0300
+++ b/Lib/_collections_abc.py Tue Jun 09 18:05:37 2015 -0400
@@ -53,6 +53,11 @@
mappingproxy = type(type.__dict__)
generator = type((lambda: (yield))())
+async def _coro(): pass
+_coro = _coro()
+coroutine = type(_coro)
+_coro.close() # Prevent ResourceWarning
+
### ONE-TRICK PONIES ###
@@ -78,17 +83,12 @@
class _AwaitableMeta(ABCMeta):
def __instancecheck__(cls, instance):
- # 0x80 = CO_COROUTINE
# 0x100 = CO_ITERABLE_COROUTINE
# We don't want to import 'inspect' module, as
# a dependency for 'collections.abc'.
- CO_COROUTINES = 0x80 | 0x100
-
if (isinstance(instance, generator) and
- instance.gi_code.co_flags & CO_COROUTINES):
-
+ instance.gi_code.co_flags & 0x100):
return True
-
return super().__instancecheck__(instance)
@@ -111,6 +111,9 @@
return NotImplemented
+Awaitable.register(coroutine)
+
+
class Coroutine(Awaitable):
__slots__ = ()
@@ -159,6 +162,9 @@
return NotImplemented
+Coroutine.register(coroutine)
+
+
class AsyncIterable(metaclass=ABCMeta):
__slots__ = ()
diff -r 38f75ebfe4cd Lib/opcode.py
--- a/Lib/opcode.py Tue Jun 09 18:42:36 2015 +0300
+++ b/Lib/opcode.py Tue Jun 09 18:05:37 2015 -0400
@@ -103,6 +103,7 @@
def_op('BINARY_OR', 66)
def_op('INPLACE_POWER', 67)
def_op('GET_ITER', 68)
+def_op('GET_YIELD_FROM_ITER', 69)
def_op('PRINT_EXPR', 70)
def_op('LOAD_BUILD_CLASS', 71)
diff -r 38f75ebfe4cd Lib/test/test_coroutines.py
--- a/Lib/test/test_coroutines.py Tue Jun 09 18:42:36 2015 +0300
+++ b/Lib/test/test_coroutines.py Tue Jun 09 18:05:37 2015 -0400
@@ -24,7 +24,7 @@
def run_async(coro):
- assert coro.__class__ is types.GeneratorType
+ assert coro.__class__ in {types.GeneratorType, types.CoroutineType}
buffer = []
result = None
@@ -37,6 +37,25 @@
return buffer, result
+def run_async__await__(coro):
+ assert coro.__class__ is types.CoroutineType
+ aw = coro.__await__()
+ buffer = []
+ result = None
+ i = 0
+ while True:
+ try:
+ if i % 2:
+ buffer.append(next(aw))
+ else:
+ buffer.append(aw.send(None))
+ i += 1
+ except StopIteration as ex:
+ result = ex.args[0] if ex.args else None
+ break
+ return buffer, result
+
+
@contextlib.contextmanager
def silence_coro_gc():
with warnings.catch_warnings():
@@ -121,13 +140,15 @@
return 10
f = foo()
- self.assertIsInstance(f, types.GeneratorType)
+ self.assertIsInstance(f, types.CoroutineType)
self.assertTrue(bool(foo.__code__.co_flags & 0x80))
self.assertTrue(bool(foo.__code__.co_flags & 0x20))
self.assertTrue(bool(f.gi_code.co_flags & 0x80))
self.assertTrue(bool(f.gi_code.co_flags & 0x20))
self.assertEqual(run_async(f), ([], 10))
+ self.assertEqual(run_async__await__(foo()), ([], 10))
+
def bar(): pass
self.assertFalse(bool(bar.__code__.co_flags & 0x80))
@@ -152,7 +173,7 @@
raise StopIteration
check = lambda: self.assertRaisesRegex(
- TypeError, "coroutine-objects do not support iteration")
+ TypeError, "'coroutine' object is not iterable")
with check():
list(foo())
@@ -166,9 +187,6 @@
with check():
iter(foo())
- with check():
- next(foo())
-
with silence_coro_gc(), check():
for i in foo():
pass
@@ -185,7 +203,7 @@
await bar()
check = lambda: self.assertRaisesRegex(
- TypeError, "coroutine-objects do not support iteration")
+ TypeError, "'coroutine' object is not iterable")
with check():
for el in foo(): pass
@@ -244,6 +262,41 @@
foo()
support.gc_collect()
+ def test_func_10(self):
+ N = 0
+
+ @types.coroutine
+ def gen():
+ nonlocal N
+ try:
+ a = yield
+ yield (a ** 2)
+ except ZeroDivisionError:
+ N += 100
+ raise
+ finally:
+ N += 1
+
+ async def foo():
+ await gen()
+
+ coro = foo()
+ aw = coro.__await__()
+ self.assertIs(aw, iter(aw))
+ next(aw)
+ self.assertEqual(aw.send(10), 100)
+
+ self.assertEqual(N, 0)
+ aw.close()
+ self.assertEqual(N, 1)
+
+ coro = foo()
+ aw = coro.__await__()
+ next(aw)
+ with self.assertRaises(ZeroDivisionError):
+ aw.throw(ZeroDivisionError, None, None)
+ self.assertEqual(N, 102)
+
def test_await_1(self):
async def foo():
@@ -262,6 +315,7 @@
await AsyncYieldFrom([1, 2, 3])
self.assertEqual(run_async(foo()), ([1, 2, 3], None))
+ self.assertEqual(run_async__await__(foo()), ([1, 2, 3], None))
def test_await_4(self):
async def bar():
diff -r 38f75ebfe4cd Lib/test/test_types.py
--- a/Lib/test/test_types.py Tue Jun 09 18:42:36 2015 +0300
+++ b/Lib/test/test_types.py Tue Jun 09 18:05:37 2015 -0400
@@ -1210,6 +1210,21 @@
'callable wrapped .* non-coroutine'):
foo()
+ def test_async_def(self):
+ # Test that types.coroutine passes 'async def' coroutines
+ # without modification
+
+ async def foo(): pass
+ foo_flags = foo.__code__.co_flags
+ self.assertIs(foo, types.coroutine(foo))
+ self.assertEqual(foo.__code__.co_flags, foo_flags)
+
+ @types.coroutine
+ def bar(): return foo()
+ coro = bar()
+ self.assertEqual(coro.gi_code.co_flags, foo_flags)
+ coro.close()
+
def test_duck_coro(self):
class CoroLike:
def send(self): pass
diff -r 38f75ebfe4cd Lib/types.py
--- a/Lib/types.py Tue Jun 09 18:42:36 2015 +0300
+++ b/Lib/types.py Tue Jun 09 18:05:37 2015 -0400
@@ -19,6 +19,11 @@
yield 1
GeneratorType = type(_g())
+async def _c(): pass
+_c = _c()
+CoroutineType = type(_c)
+_c.close() # Prevent ResourceWarning
+
class _C:
def _m(self): pass
MethodType = type(_C()._m)
@@ -40,7 +45,7 @@
GetSetDescriptorType = type(FunctionType.__code__)
MemberDescriptorType = type(FunctionType.__globals__)
-del sys, _f, _g, _C, # Not for export
+del sys, _f, _g, _C, _c, # Not for export
# Provide a PEP 3115 compliant mechanism for class creation
@@ -167,26 +172,30 @@
# We don't want to import 'dis' or 'inspect' just for
# these constants.
CO_GENERATOR = 0x20
+ CO_COROUTINE = 0x80
CO_ITERABLE_COROUTINE = 0x100
if not callable(func):
raise TypeError('types.coroutine() expects a callable')
if (isinstance(func, FunctionType) and
- isinstance(getattr(func, '__code__', None), CodeType) and
- (func.__code__.co_flags & CO_GENERATOR)):
+ isinstance(getattr(func, '__code__', None), CodeType)):
- # TODO: Implement this in C.
- co = func.__code__
- func.__code__ = CodeType(
- co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
- co.co_stacksize,
- co.co_flags | CO_ITERABLE_COROUTINE,
- co.co_code,
- co.co_consts, co.co_names, co.co_varnames, co.co_filename,
- co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
- co.co_cellvars)
- return func
+ if func.__code__.co_flags & CO_COROUTINE:
+ return func
+
+ if func.__code__.co_flags & CO_GENERATOR:
+ # TODO: Implement this in C.
+ co = func.__code__
+ func.__code__ = CodeType(
+ co.co_argcount, co.co_kwonlyargcount, co.co_nlocals,
+ co.co_stacksize,
+ co.co_flags | CO_ITERABLE_COROUTINE,
+ co.co_code,
+ co.co_consts, co.co_names, co.co_varnames, co.co_filename,
+ co.co_name, co.co_firstlineno, co.co_lnotab, co.co_freevars,
+ co.co_cellvars)
+ return func
# The following code is primarily to support functions that
# return generator-like objects (for instance generators
@@ -218,6 +227,8 @@
@_functools.wraps(func)
def wrapped(*args, **kwargs):
coro = func(*args, **kwargs)
+ if coro.__class__ is CoroutineType:
+ return coro
if coro.__class__ is GeneratorType:
return GeneratorWrapper(coro)
# slow checks
diff -r 38f75ebfe4cd Objects/genobject.c
--- a/Objects/genobject.c Tue Jun 09 18:42:36 2015 +0300
+++ b/Objects/genobject.c Tue Jun 09 18:05:37 2015 -0400
@@ -432,12 +432,6 @@
static PyObject *
gen_iternext(PyGenObject *gen)
{
- if (((PyCodeObject*)gen->gi_code)->co_flags & CO_COROUTINE) {
- PyErr_SetString(PyExc_TypeError,
- "coroutine-objects do not support iteration");
- return NULL;
- }
-
return gen_send_ex(gen, NULL, 0);
}
@@ -494,14 +488,8 @@
static PyObject *
gen_repr(PyGenObject *gen)
{
- if (PyGen_CheckCoroutineExact(gen)) {
- return PyUnicode_FromFormat("",
- gen->gi_qualname, gen);
- }
- else {
- return PyUnicode_FromFormat("",
- gen->gi_qualname, gen);
- }
+ return PyUnicode_FromFormat("",
+ gen->gi_qualname, gen);
}
static PyObject *
@@ -537,19 +525,6 @@
return op->gi_qualname;
}
-static PyObject *
-gen_get_iter(PyGenObject *gen)
-{
- if (((PyCodeObject*)gen->gi_code)->co_flags & CO_COROUTINE) {
- PyErr_SetString(PyExc_TypeError,
- "coroutine-objects do not support iteration");
- return NULL;
- }
-
- Py_INCREF(gen);
- return (PyObject *)gen;
-}
-
static int
gen_set_qualname(PyGenObject *op, PyObject *value)
{
@@ -619,7 +594,7 @@
0, /* tp_clear */
0, /* tp_richcompare */
offsetof(PyGenObject, gi_weakreflist), /* tp_weaklistoffset */
- (getiterfunc)gen_get_iter, /* tp_iter */
+ PyObject_SelfIter, /* tp_iter */
(iternextfunc)gen_iternext, /* tp_iternext */
gen_methods, /* tp_methods */
gen_memberlist, /* tp_members */
@@ -645,10 +620,11 @@
_PyGen_Finalize, /* tp_finalize */
};
-PyObject *
-PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname)
+static PyObject *
+gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
+ PyObject *name, PyObject *qualname)
{
- PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type);
+ PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
if (gen == NULL) {
Py_DECREF(f);
return NULL;
@@ -674,9 +650,15 @@
}
PyObject *
+PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname)
+{
+ return gen_new_with_qualname(&PyGen_Type, f, name, qualname);
+}
+
+PyObject *
PyGen_New(PyFrameObject *f)
{
- return PyGen_NewWithQualName(f, NULL, NULL);
+ return gen_new_with_qualname(&PyGen_Type, f, NULL, NULL);
}
int
@@ -697,6 +679,27 @@
return 0;
}
+/* Coroutine Object */
+
+static PyTypeObject PyCoroWrapper_Type;
+
+typedef struct {
+ PyObject_HEAD
+ PyGenObject *cw_coroutine;
+} PyCoroWrapper;
+
+static int
+gen_is_coroutine(PyObject *o)
+{
+ if (PyGen_CheckExact(o)) {
+ PyCodeObject *code = (PyCodeObject *)((PyGenObject*)o)->gi_code;
+ if (code->co_flags & CO_ITERABLE_COROUTINE) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
/*
* This helper function returns an awaitable for `o`:
* - `o` if `o` is a coroutine-object;
@@ -706,13 +709,13 @@
* an awaitable and returns NULL.
*/
PyObject *
-_PyGen_GetAwaitableIter(PyObject *o)
+_PyCoro_GetAwaitableIter(PyObject *o)
{
unaryfunc getter = NULL;
PyTypeObject *ot;
- if (PyGen_CheckCoroutineExact(o)) {
- /* Fast path. It's a central function for 'await'. */
+ if (/*PyCoro_CheckExact(o) || */gen_is_coroutine(o)) {
+ /* 'o' is a coroutine. */
Py_INCREF(o);
return o;
}
@@ -724,22 +727,19 @@
if (getter != NULL) {
PyObject *res = (*getter)(o);
if (res != NULL) {
- if (!PyIter_Check(res)) {
+ if (PyCoro_CheckExact(res) || gen_is_coroutine(res)) {
+ /* __await__ must return an *iterator*, not
+ a coroutine or another awaitable (see PEP 492) */
+ PyErr_SetString(PyExc_TypeError,
+ "__await__() returned a coroutine");
+ Py_CLEAR(res);
+ } else if (!PyIter_Check(res)) {
PyErr_Format(PyExc_TypeError,
"__await__() returned non-iterator "
"of type '%.100s'",
Py_TYPE(res)->tp_name);
Py_CLEAR(res);
}
- else {
- if (PyGen_CheckCoroutineExact(res)) {
- /* __await__ must return an *iterator*, not
- a coroutine or another awaitable (see PEP 492) */
- PyErr_SetString(PyExc_TypeError,
- "__await__() returned a coroutine");
- Py_CLEAR(res);
- }
- }
}
return res;
}
@@ -747,6 +747,200 @@
PyErr_Format(PyExc_TypeError,
"object %.100s can't be used in 'await' expression",
ot->tp_name);
-
return NULL;
}
+
+static PyObject *
+coro_repr(PyGenObject *gen)
+{
+ return PyUnicode_FromFormat("",
+ gen->gi_qualname, gen);
+}
+
+static PyObject *
+coro_await(PyGenObject *gen)
+{
+ PyCoroWrapper *cw = PyObject_GC_New(PyCoroWrapper, &PyCoroWrapper_Type);
+ if (cw == NULL) {
+ return NULL;
+ }
+ Py_INCREF(gen);
+ cw->cw_coroutine = gen;
+ _PyObject_GC_TRACK(cw);
+ return (PyObject *)cw;
+}
+
+static PyGetSetDef coro_getsetlist[] = {
+ {"__name__", (getter)gen_get_name, (setter)gen_set_name,
+ PyDoc_STR("name of the coroutine")},
+ {"__qualname__", (getter)gen_get_qualname, (setter)gen_set_qualname,
+ PyDoc_STR("qualified name of the coroutine")},
+ {NULL} /* Sentinel */
+};
+
+static PyMemberDef coro_memberlist[] = {
+ {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY},
+ {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY},
+ {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY},
+ {NULL} /* Sentinel */
+};
+
+static PyMethodDef coro_methods[] = {
+ {"send",(PyCFunction)_PyGen_Send, METH_O, send_doc},
+ {"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
+ {"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
+ {NULL, NULL} /* Sentinel */
+};
+
+static PyAsyncMethods coro_as_async = {
+ (unaryfunc)coro_await, /* am_await */
+ 0, /* am_aiter */
+ 0 /* am_anext */
+};
+
+PyTypeObject PyCoro_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "coroutine", /* tp_name */
+ sizeof(PyGenObject), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ /* methods */
+ (destructor)gen_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ &coro_as_async, /* tp_as_async */
+ (reprfunc)coro_repr, /* 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 |
+ Py_TPFLAGS_HAVE_FINALIZE, /* tp_flags */
+ 0, /* tp_doc */
+ (traverseproc)gen_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ offsetof(PyGenObject, gi_weakreflist), /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ coro_methods, /* tp_methods */
+ coro_memberlist, /* tp_members */
+ coro_getsetlist, /* 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 */
+ 0, /* tp_is_gc */
+ 0, /* tp_bases */
+ 0, /* tp_mro */
+ 0, /* tp_cache */
+ 0, /* tp_subclasses */
+ 0, /* tp_weaklist */
+ 0, /* tp_del */
+ 0, /* tp_version_tag */
+ _PyGen_Finalize, /* tp_finalize */
+};
+
+static void
+coro_wrapper_dealloc(PyCoroWrapper *cw)
+{
+ _PyObject_GC_UNTRACK((PyObject *)cw);
+ Py_CLEAR(cw->cw_coroutine);
+ PyObject_GC_Del(cw);
+}
+
+static PyObject *
+coro_wrapper_iternext(PyCoroWrapper *cw)
+{
+ return gen_send_ex(cw->cw_coroutine, NULL, 0);
+}
+
+static PyObject *
+coro_wrapper_send(PyCoroWrapper *cw, PyObject *arg)
+{
+ return gen_send_ex(cw->cw_coroutine, arg, 0);
+}
+
+static PyObject *
+coro_wrapper_throw(PyCoroWrapper *cw, PyObject *args)
+{
+ return gen_throw(cw->cw_coroutine, args);
+}
+
+static PyObject *
+coro_wrapper_close(PyCoroWrapper *cw, PyObject *args)
+{
+ return gen_close(cw->cw_coroutine, args);
+}
+
+static int
+coro_wrapper_traverse(PyCoroWrapper *cw, visitproc visit, void *arg)
+{
+ Py_VISIT(cw->cw_coroutine);
+ return 0;
+}
+
+static PyMethodDef coro_wrapper_methods[] = {
+ {"send",(PyCFunction)coro_wrapper_send, METH_O, send_doc},
+ {"throw",(PyCFunction)coro_wrapper_throw, METH_VARARGS, throw_doc},
+ {"close",(PyCFunction)coro_wrapper_close, METH_NOARGS, close_doc},
+ {NULL, NULL} /* Sentinel */
+};
+
+static PyTypeObject PyCoroWrapper_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "coroutine_wrapper",
+ sizeof(PyCoroWrapper), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)coro_wrapper_dealloc, /* destructor tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* 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 implementing __await__ for coroutines.",
+ (traverseproc)coro_wrapper_traverse, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ PyObject_SelfIter, /* tp_iter */
+ (iternextfunc)coro_wrapper_iternext, /* tp_iternext */
+ coro_wrapper_methods, /* 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 */
+ PyObject_Del, /* tp_free */
+};
+
+PyObject *
+PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
+{
+ return gen_new_with_qualname(&PyCoro_Type, f, name, qualname);
+}
diff -r 38f75ebfe4cd Python/ceval.c
--- a/Python/ceval.c Tue Jun 09 18:42:36 2015 +0300
+++ b/Python/ceval.c Tue Jun 09 18:05:37 2015 -0400
@@ -1955,7 +1955,7 @@
goto error;
}
- awaitable = _PyGen_GetAwaitableIter(iter);
+ awaitable = _PyCoro_GetAwaitableIter(iter);
if (awaitable == NULL) {
SET_TOP(NULL);
PyErr_Format(
@@ -1998,7 +1998,7 @@
goto error;
}
- awaitable = _PyGen_GetAwaitableIter(next_iter);
+ awaitable = _PyCoro_GetAwaitableIter(next_iter);
if (awaitable == NULL) {
PyErr_Format(
PyExc_TypeError,
@@ -2017,7 +2017,7 @@
TARGET(GET_AWAITABLE) {
PyObject *iterable = TOP();
- PyObject *iter = _PyGen_GetAwaitableIter(iterable);
+ PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
Py_DECREF(iterable);
@@ -2034,25 +2034,7 @@
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;
- }
+ if (PyGen_CheckExact(reciever) || PyCoro_CheckExact(reciever)) {
retval = _PyGen_Send((PyGenObject *)reciever, v);
} else {
_Py_IDENTIFIER(send);
@@ -2929,19 +2911,33 @@
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;
+ PREDICT(FOR_ITER);
+ DISPATCH();
+ }
+
+ TARGET(GET_YIELD_FROM_ITER) {
+ /* before: [obj]; after [getiter(obj)] */
+ PyObject *iterable = TOP();
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)) {
+ if (PyCoro_CheckExact(iterable)) {
+ /* `iterable` is a coroutine */
+ if (!(co->co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE))) {
+ /* and it is used in a 'yield from' expression of a
+ regular generator. */
+ Py_DECREF(iterable);
+ SET_TOP(NULL);
+ PyErr_SetString(PyExc_TypeError,
+ "cannot 'yield from' a coroutine object "
+ "from a generator");
+ goto error;
+ }
+ }
+ else if (!PyGen_CheckExact(iterable)) {
/* `iterable` is not a generator. */
iter = PyObject_GetIter(iterable);
Py_DECREF(iterable);
@@ -2949,7 +2945,6 @@
if (iter == NULL)
goto error;
}
- PREDICT(FOR_ITER);
DISPATCH();
}
@@ -3942,7 +3937,11 @@
/* Create a new generator that owns the ready to run frame
* and return that as the value. */
- gen = PyGen_NewWithQualName(f, name, qualname);
+ if (co->co_flags & CO_COROUTINE) {
+ gen = PyCoro_New(f, name, qualname);
+ } else {
+ gen = PyGen_NewWithQualName(f, name, qualname);
+ }
if (gen == NULL)
return NULL;
diff -r 38f75ebfe4cd Python/compile.c
--- a/Python/compile.c Tue Jun 09 18:42:36 2015 +0300
+++ b/Python/compile.c Tue Jun 09 18:05:37 2015 -0400
@@ -1064,6 +1064,8 @@
return 0;
case GET_ANEXT:
return 1;
+ case GET_YIELD_FROM_ITER:
+ return 0;
default:
return PY_INVALID_STACK_EFFECT;
}
@@ -3850,7 +3852,7 @@
return compiler_error(c, "'yield from' inside async function");
VISIT(c, expr, e->v.YieldFrom.value);
- ADDOP(c, GET_ITER);
+ ADDOP(c, GET_YIELD_FROM_ITER);
ADDOP_O(c, LOAD_CONST, Py_None, consts);
ADDOP(c, YIELD_FROM);
break;
diff -r 38f75ebfe4cd Python/opcode_targets.h
--- a/Python/opcode_targets.h Tue Jun 09 18:42:36 2015 +0300
+++ b/Python/opcode_targets.h Tue Jun 09 18:05:37 2015 -0400
@@ -68,7 +68,7 @@
&&TARGET_BINARY_OR,
&&TARGET_INPLACE_POWER,
&&TARGET_GET_ITER,
- &&_unknown_opcode,
+ &&TARGET_GET_YIELD_FROM_ITER,
&&TARGET_PRINT_EXPR,
&&TARGET_LOAD_BUILD_CLASS,
&&TARGET_YIELD_FROM,