diff options
| -rw-r--r-- | Cython/Compiler/Builtin.py | 7 | ||||
| -rw-r--r-- | Cython/Compiler/Code.py | 1 | ||||
| -rw-r--r-- | Cython/Compiler/ExprNodes.py | 151 | ||||
| -rw-r--r-- | Cython/Compiler/FlowControl.py | 6 | ||||
| -rw-r--r-- | Cython/Compiler/ModuleNode.py | 2 | ||||
| -rw-r--r-- | Cython/Compiler/Nodes.py | 64 | ||||
| -rw-r--r-- | Cython/Compiler/Parsing.pxd | 7 | ||||
| -rw-r--r-- | Cython/Compiler/Parsing.py | 30 | ||||
| -rw-r--r-- | Cython/Utility/Coroutine.c | 149 | ||||
| -rw-r--r-- | tests/run/test_coroutines_pep492.pyx | 6 |
10 files changed, 356 insertions, 67 deletions
diff --git a/Cython/Compiler/Builtin.py b/Cython/Compiler/Builtin.py index b518abbd7..843f83edc 100644 --- a/Cython/Compiler/Builtin.py +++ b/Cython/Compiler/Builtin.py @@ -398,9 +398,16 @@ def init_builtins(): init_builtin_structs() init_builtin_types() init_builtin_funcs() + builtin_scope.declare_var( '__debug__', PyrexTypes.c_const_type(PyrexTypes.c_bint_type), pos=None, cname='(!Py_OptimizeFlag)', is_cdef=True) + + entry = builtin_scope.declare_var( + 'StopAsyncIteration', PyrexTypes.py_object_type, + pos=None, cname='__Pyx_PyExc_StopAsyncIteration') + entry.utility_code = UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c") + global list_type, tuple_type, dict_type, set_type, frozenset_type global bytes_type, str_type, unicode_type, basestring_type, slice_type global float_type, bool_type, type_type, complex_type, bytearray_type diff --git a/Cython/Compiler/Code.py b/Cython/Compiler/Code.py index 8aa64b3e2..99587e4aa 100644 --- a/Cython/Compiler/Code.py +++ b/Cython/Compiler/Code.py @@ -49,7 +49,6 @@ non_portable_builtins_map = { 'basestring' : ('PY_MAJOR_VERSION >= 3', 'str'), 'xrange' : ('PY_MAJOR_VERSION >= 3', 'range'), 'raw_input' : ('PY_MAJOR_VERSION >= 3', 'input'), - 'StopAsyncIteration': ('PY_VERSION_HEX < 0x030500B1', 'StopIteration'), } basicsize_builtins_map = { diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index c2f67beed..12a76b832 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -2298,6 +2298,7 @@ class IteratorNode(ExprNode): counter_cname = None cpp_iterator_cname = None reversed = False # currently only used for list/tuple types (see Optimize.py) + is_async = False subexprs = ['sequence'] @@ -2311,8 +2312,7 @@ class IteratorNode(ExprNode): self.analyse_cpp_types(env) else: self.sequence = self.sequence.coerce_to_pyobject(env) - if self.sequence.type is list_type or \ - self.sequence.type is tuple_type: + if self.sequence.type in (list_type, tuple_type): self.sequence = self.sequence.as_none_safe_node("'NoneType' object is not iterable") self.is_temp = 1 return self @@ -2400,8 +2400,8 @@ class IteratorNode(ExprNode): return if sequence_type.is_array or sequence_type.is_ptr: raise InternalError("for in carray slice not transformed") - is_builtin_sequence = sequence_type is list_type or \ - sequence_type is tuple_type + + is_builtin_sequence = sequence_type in (list_type, tuple_type) if not is_builtin_sequence: # reversed() not currently optimised (see Optimize.py) assert not self.reversed, "internal error: reversed() only implemented for list/tuple objects" @@ -2411,6 +2411,7 @@ class IteratorNode(ExprNode): "if (likely(PyList_CheckExact(%s)) || PyTuple_CheckExact(%s)) {" % ( self.sequence.py_result(), self.sequence.py_result())) + if is_builtin_sequence or self.may_be_a_sequence: self.counter_cname = code.funcstate.allocate_temp( PyrexTypes.c_py_ssize_t_type, manage_ref=False) @@ -2421,25 +2422,25 @@ class IteratorNode(ExprNode): init_value = 'PyTuple_GET_SIZE(%s) - 1' % self.result() else: init_value = '0' - code.putln( - "%s = %s; __Pyx_INCREF(%s); %s = %s;" % ( - self.result(), - self.sequence.py_result(), - self.result(), - self.counter_cname, - init_value - )) + code.putln("%s = %s; __Pyx_INCREF(%s); %s = %s;" % ( + self.result(), + self.sequence.py_result(), + self.result(), + self.counter_cname, + init_value)) if not is_builtin_sequence: self.iter_func_ptr = code.funcstate.allocate_temp(self._func_iternext_type, manage_ref=False) if self.may_be_a_sequence: code.putln("%s = NULL;" % self.iter_func_ptr) code.putln("} else {") code.put("%s = -1; " % self.counter_cname) + code.putln("%s = PyObject_GetIter(%s); %s" % ( - self.result(), - self.sequence.py_result(), - code.error_goto_if_null(self.result(), self.pos))) + self.result(), + self.sequence.py_result(), + code.error_goto_if_null(self.result(), self.pos))) code.put_gotref(self.py_result()) + # PyObject_GetIter() fails if "tp_iternext" is not set, but the check below # makes it visible to the C compiler that the pointer really isn't NULL, so that # it can distinguish between the special cases and the generic case @@ -2553,7 +2554,7 @@ class IteratorNode(ExprNode): class NextNode(AtomicExprNode): # Used as part of for statement implementation. - # Implements result = iterator.next() + # Implements result = next(iterator) # Created during analyse_types phase. # The iterator is not owned by this node. # @@ -2566,7 +2567,7 @@ class NextNode(AtomicExprNode): def type_dependencies(self, env): return self.iterator.type_dependencies(env) - def infer_type(self, env, iterator_type = None): + def infer_type(self, env, iterator_type=None): if iterator_type is None: iterator_type = self.iterator.infer_type(env) if iterator_type.is_ptr or iterator_type.is_array: @@ -2596,13 +2597,75 @@ class NextNode(AtomicExprNode): self.iterator.generate_iter_next_result_code(self.result(), code) +class AsyncIteratorNode(ExprNode): + # Used as part of 'async for' statement implementation. + # + # Implements result = sequence.__aiter__() + # + # sequence ExprNode + + subexprs = ['sequence'] + + is_async = True + type = py_object_type + is_temp = 1 + + def infer_type(self, env): + return py_object_type + + def analyse_types(self, env): + self.sequence = self.sequence.analyse_types(env) + if not self.sequence.type.is_pyobject: + error(self.pos, "async for loops not allowed on C/C++ types") + self.sequence = self.sequence.coerce_to_pyobject(env) + return self + + def generate_result_code(self, code): + code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c")) + code.putln("%s = __Pyx_Coroutine_GetAsyncIter(%s); %s" % ( + self.result(), + self.sequence.py_result(), + code.error_goto_if_null(self.result(), self.pos))) + code.put_gotref(self.result()) + + +class AsyncNextNode(AtomicExprNode): + # Used as part of 'async for' statement implementation. + # Implements result = iterator.__anext__() + # Created during analyse_types phase. + # The iterator is not owned by this node. + # + # iterator IteratorNode + + type = py_object_type + is_temp = 1 + + def __init__(self, iterator): + AtomicExprNode.__init__(self, iterator.pos) + self.iterator = iterator + + def infer_type(self, env): + return py_object_type + + def analyse_types(self, env): + return self + + def generate_result_code(self, code): + code.globalstate.use_utility_code(UtilityCode.load_cached("AsyncIter", "Coroutine.c")) + code.putln("%s = __Pyx_Coroutine_AsyncIterNext(%s); %s" % ( + self.result(), + self.iterator.py_result(), + code.error_goto_if_null(self.result(), self.pos))) + code.put_gotref(self.result()) + + class WithExitCallNode(ExprNode): # The __exit__() call of a 'with' statement. Used in both the # except and finally clauses. # with_stat WithStatNode the surrounding 'with' statement # args TupleNode or ResultStatNode the exception info tuple - # await AwaitExprNode the await + # await AwaitExprNode the await expression of an 'async with' statement subexprs = ['args', 'await'] test_if_run = True @@ -2639,7 +2702,8 @@ class WithExitCallNode(ExprNode): code.put_gotref(result_var) if self.await: - self.await.generate_evaluation_code(code, source_cname=result_var) + # FIXME: result_var temp currently leaks into the closure + self.await.generate_evaluation_code(code, source_cname=result_var, decref_source=True) code.putln("%s = %s;" % (result_var, self.await.py_result())) self.await.generate_post_assignment_code(code) self.await.free_temps(code) @@ -8686,7 +8750,7 @@ class YieldFromExprNode(YieldExprNode): code.globalstate.use_utility_code(UtilityCode.load_cached("GeneratorYieldFrom", "Coroutine.c")) return "__Pyx_Generator_Yield_From" - def generate_evaluation_code(self, code, source_cname=None): + def generate_evaluation_code(self, code, source_cname=None, decref_source=False): if source_cname is None: self.arg.generate_evaluation_code(code) code.putln("%s = %s(%s, %s);" % ( @@ -8697,7 +8761,7 @@ class YieldFromExprNode(YieldExprNode): if source_cname is None: self.arg.generate_disposal_code(code) self.arg.free_temps(code) - else: + elif decref_source: code.put_decref_clear(source_cname, py_object_type) code.put_xgotref(Naming.retval_cname) @@ -8706,17 +8770,23 @@ class YieldFromExprNode(YieldExprNode): code.putln("} else {") # either error or sub-generator has normally terminated: return value => node result if self.result_is_used: - # YieldExprNode has allocated the result temp for us - code.putln("%s = NULL;" % self.result()) - code.put_error_if_neg(self.pos, "__Pyx_PyGen_FetchStopIterationValue(&%s)" % self.result()) - code.put_gotref(self.result()) + self.fetch_iteration_result(code) else: - code.putln("PyObject* exc_type = PyErr_Occurred();") - code.putln("if (exc_type) {") - code.putln("if (likely(exc_type == PyExc_StopIteration ||" - " PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();") - code.putln("else %s" % code.error_goto(self.pos)) - code.putln("}") + self.handle_iteration_exception(code) + code.putln("}") + + def fetch_iteration_result(self, code): + # YieldExprNode has allocated the result temp for us + code.putln("%s = NULL;" % self.result()) + code.put_error_if_neg(self.pos, "__Pyx_PyGen_FetchStopIterationValue(&%s)" % self.result()) + code.put_gotref(self.result()) + + def handle_iteration_exception(self, code): + code.putln("PyObject* exc_type = PyErr_Occurred();") + code.putln("if (exc_type) {") + code.putln("if (likely(exc_type == PyExc_StopIteration ||" + " PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();") + code.putln("else %s" % code.error_goto(self.pos)) code.putln("}") @@ -8725,7 +8795,7 @@ class AwaitExprNode(YieldFromExprNode): # # arg ExprNode the Awaitable value to await # label_num integer yield label number - # is_yield_from boolean is a YieldFromExprNode to delegate to another generator + is_await = True expr_keyword = 'await' @@ -8739,6 +8809,23 @@ class AwaitExprNode(YieldFromExprNode): return "__Pyx_Coroutine_Yield_From" +class AwaitIterNextExprNode(AwaitExprNode): + # 'await' expression node as part of 'async for' iteration + # + # Breaks out of loop on StopAsyncIteration exception. + + def fetch_iteration_result(self, code): + assert code.break_label, "AwaitIterNextExprNode outside of 'async for' loop" + code.globalstate.use_utility_code(UtilityCode.load_cached("StopAsyncIteration", "Coroutine.c")) + code.putln("PyObject* exc_type = PyErr_Occurred();") + code.putln("if (exc_type && likely(exc_type == __Pyx_PyExc_StopAsyncIteration ||" + " PyErr_GivenExceptionMatches(exc_type, __Pyx_PyExc_StopAsyncIteration))) {") + code.putln("PyErr_Clear();") + code.putln("break;") + code.putln("}") + super(AwaitIterNextExprNode, self).fetch_iteration_result(code) + + class GlobalsExprNode(AtomicExprNode): type = dict_type is_temp = 1 diff --git a/Cython/Compiler/FlowControl.py b/Cython/Compiler/FlowControl.py index e0569a0fa..3d6b17a07 100644 --- a/Cython/Compiler/FlowControl.py +++ b/Cython/Compiler/FlowControl.py @@ -991,6 +991,9 @@ class ControlFlowAnalysis(CythonTransform): self.mark_assignment(target, node.item) + def visit_AsyncForStatNode(self, node): + return self.visit_ForInStatNode(node) + def visit_ForInStatNode(self, node): condition_block = self.flow.nextblock() next_block = self.flow.newblock() @@ -1002,6 +1005,9 @@ class ControlFlowAnalysis(CythonTransform): if isinstance(node, Nodes.ForInStatNode): self.mark_forloop_target(node) + elif isinstance(node, Nodes.AsyncForStatNode): + # not entirely correct, but good enough for now + self.mark_assignment(node.target, node.item) else: # Parallel self.mark_assignment(node.target) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index 5aaae67e7..42a63b40e 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -2071,7 +2071,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): code.putln("%s = PyBytes_FromStringAndSize(\"\", 0); %s" % ( Naming.empty_bytes, code.error_goto_if_null(Naming.empty_bytes, self.pos))) - for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator'): + for ext_type in ('CyFunction', 'FusedFunction', 'Coroutine', 'Generator', 'StopAsyncIteration'): code.putln("#ifdef __Pyx_%s_USED" % ext_type) code.put_error_if_neg(self.pos, "__pyx_%s_init()" % ext_type) code.putln("#endif") diff --git a/Cython/Compiler/Nodes.py b/Cython/Compiler/Nodes.py index ddd43f5ba..2694ca8ba 100644 --- a/Cython/Compiler/Nodes.py +++ b/Cython/Compiler/Nodes.py @@ -6069,40 +6069,49 @@ class DictIterationNextNode(Node): target.generate_assignment_code(result, code) var.release(code) + def ForStatNode(pos, **kw): if 'iterator' in kw: - return ForInStatNode(pos, **kw) + if kw['iterator'].is_async: + return AsyncForStatNode(pos, **kw) + else: + return ForInStatNode(pos, **kw) else: return ForFromStatNode(pos, **kw) -class ForInStatNode(LoopNode, StatNode): - # for statement + +class _ForInStatNode(LoopNode, StatNode): + # Base class of 'for-in' statements. # # target ExprNode - # iterator IteratorNode + # iterator IteratorNode | AwaitExprNode(AsyncIteratorNode) # body StatNode # else_clause StatNode - # item NextNode used internally + # item NextNode | AwaitExprNode(AsyncNextNode) + # is_async boolean true for 'async for' statements - child_attrs = ["target", "iterator", "body", "else_clause"] + child_attrs = ["target", "item", "iterator", "body", "else_clause"] item = None + is_async = False + + def _create_item_node(self): + raise NotImplementedError("must be implemented by subclasses") def analyse_declarations(self, env): - from . import ExprNodes self.target.analyse_target_declaration(env) self.body.analyse_declarations(env) if self.else_clause: self.else_clause.analyse_declarations(env) - self.item = ExprNodes.NextNode(self.iterator) + self._create_item_node() def analyse_expressions(self, env): self.target = self.target.analyse_target_types(env) self.iterator = self.iterator.analyse_expressions(env) - from . import ExprNodes - self.item = ExprNodes.NextNode(self.iterator) # must rewrap after analysis + self._create_item_node() # must rewrap self.item after analysis self.item = self.item.analyse_expressions(env) - if (self.iterator.type.is_ptr or self.iterator.type.is_array) and \ - self.target.type.assignable_from(self.iterator.type): + if (not self.is_async and + (self.iterator.type.is_ptr or self.iterator.type.is_array) and + self.target.type.assignable_from(self.iterator.type)): # C array slice optimization. pass else: @@ -6168,6 +6177,37 @@ class ForInStatNode(LoopNode, StatNode): self.item.annotate(code) +class ForInStatNode(_ForInStatNode): + # 'for' statement + + is_async = False + + def _create_item_node(self): + from .ExprNodes import NextNode + self.item = NextNode(self.iterator) + + +class AsyncForStatNode(_ForInStatNode): + # 'async for' statement + # + # iterator AwaitExprNode(AsyncIteratorNode) + # item AwaitIterNextExprNode(AsyncIteratorNode) + + is_async = True + + def __init__(self, pos, iterator, **kw): + assert 'item' not in kw + from . import ExprNodes + # AwaitExprNodes must appear before running MarkClosureVisitor + kw['iterator'] = ExprNodes.AwaitExprNode(iterator.pos, arg=iterator) + kw['item'] = ExprNodes.AwaitIterNextExprNode(iterator.pos, arg=None) + _ForInStatNode.__init__(self, pos, **kw) + + def _create_item_node(self): + from . import ExprNodes + self.item.arg = ExprNodes.AsyncNextNode(self.iterator) + + class ForFromStatNode(LoopNode, StatNode): # for name from expr rel name rel expr # diff --git a/Cython/Compiler/Parsing.pxd b/Cython/Compiler/Parsing.pxd index 1b766a7fe..5c431ad97 100644 --- a/Cython/Compiler/Parsing.pxd +++ b/Cython/Compiler/Parsing.pxd @@ -44,7 +44,6 @@ cdef p_typecast(PyrexScanner s) cdef p_sizeof(PyrexScanner s) cdef p_yield_expression(PyrexScanner s) cdef p_yield_statement(PyrexScanner s) -cdef p_await_expression(PyrexScanner s) cdef p_async_statement(PyrexScanner s, ctx) cdef p_power(PyrexScanner s) cdef p_new_expr(PyrexScanner s) @@ -109,13 +108,13 @@ cdef p_if_statement(PyrexScanner s) cdef p_if_clause(PyrexScanner s) cdef p_else_clause(PyrexScanner s) cdef p_while_statement(PyrexScanner s) -cdef p_for_statement(PyrexScanner s) -cdef dict p_for_bounds(PyrexScanner s, bint allow_testlist = *) +cdef p_for_statement(PyrexScanner s, bint is_async=*) +cdef dict p_for_bounds(PyrexScanner s, bint allow_testlist=*, bint is_async=*) cdef p_for_from_relation(PyrexScanner s) cdef p_for_from_step(PyrexScanner s) cdef p_target(PyrexScanner s, terminator) cdef p_for_target(PyrexScanner s) -cdef p_for_iterator(PyrexScanner s, bint allow_testlist = *) +cdef p_for_iterator(PyrexScanner s, bint allow_testlist=*, bint is_async=*) cdef p_try_statement(PyrexScanner s) cdef p_except_clause(PyrexScanner s) cdef p_include_statement(PyrexScanner s, ctx) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 3e008bee3..1ce20c78d 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -390,8 +390,7 @@ def p_async_statement(s, ctx, decorators): elif decorators: s.error("Decorators can only be followed by functions or classes") elif s.sy == 'for': - #s.error("'async for' is not currently supported", fatal=False) - return p_statement(s, ctx) # TODO: implement + return p_for_statement(s, is_async=True) elif s.sy == 'with': s.next() return p_with_items(s, is_async=True) @@ -399,10 +398,6 @@ def p_async_statement(s, ctx, decorators): s.error("expected one of 'def', 'for', 'with' after 'async'") -def p_await_expression(s): - n1 = p_atom(s) - - #power: atom_expr ('**' factor)* #atom_expr: ['await'] atom trailer* @@ -1604,23 +1599,25 @@ def p_while_statement(s): condition = test, body = body, else_clause = else_clause) -def p_for_statement(s): + +def p_for_statement(s, is_async=False): # s.sy == 'for' pos = s.position() s.next() - kw = p_for_bounds(s, allow_testlist=True) + kw = p_for_bounds(s, allow_testlist=True, is_async=is_async) body = p_suite(s) else_clause = p_else_clause(s) - kw.update(body = body, else_clause = else_clause) + kw.update(body=body, else_clause=else_clause, is_async=is_async) return Nodes.ForStatNode(pos, **kw) -def p_for_bounds(s, allow_testlist=True): + +def p_for_bounds(s, allow_testlist=True, is_async=False): target = p_for_target(s) if s.sy == 'in': s.next() - iterator = p_for_iterator(s, allow_testlist) - return dict( target = target, iterator = iterator ) - elif not s.in_python_file: + iterator = p_for_iterator(s, allow_testlist, is_async=is_async) + return dict(target=target, iterator=iterator) + elif not s.in_python_file and not is_async: if s.sy == 'from': s.next() bound1 = p_bit_expr(s) @@ -1690,16 +1687,19 @@ def p_target(s, terminator): else: return expr + def p_for_target(s): return p_target(s, 'in') -def p_for_iterator(s, allow_testlist=True): + +def p_for_iterator(s, allow_testlist=True, is_async=False): pos = s.position() if allow_testlist: expr = p_testlist(s) else: expr = p_or_test(s) - return ExprNodes.IteratorNode(pos, sequence = expr) + return (ExprNodes.AsyncIteratorNode if is_async else ExprNodes.IteratorNode)(pos, sequence=expr) + def p_try_statement(s): # s.sy == 'try' diff --git a/Cython/Utility/Coroutine.c b/Cython/Utility/Coroutine.c index 4d2e51987..a2fef160e 100644 --- a/Cython/Utility/Coroutine.c +++ b/Cython/Utility/Coroutine.c @@ -155,6 +155,56 @@ bad: } +//////////////////// AsyncIter.proto //////////////////// + +static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *o); /*proto*/ +static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *o); /*proto*/ + +//////////////////// AsyncIter //////////////////// +//@requires: GetAwaitIter +//@requires: ObjectHandling.c::PyObjectCallMethod0 + +static CYTHON_INLINE PyObject *__Pyx_Coroutine_GetAsyncIter(PyObject *obj) { +#if PY_VERSION_HEX >= 0x030500B1 + PyAsyncMethods* am = Py_TYPE(obj)->tp_as_async; + if (likely(am && am->am_aiter)) { + return (*am->am_aiter)(obj); + } +#else + PyObject *iter = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__aiter__")); + if (likely(iter)) + return iter; + // FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__aiter__' + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + return NULL; +#endif + + PyErr_Format(PyExc_TypeError, "'async for' requires an object with __aiter__ method, got %.100s", + Py_TYPE(obj)->tp_name); + return NULL; +} + +static CYTHON_INLINE PyObject *__Pyx_Coroutine_AsyncIterNext(PyObject *obj) { +#if PY_VERSION_HEX >= 0x030500B1 + PyAsyncMethods* am = Py_TYPE(obj)->tp_as_async; + if (likely(am && am->am_anext)) { + return (*am->am_anext)(obj); + } else { +#else + PyObject *value = __Pyx_PyObject_CallMethod0(obj, PYIDENT("__anext__")); + if (likely(value)) + return value; + // FIXME: for the sake of a nicely conforming exception message, assume any AttributeError meant '__anext__' + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { +#endif + + PyErr_Format(PyExc_TypeError, "'async for' requires an object with __anext__ method, got %.100s", + Py_TYPE(obj)->tp_name); + } + return NULL; +} + + //////////////////// pep479.proto //////////////////// static void __Pyx_Generator_Replace_StopIteration(void); /*proto*/ @@ -1531,3 +1581,102 @@ except AttributeError: #endif return module; } + + +//////////////////// StopAsyncIteration.proto //////////////////// + +#define __Pyx_StopAsyncIteration_USED +static PyObject *__Pyx_PyExc_StopAsyncIteration; +static int __pyx_StopAsyncIteration_init(void); /*proto*/ + +//////////////////// StopAsyncIteration //////////////////// + +#if PY_VERSION_HEX < 0x030500B1 +static PyTypeObject __Pyx__PyExc_StopAsyncIteration_type = { + PyVarObject_HEAD_INIT(0, 0) + "StopAsyncIteration", /*tp_name*/ + sizeof(PyBaseExceptionObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ +#if PY_MAJOR_VERSION < 3 + 0, /*tp_compare*/ +#else + 0, /*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_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + PyDoc_STR("Signal the end from iterator.__anext__()."), /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*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*/ + 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*/ +#if PY_VERSION_HEX >= 0x030400a1 + 0, /*tp_finalize*/ +#endif +}; +#endif + +static int __pyx_StopAsyncIteration_init(void) { +#if PY_VERSION_HEX >= 0x030500B1 + __Pyx_PyExc_StopAsyncIteration = PyExc_StopAsyncIteration; +#else + PyObject *builtins = PyEval_GetBuiltins(); + if (likely(builtins)) { + PyObject *exc = PyMapping_GetItemString(builtins, "StopAsyncIteration"); + if (exc) { + __Pyx_PyExc_StopAsyncIteration = exc; + return 0; + } + } + PyErr_Clear(); + + __Pyx__PyExc_StopAsyncIteration_type.tp_traverse = ((PyTypeObject*)PyExc_BaseException)->tp_traverse; + __Pyx__PyExc_StopAsyncIteration_type.tp_clear = ((PyTypeObject*)PyExc_BaseException)->tp_clear; + __Pyx__PyExc_StopAsyncIteration_type.tp_dealloc = ((PyTypeObject*)PyExc_BaseException)->tp_dealloc; + __Pyx__PyExc_StopAsyncIteration_type.tp_dictoffset = ((PyTypeObject*)PyExc_BaseException)->tp_dictoffset; + __Pyx__PyExc_StopAsyncIteration_type.tp_base = (PyTypeObject*)PyExc_Exception; + __Pyx__PyExc_StopAsyncIteration_type.tp_init = ((PyTypeObject*)PyExc_BaseException)->tp_init; + __Pyx__PyExc_StopAsyncIteration_type.tp_new = ((PyTypeObject*)PyExc_BaseException)->tp_new; + + __Pyx_PyExc_StopAsyncIteration = (PyObject*) __Pyx_FetchCommonType(&__Pyx__PyExc_StopAsyncIteration_type); + if (unlikely(!__Pyx_PyExc_StopAsyncIteration)) + return -1; + if (builtins && unlikely(PyMapping_SetItemString(builtins, "StopAsyncIteration", __Pyx_PyExc_StopAsyncIteration) < 0)) + return -1; +#endif + return 0; +} diff --git a/tests/run/test_coroutines_pep492.pyx b/tests/run/test_coroutines_pep492.pyx index 793831883..7d216835d 100644 --- a/tests/run/test_coroutines_pep492.pyx +++ b/tests/run/test_coroutines_pep492.pyx @@ -104,6 +104,7 @@ class TokenizerRegrTest(unittest.TestCase): class CoroutineTest(unittest.TestCase): + @classmethod def setUpClass(cls): # never mark warnings as "already seen" to prevent them from being suppressed from warnings import simplefilter @@ -209,8 +210,9 @@ class CoroutineTest(unittest.TestCase): with check(): iter(foo()) - with check(): - next(foo()) + # in Cython: not iterable, but an iterator ... + #with check(): + # next(foo()) with silence_coro_gc(), check(): for i in foo(): |
