Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
cython
Commits
ccf3f865
Commit
ccf3f865
authored
May 25, 2015
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement 'async for' loop statement (PEP 492)
parent
f8c0aafb
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
356 additions
and
67 deletions
+356
-67
Cython/Compiler/Builtin.py
Cython/Compiler/Builtin.py
+7
-0
Cython/Compiler/Code.py
Cython/Compiler/Code.py
+0
-1
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+119
-32
Cython/Compiler/FlowControl.py
Cython/Compiler/FlowControl.py
+6
-0
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+1
-1
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+52
-12
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.pxd
+3
-4
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+15
-15
Cython/Utility/Coroutine.c
Cython/Utility/Coroutine.c
+149
-0
tests/run/test_coroutines_pep492.pyx
tests/run/test_coroutines_pep492.pyx
+4
-2
No files found.
Cython/Compiler/Builtin.py
View file @
ccf3f865
...
...
@@ -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
...
...
Cython/Compiler/Code.py
View file @
ccf3f865
...
...
@@ -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
=
{
...
...
Cython/Compiler/ExprNodes.py
View file @
ccf3f865
...
...
@@ -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
)
el
s
e
:
el
if
decref_sourc
e
:
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
...
...
Cython/Compiler/FlowControl.py
View file @
ccf3f865
...
...
@@ -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
)
...
...
Cython/Compiler/ModuleNode.py
View file @
ccf3f865
...
...
@@ -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"
)
...
...
Cython/Compiler/Nodes.py
View file @
ccf3f865
...
...
@@ -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"
,
"ite
m"
,
"ite
rator"
,
"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
#
...
...
Cython/Compiler/Parsing.pxd
View file @
ccf3f865
...
...
@@ -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
)
...
...
Cython/Compiler/Parsing.py
View file @
ccf3f865
...
...
@@ -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'
...
...
Cython/Utility/Coroutine.c
View file @
ccf3f865
...
...
@@ -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
;
}
tests/run/test_coroutines_pep492.pyx
View file @
ccf3f865
...
...
@@ -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
():
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment