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 12:35:35 2015 -0400
@@ -38,11 +38,10 @@
#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)))
+#define PyGen_IsCoroutine(op) (PyGen_CheckExact(op) && \
+ (((PyCodeObject*) \
+ ((PyGenObject*)op)->gi_code) \
+ ->co_flags & CO_ITERABLE_COROUTINE))
PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *);
PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *,
@@ -54,6 +53,15 @@
PyObject *_PyGen_GetAwaitableIter(PyObject *o);
+PyAPI_DATA(PyTypeObject) PyCoro_Type;
+
+#define PyCoro_CheckExact(op) (Py_TYPE(op) == &PyCoro_Type)
+
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(PyObject *) PyCoro_NewWithQualName(struct _frame *,
+ PyObject *name, PyObject *qualname);
+#endif
+
#ifdef __cplusplus
}
#endif
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 12:35:35 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 12:35:35 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 12:35:35 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 12:35:35 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
@@ -121,7 +121,7 @@
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))
@@ -152,7 +152,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 +166,6 @@
with check():
iter(foo())
- with check():
- next(foo())
-
with silence_coro_gc(), check():
for i in foo():
pass
@@ -185,7 +182,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
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 12:35:35 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_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
+
+ if func.__code__.co_flags & CO_COROUTINE:
+ 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 12:35:35 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("