Commit 626f79ae authored by Stefan Behnel's avatar Stefan Behnel

initial attempt to evaluate "@coroutine" at compile time by making the resulting coroutine iterable

Signed-off-by: default avatarStefan Behnel <stefan_ml@behnel.de>
parent fc6d44b9
...@@ -4054,6 +4054,7 @@ class GeneratorDefNode(DefNode): ...@@ -4054,6 +4054,7 @@ class GeneratorDefNode(DefNode):
is_generator = True is_generator = True
is_coroutine = False is_coroutine = False
is_iterable_coroutine = False
is_asyncgen = False is_asyncgen = False
gen_type_name = 'Generator' gen_type_name = 'Generator'
needs_closure = True needs_closure = True
...@@ -4106,6 +4107,11 @@ class AsyncDefNode(GeneratorDefNode): ...@@ -4106,6 +4107,11 @@ class AsyncDefNode(GeneratorDefNode):
is_coroutine = True is_coroutine = True
class IterableAsyncDefNode(AsyncDefNode):
gen_type_name = 'IterableCoroutine'
is_iterable_coroutine = True
class AsyncGenNode(AsyncDefNode): class AsyncGenNode(AsyncDefNode):
gen_type_name = 'AsyncGen' gen_type_name = 'AsyncGen'
is_asyncgen = True is_asyncgen = True
......
...@@ -2593,10 +2593,27 @@ class MarkClosureVisitor(CythonTransform): ...@@ -2593,10 +2593,27 @@ class MarkClosureVisitor(CythonTransform):
collector.visitchildren(node) collector.visitchildren(node)
if node.is_async_def: if node.is_async_def:
coroutine_type = Nodes.AsyncGenNode if collector.has_yield else Nodes.AsyncDefNode coroutine_type = Nodes.AsyncDefNode
if collector.has_yield: if collector.has_yield:
coroutine_type = Nodes.AsyncGenNode
for yield_expr in collector.yields + collector.returns: for yield_expr in collector.yields + collector.returns:
yield_expr.in_async_gen = True yield_expr.in_async_gen = True
elif node.decorators:
# evaluate @asyncio.coroutine() decorator at compile time if it's the inner-most one
# TODO: better decorator validation: should come from imported module
decorator = node.decorators[-1].decorator
if decorator.is_name and decorator.name == 'coroutine':
pass
elif decorator.is_attribute and decorator.attribute == 'coroutine':
if decorator.obj.is_name and decorator.obj.name in ('types', 'asyncio'):
pass
else:
decorator = None
else:
decorator = None
if decorator is not None:
node.decorators.pop()
coroutine_type = Nodes.IterableAsyncDefNode
elif collector.has_await: elif collector.has_await:
found = next(y for y in collector.yields if y.is_await) found = next(y for y in collector.yields if y.is_await)
error(found.pos, "'await' not allowed in generators (use 'yield')") error(found.pos, "'await' not allowed in generators (use 'yield')")
......
...@@ -15,7 +15,7 @@ static void __PyxPyIter_CheckErrorAndDecref(PyObject *source) { ...@@ -15,7 +15,7 @@ static void __PyxPyIter_CheckErrorAndDecref(PyObject *source) {
static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) { static CYTHON_INLINE PyObject* __Pyx_Generator_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
PyObject *source_gen, *retval; PyObject *source_gen, *retval;
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
if (__Pyx_Coroutine_CheckExact(source)) { if (__Pyx_Coroutine_Check(source)) {
// TODO: this should only happen for types.coroutine()ed generators, but we can't determine that here // TODO: this should only happen for types.coroutine()ed generators, but we can't determine that here
Py_INCREF(source); Py_INCREF(source);
source_gen = source; source_gen = source;
...@@ -71,7 +71,7 @@ static PyObject* __Pyx__Coroutine_Yield_From_Generic(__pyx_CoroutineObject *gen, ...@@ -71,7 +71,7 @@ static PyObject* __Pyx__Coroutine_Yield_From_Generic(__pyx_CoroutineObject *gen,
return NULL; return NULL;
} }
// source_gen is now the iterator, make the first next() call // source_gen is now the iterator, make the first next() call
if (__Pyx_Coroutine_CheckExact(source_gen)) { if (__Pyx_Coroutine_Check(source_gen)) {
retval = __Pyx_Generator_Next(source_gen); retval = __Pyx_Generator_Next(source_gen);
} else { } else {
#if CYTHON_USE_TYPE_SLOTS #if CYTHON_USE_TYPE_SLOTS
...@@ -90,7 +90,7 @@ static PyObject* __Pyx__Coroutine_Yield_From_Generic(__pyx_CoroutineObject *gen, ...@@ -90,7 +90,7 @@ static PyObject* __Pyx__Coroutine_Yield_From_Generic(__pyx_CoroutineObject *gen,
static CYTHON_INLINE PyObject* __Pyx_Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) { static CYTHON_INLINE PyObject* __Pyx_Coroutine_Yield_From(__pyx_CoroutineObject *gen, PyObject *source) {
PyObject *retval; PyObject *retval;
if (__Pyx_Coroutine_CheckExact(source)) { if (__Pyx_Coroutine_Check(source)) {
if (unlikely(((__pyx_CoroutineObject*)source)->yieldfrom)) { if (unlikely(((__pyx_CoroutineObject*)source)->yieldfrom)) {
PyErr_SetString( PyErr_SetString(
PyExc_RuntimeError, PyExc_RuntimeError,
...@@ -126,7 +126,7 @@ static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *o); /*proto*/ ...@@ -126,7 +126,7 @@ static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *o); /*proto*/
static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAwaitableIter(PyObject *o) { static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAwaitableIter(PyObject *o) {
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
if (__Pyx_Coroutine_CheckExact(o)) { if (__Pyx_Coroutine_Check(o)) {
return __Pyx_NewRef(o); return __Pyx_NewRef(o);
} }
#endif #endif
...@@ -219,7 +219,7 @@ static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *obj) { ...@@ -219,7 +219,7 @@ static PyObject *__Pyx__Coroutine_GetAwaitableIter(PyObject *obj) {
} else { } else {
int is_coroutine = 0; int is_coroutine = 0;
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
is_coroutine |= __Pyx_Coroutine_CheckExact(res); is_coroutine |= __Pyx_Coroutine_Check(res);
#endif #endif
#if PY_VERSION_HEX >= 0x030500B2 || defined(PyCoro_CheckExact) #if PY_VERSION_HEX >= 0x030500B2 || defined(PyCoro_CheckExact)
is_coroutine |= PyCoro_CheckExact(res); is_coroutine |= PyCoro_CheckExact(res);
...@@ -428,6 +428,8 @@ static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__pyx_CoroutineO ...@@ -428,6 +428,8 @@ static CYTHON_INLINE void __Pyx_Coroutine_ResetFrameBackpointer(__pyx_CoroutineO
static PyTypeObject *__pyx_CoroutineType = 0; static PyTypeObject *__pyx_CoroutineType = 0;
static PyTypeObject *__pyx_CoroutineAwaitType = 0; static PyTypeObject *__pyx_CoroutineAwaitType = 0;
#define __Pyx_Coroutine_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineType) #define __Pyx_Coroutine_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineType)
// __Pyx_Coroutine_Check(obj): see override for IterableCoroutine below
#define __Pyx_Coroutine_Check(obj) __Pyx_Coroutine_CheckExact(obj)
#define __Pyx_CoroutineAwait_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineAwaitType) #define __Pyx_CoroutineAwait_CheckExact(obj) (Py_TYPE(obj) == __pyx_CoroutineAwaitType)
#define __Pyx_Coroutine_New(body, code, closure, name, qualname, module_name) \ #define __Pyx_Coroutine_New(body, code, closure, name, qualname, module_name) \
...@@ -595,7 +597,7 @@ static void __Pyx__Coroutine_AlreadyRunningError(CYTHON_UNUSED __pyx_CoroutineOb ...@@ -595,7 +597,7 @@ static void __Pyx__Coroutine_AlreadyRunningError(CYTHON_UNUSED __pyx_CoroutineOb
const char *msg; const char *msg;
if (0) { if (0) {
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
} else if (__Pyx_Coroutine_CheckExact((PyObject*)gen)) { } else if (__Pyx_Coroutine_Check((PyObject*)gen)) {
msg = "coroutine already executing"; msg = "coroutine already executing";
#endif #endif
#ifdef __Pyx_AsyncGen_USED #ifdef __Pyx_AsyncGen_USED
...@@ -613,7 +615,7 @@ static void __Pyx__Coroutine_NotStartedError(CYTHON_UNUSED PyObject *gen) { ...@@ -613,7 +615,7 @@ static void __Pyx__Coroutine_NotStartedError(CYTHON_UNUSED PyObject *gen) {
const char *msg; const char *msg;
if (0) { if (0) {
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
} else if (__Pyx_Coroutine_CheckExact(gen)) { } else if (__Pyx_Coroutine_Check(gen)) {
msg = "can't send non-None value to a just-started coroutine"; msg = "can't send non-None value to a just-started coroutine";
#endif #endif
#ifdef __Pyx_AsyncGen_USED #ifdef __Pyx_AsyncGen_USED
...@@ -629,7 +631,7 @@ static void __Pyx__Coroutine_NotStartedError(CYTHON_UNUSED PyObject *gen) { ...@@ -629,7 +631,7 @@ static void __Pyx__Coroutine_NotStartedError(CYTHON_UNUSED PyObject *gen) {
#define __Pyx_Coroutine_AlreadyTerminatedError(gen, value, closing) (__Pyx__Coroutine_AlreadyTerminatedError(gen, value, closing), (PyObject*)NULL) #define __Pyx_Coroutine_AlreadyTerminatedError(gen, value, closing) (__Pyx__Coroutine_AlreadyTerminatedError(gen, value, closing), (PyObject*)NULL)
static void __Pyx__Coroutine_AlreadyTerminatedError(CYTHON_UNUSED PyObject *gen, PyObject *value, CYTHON_UNUSED int closing) { static void __Pyx__Coroutine_AlreadyTerminatedError(CYTHON_UNUSED PyObject *gen, PyObject *value, CYTHON_UNUSED int closing) {
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
if (!closing && __Pyx_Coroutine_CheckExact(gen)) { if (!closing && __Pyx_Coroutine_Check(gen)) {
// `self` is an exhausted coroutine: raise an error, // `self` is an exhausted coroutine: raise an error,
// except when called from gen_close(), which should // except when called from gen_close(), which should
// always be a silent method. // always be a silent method.
...@@ -775,7 +777,7 @@ static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) { ...@@ -775,7 +777,7 @@ static PyObject *__Pyx_Coroutine_Send(PyObject *self, PyObject *value) {
} else } else
#endif #endif
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
if (__Pyx_Coroutine_CheckExact(yf)) { if (__Pyx_Coroutine_Check(yf)) {
ret = __Pyx_Coroutine_Send(yf, value); ret = __Pyx_Coroutine_Send(yf, value);
} else } else
#endif #endif
...@@ -828,7 +830,7 @@ static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) { ...@@ -828,7 +830,7 @@ static int __Pyx_Coroutine_CloseIter(__pyx_CoroutineObject *gen, PyObject *yf) {
} else } else
#endif #endif
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
if (__Pyx_Coroutine_CheckExact(yf)) { if (__Pyx_Coroutine_Check(yf)) {
retval = __Pyx_Coroutine_Close(yf); retval = __Pyx_Coroutine_Close(yf);
if (!retval) if (!retval)
return -1; return -1;
...@@ -926,7 +928,7 @@ static PyObject *__Pyx_Coroutine_Close(PyObject *self) { ...@@ -926,7 +928,7 @@ static PyObject *__Pyx_Coroutine_Close(PyObject *self) {
Py_DECREF(retval); Py_DECREF(retval);
if ((0)) { if ((0)) {
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
} else if (__Pyx_Coroutine_CheckExact(self)) { } else if (__Pyx_Coroutine_Check(self)) {
msg = "coroutine ignored GeneratorExit"; msg = "coroutine ignored GeneratorExit";
#endif #endif
#ifdef __Pyx_AsyncGen_USED #ifdef __Pyx_AsyncGen_USED
...@@ -981,7 +983,7 @@ static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject ...@@ -981,7 +983,7 @@ static PyObject *__Pyx__Coroutine_Throw(PyObject *self, PyObject *typ, PyObject
|| __Pyx_Generator_CheckExact(yf) || __Pyx_Generator_CheckExact(yf)
#endif #endif
#ifdef __Pyx_Coroutine_USED #ifdef __Pyx_Coroutine_USED
|| __Pyx_Coroutine_CheckExact(yf) || __Pyx_Coroutine_Check(yf)
#endif #endif
) { ) {
ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit); ret = __Pyx__Coroutine_Throw(yf, typ, val, tb, args, close_on_genexit);
...@@ -1457,7 +1459,7 @@ static CYTHON_INLINE PyObject *__Pyx__Coroutine_await(PyObject *coroutine) { ...@@ -1457,7 +1459,7 @@ static CYTHON_INLINE PyObject *__Pyx__Coroutine_await(PyObject *coroutine) {
} }
static PyObject *__Pyx_Coroutine_await(PyObject *coroutine) { static PyObject *__Pyx_Coroutine_await(PyObject *coroutine) {
if (unlikely(!coroutine || !__Pyx_Coroutine_CheckExact(coroutine))) { if (unlikely(!coroutine || !__Pyx_Coroutine_Check(coroutine))) {
PyErr_SetString(PyExc_TypeError, "invalid input, expected coroutine"); PyErr_SetString(PyExc_TypeError, "invalid input, expected coroutine");
return NULL; return NULL;
} }
...@@ -1592,6 +1594,13 @@ static int __pyx_Coroutine_init(void) { ...@@ -1592,6 +1594,13 @@ static int __pyx_Coroutine_init(void) {
// on Windows, C-API functions can't be used in slots statically // on Windows, C-API functions can't be used in slots statically
__pyx_CoroutineType_type.tp_getattro = PyObject_GenericGetAttr; __pyx_CoroutineType_type.tp_getattro = PyObject_GenericGetAttr;
#ifdef __Pyx_IterableCoroutine_USED
__pyx_IterableCoroutineType_type.tp_getattro = PyObject_GenericGetAttr;
__pyx_IterableCoroutineType = __Pyx_FetchCommonType(&__pyx_IterableCoroutineType_type);
if (unlikely(!__pyx_IterableCoroutineType))
return -1;
#endif
__pyx_CoroutineType = __Pyx_FetchCommonType(&__pyx_CoroutineType_type); __pyx_CoroutineType = __Pyx_FetchCommonType(&__pyx_CoroutineType_type);
if (unlikely(!__pyx_CoroutineType)) if (unlikely(!__pyx_CoroutineType))
return -1; return -1;
...@@ -1602,6 +1611,91 @@ static int __pyx_Coroutine_init(void) { ...@@ -1602,6 +1611,91 @@ static int __pyx_Coroutine_init(void) {
return 0; return 0;
} }
//////////////////// IterableCoroutine.proto ////////////////////
#define __Pyx_IterableCoroutine_USED
static PyTypeObject *__pyx_IterableCoroutineType = 0;
#undef __Pyx_Coroutine_Check
#define __Pyx_Coroutine_Check(obj) (__Pyx_Coroutine_CheckExact(obj) || (Py_TYPE(obj) == __pyx_IterableCoroutineType))
#define __Pyx_IterableCoroutine_New(body, closure, name, qualname, module_name) \
__Pyx__Coroutine_New(__pyx_IterableCoroutineType, body, closure, name, qualname, module_name)
//////////////////// IterableCoroutine ////////////////////
//@requires: Coroutine
static PyTypeObject __pyx_IterableCoroutineType_type = {
PyVarObject_HEAD_INIT(0, 0)
"legacy_coroutine", /*tp_name*/
sizeof(__pyx_CoroutineObject), /*tp_basicsize*/
0, /*tp_itemsize*/
(destructor) __Pyx_Coroutine_dealloc,/*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
#if CYTHON_USE_ASYNC_SLOTS
&__pyx_Coroutine_as_async, /*tp_as_async (tp_reserved) - Py3 only! */
#else
0, /*tp_reserved*/
#endif
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
0, /*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) __Pyx_Coroutine_traverse, /*tp_traverse*/
0, /*tp_clear*/
#if CYTHON_USE_ASYNC_SLOTS && CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3 && PY_VERSION_HEX < 0x030500B1
// in order to (mis-)use tp_reserved above, we must also implement tp_richcompare
__Pyx_Coroutine_compare, /*tp_richcompare*/
#else
0, /*tp_richcompare*/
#endif
offsetof(__pyx_CoroutineObject, gi_weakreflist), /*tp_weaklistoffset*/
// enable iteration for legacy support of asyncio yield-from protocol
__Pyx_Coroutine_await, /*tp_iter*/
(iternextfunc) __Pyx_Generator_Next, /*tp_iternext*/
__pyx_Coroutine_methods, /*tp_methods*/
__pyx_Coroutine_memberlist, /*tp_members*/
__pyx_Coroutine_getsets, /*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*/
#if PY_VERSION_HEX >= 0x030400a1
0, /*tp_del*/
#else
__Pyx_Coroutine_del, /*tp_del*/
#endif
0, /*tp_version_tag*/
#if PY_VERSION_HEX >= 0x030400a1
__Pyx_Coroutine_del, /*tp_finalize*/
#endif
};
//////////////////// Generator //////////////////// //////////////////// Generator ////////////////////
//@requires: CoroutineBase //@requires: CoroutineBase
//@requires: PatchGeneratorABC //@requires: PatchGeneratorABC
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment