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
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
cython
Commits
a129d192
Commit
a129d192
authored
May 25, 2015
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '_pep492_async_await'
parents
19ffc8b6
9ee3d69a
Changes
27
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
3607 additions
and
144 deletions
+3607
-144
Cython/Compiler/Builtin.py
Cython/Compiler/Builtin.py
+7
-0
Cython/Compiler/Code.py
Cython/Compiler/Code.py
+9
-2
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+164
-38
Cython/Compiler/FlowControl.py
Cython/Compiler/FlowControl.py
+6
-0
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+4
-11
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+76
-26
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+43
-23
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.pxd
+7
-6
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+90
-30
Cython/Compiler/Pipeline.py
Cython/Compiler/Pipeline.py
+1
-1
Cython/Compiler/Scanning.pxd
Cython/Compiler/Scanning.pxd
+3
-0
Cython/Compiler/Scanning.py
Cython/Compiler/Scanning.py
+16
-0
Cython/Compiler/TypeSlots.py
Cython/Compiler/TypeSlots.py
+40
-3
Cython/Parser/Grammar
Cython/Parser/Grammar
+4
-2
Cython/Utility/Coroutine.c
Cython/Utility/Coroutine.c
+1690
-0
Cython/Utility/CythonFunction.c
Cython/Utility/CythonFunction.c
+2
-2
tests/errors/pep492_badsyntax_async1.pyx
tests/errors/pep492_badsyntax_async1.pyx
+10
-0
tests/errors/pep492_badsyntax_async2.pyx
tests/errors/pep492_badsyntax_async2.pyx
+11
-0
tests/errors/pep492_badsyntax_async3.pyx
tests/errors/pep492_badsyntax_async3.pyx
+9
-0
tests/errors/pep492_badsyntax_async4.pyx
tests/errors/pep492_badsyntax_async4.pyx
+9
-0
tests/errors/pep492_badsyntax_async5.pyx
tests/errors/pep492_badsyntax_async5.pyx
+9
-0
tests/errors/pep492_badsyntax_async6.pyx
tests/errors/pep492_badsyntax_async6.pyx
+10
-0
tests/errors/pep492_badsyntax_async7.pyx
tests/errors/pep492_badsyntax_async7.pyx
+10
-0
tests/errors/pep492_badsyntax_async8.pyx
tests/errors/pep492_badsyntax_async8.pyx
+9
-0
tests/errors/pep492_badsyntax_async9.pyx
tests/errors/pep492_badsyntax_async9.pyx
+9
-0
tests/run/async_iter_pep492.pyx
tests/run/async_iter_pep492.pyx
+298
-0
tests/run/test_coroutines_pep492.pyx
tests/run/test_coroutines_pep492.pyx
+1061
-0
No files found.
Cython/Compiler/Builtin.py
View file @
a129d192
...
...
@@ -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 @
a129d192
...
...
@@ -49,7 +49,7 @@ non_portable_builtins_map = {
'basestring'
:
(
'PY_MAJOR_VERSION >= 3'
,
'str'
),
'xrange'
:
(
'PY_MAJOR_VERSION >= 3'
,
'range'
),
'raw_input'
:
(
'PY_MAJOR_VERSION >= 3'
,
'input'
),
}
}
basicsize_builtins_map
=
{
# builtins whose type has a different tp_basicsize than sizeof(...)
...
...
@@ -63,6 +63,13 @@ uncachable_builtins = [
'_'
,
# e.g. gettext
]
special_py_methods
=
set
([
'__cinit__'
,
'__dealloc__'
,
'__richcmp__'
,
'__next__'
,
'__await__'
,
'__aiter__'
,
'__anext__'
,
'__getreadbuffer__'
,
'__getwritebuffer__'
,
'__getsegcount__'
,
'__getcharbuffer__'
,
'__getbuffer__'
,
'__releasebuffer__'
])
modifier_output_mapper
=
{
'inline'
:
'CYTHON_INLINE'
}.
get
...
...
@@ -1999,7 +2006,7 @@ class CCodeWriter(object):
def
put_pymethoddef
(
self
,
entry
,
term
,
allow_skip
=
True
):
if
entry
.
is_special
or
entry
.
name
==
'__getattribute__'
:
if
entry
.
name
not
in
[
'__cinit__'
,
'__dealloc__'
,
'__richcmp__'
,
'__next__'
,
'__getreadbuffer__'
,
'__getwritebuffer__'
,
'__getsegcount__'
,
'__getcharbuffer__'
,
'__getbuffer__'
,
'__releasebuffer__'
]
:
if
entry
.
name
not
in
special_py_methods
:
if
entry
.
name
==
'__getattr__'
and
not
self
.
globalstate
.
directives
[
'fast_getattr'
]:
pass
# Python's typeobject.c will automatically fill in our slot
...
...
Cython/Compiler/ExprNodes.py
View file @
a129d192
This diff is collapsed.
Click to expand it.
Cython/Compiler/FlowControl.py
View file @
a129d192
...
...
@@ -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 @
a129d192
...
...
@@ -2071,17 +2071,10 @@ 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
)))
code
.
putln
(
"#ifdef __Pyx_CyFunction_USED"
)
code
.
put_error_if_neg
(
self
.
pos
,
"__Pyx_CyFunction_init()"
)
code
.
putln
(
"#endif"
)
code
.
putln
(
"#ifdef __Pyx_FusedFunction_USED"
)
code
.
put_error_if_neg
(
self
.
pos
,
"__pyx_FusedFunction_init()"
)
code
.
putln
(
"#endif"
)
code
.
putln
(
"#ifdef __Pyx_Generator_USED"
)
code
.
put_error_if_neg
(
self
.
pos
,
"__pyx_Generator_init()"
)
code
.
putln
(
"#endif"
)
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"
)
code
.
putln
(
"/*--- Library function declarations ---*/"
)
env
.
generate_library_function_declarations
(
code
)
...
...
Cython/Compiler/Nodes.py
View file @
a129d192
...
...
@@ -1574,9 +1574,11 @@ class FuncDefNode(StatNode, BlockNode):
# pymethdef_required boolean Force Python method struct generation
# directive_locals { string : ExprNode } locals defined by cython.locals(...)
# directive_returns [ExprNode] type defined by cython.returns(...)
# star_arg PyArgDeclNode or None * argument
# starstar_arg PyArgDeclNode or None ** argument
# star_arg PyArgDeclNode or None * argument
# starstar_arg PyArgDeclNode or None ** argument
#
# is_async_def boolean is a Coroutine function
#
# has_fused_arguments boolean
# Whether this cdef function has fused parameters. This is needed
# by AnalyseDeclarationsTransform, so it can replace CFuncDefNodes
...
...
@@ -1588,6 +1590,7 @@ class FuncDefNode(StatNode, BlockNode):
pymethdef_required
=
False
is_generator
=
False
is_generator_body
=
False
is_async_def
=
False
modifiers
=
[]
has_fused_arguments
=
False
star_arg
=
None
...
...
@@ -3936,6 +3939,7 @@ class GeneratorDefNode(DefNode):
#
is_generator
=
True
is_coroutine
=
False
needs_closure
=
True
child_attrs
=
DefNode
.
child_attrs
+
[
"gbody"
]
...
...
@@ -3956,8 +3960,9 @@ class GeneratorDefNode(DefNode):
qualname
=
code
.
intern_identifier
(
self
.
qualname
)
code
.
putln
(
'{'
)
code
.
putln
(
'__pyx_GeneratorObject *gen = __Pyx_Generator_New('
'(__pyx_generator_body_t) %s, (PyObject *) %s, %s, %s); %s'
%
(
code
.
putln
(
'__pyx_CoroutineObject *gen = __Pyx_%s_New('
'(__pyx_coroutine_body_t) %s, (PyObject *) %s, %s, %s); %s'
%
(
'Coroutine'
if
self
.
is_coroutine
else
'Generator'
,
body_cname
,
Naming
.
cur_scope_cname
,
name
,
qualname
,
code
.
error_goto_if_null
(
'gen'
,
self
.
pos
)))
code
.
put_decref
(
Naming
.
cur_scope_cname
,
py_object_type
)
...
...
@@ -3972,13 +3977,18 @@ class GeneratorDefNode(DefNode):
code
.
putln
(
'}'
)
def
generate_function_definitions
(
self
,
env
,
code
):
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
"Generator"
,
"Generator.c"
))
env
.
use_utility_code
(
UtilityCode
.
load_cached
(
'Coroutine'
if
self
.
is_coroutine
else
'Generator'
,
"Coroutine.c"
))
self
.
gbody
.
generate_function_header
(
code
,
proto
=
True
)
super
(
GeneratorDefNode
,
self
).
generate_function_definitions
(
env
,
code
)
self
.
gbody
.
generate_function_definitions
(
env
,
code
)
class
AsyncDefNode
(
GeneratorDefNode
):
is_coroutine
=
True
class
GeneratorBodyDefNode
(
DefNode
):
# Main code body of a generator implemented as a DefNode.
#
...
...
@@ -4005,7 +4015,7 @@ class GeneratorBodyDefNode(DefNode):
self
.
declare_generator_body
(
env
)
def
generate_function_header
(
self
,
code
,
proto
=
False
):
header
=
"static PyObject *%s(__pyx_
Generator
Object *%s, PyObject *%s)"
%
(
header
=
"static PyObject *%s(__pyx_
Coroutine
Object *%s, PyObject *%s)"
%
(
self
.
entry
.
func_cname
,
Naming
.
generator_cname
,
Naming
.
sent_value_cname
)
...
...
@@ -4070,7 +4080,7 @@ class GeneratorBodyDefNode(DefNode):
code
.
put_label
(
code
.
error_label
)
if
Future
.
generator_stop
in
env
.
global_scope
().
context
.
future_directives
:
# PEP 479: turn accidental StopIteration exceptions into a RuntimeError
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
"pep479"
,
"
Generator
.c"
))
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
"pep479"
,
"
Coroutine
.c"
))
code
.
putln
(
"if (unlikely(PyErr_ExceptionMatches(PyExc_StopIteration))) "
"__Pyx_Generator_Replace_StopIteration();"
)
for
cname
,
type
in
code
.
funcstate
.
all_managed_temps
():
...
...
@@ -4082,7 +4092,7 @@ class GeneratorBodyDefNode(DefNode):
code
.
put_xdecref
(
Naming
.
retval_cname
,
py_object_type
)
code
.
putln
(
'%s->resume_label = -1;'
%
Naming
.
generator_cname
)
# clean up as early as possible to help breaking any reference cycles
code
.
putln
(
'__Pyx_
Generator
_clear((PyObject*)%s);'
%
Naming
.
generator_cname
)
code
.
putln
(
'__Pyx_
Coroutine
_clear((PyObject*)%s);'
%
Naming
.
generator_cname
)
code
.
put_finish_refcount_context
()
code
.
putln
(
'return NULL;'
)
code
.
putln
(
"}"
)
...
...
@@ -5512,7 +5522,7 @@ class ReturnStatNode(StatNode):
elif
self
.
in_generator
:
# return value == raise StopIteration(value), but uncatchable
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
"ReturnWithStopIteration"
,
"
Generator
.c"
))
UtilityCode
.
load_cached
(
"ReturnWithStopIteration"
,
"
Coroutine
.c"
))
code
.
putln
(
"%s = NULL; __Pyx_ReturnWithStopIteration(%s);"
%
(
Naming
.
retval_cname
,
self
.
value
.
py_result
()))
...
...
@@ -6059,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
:
...
...
@@ -6158,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
#
...
...
@@ -6444,7 +6494,7 @@ class WithStatNode(StatNode):
code
.
putln
(
"%s = __Pyx_PyObject_LookupSpecial(%s, %s); %s"
%
(
self
.
exit_var
,
self
.
manager
.
py_result
(),
code
.
intern_identifier
(
EncodedString
(
'__exit__'
)),
code
.
intern_identifier
(
EncodedString
(
'__
aexit__'
if
self
.
is_async
else
'__
exit__'
)),
code
.
error_goto_if_null
(
self
.
exit_var
,
self
.
pos
),
))
code
.
put_gotref
(
self
.
exit_var
)
...
...
@@ -7108,7 +7158,7 @@ class GILStatNode(NogilTryFinallyStatNode):
from
.ParseTreeTransforms
import
YieldNodeCollector
collector
=
YieldNodeCollector
()
collector
.
visitchildren
(
body
)
if
not
collector
.
yields
:
if
not
collector
.
yields
and
not
collector
.
awaits
:
return
if
state
==
'gil'
:
...
...
@@ -7205,8 +7255,8 @@ utility_code_for_cimports = {
utility_code_for_imports
=
{
# utility code used when special modules are imported.
# TODO: Consider a generic user-level mechanism for importing
'asyncio'
:
(
"__Pyx_patch_asyncio"
,
"PatchAsyncIO"
,
"
Generator
.c"
),
'inspect'
:
(
"__Pyx_patch_inspect"
,
"PatchInspect"
,
"
Generator
.c"
),
'asyncio'
:
(
"__Pyx_patch_asyncio"
,
"PatchAsyncIO"
,
"
Coroutine
.c"
),
'inspect'
:
(
"__Pyx_patch_inspect"
,
"PatchInspect"
,
"
Coroutine
.c"
),
}
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
a129d192
...
...
@@ -200,7 +200,7 @@ class PostParse(ScopeTrackingTransform):
node
.
lambda_name
=
EncodedString
(
u'lambda%d'
%
lambda_id
)
collector
=
YieldNodeCollector
()
collector
.
visitchildren
(
node
.
result_expr
)
if
collector
.
yields
or
isinstance
(
node
.
result_expr
,
ExprNodes
.
YieldExprNode
):
if
collector
.
yields
or
collector
.
awaits
or
isinstance
(
node
.
result_expr
,
ExprNodes
.
YieldExprNode
):
body
=
Nodes
.
ExprStatNode
(
node
.
result_expr
.
pos
,
expr
=
node
.
result_expr
)
else
:
...
...
@@ -1219,15 +1219,19 @@ class WithTransform(CythonTransform, SkipDeclarations):
def
visit_WithStatNode
(
self
,
node
):
self
.
visitchildren
(
node
,
'body'
)
pos
=
node
.
pos
is_async
=
node
.
is_async
body
,
target
,
manager
=
node
.
body
,
node
.
target
,
node
.
manager
node
.
enter_call
=
ExprNodes
.
SimpleCallNode
(
pos
,
function
=
ExprNodes
.
AttributeNode
(
pos
,
obj
=
ExprNodes
.
CloneNode
(
manager
),
attribute
=
EncodedString
(
'__enter__'
),
attribute
=
EncodedString
(
'__
aenter__'
if
is_async
else
'__
enter__'
),
is_special_lookup
=
True
),
args
=
[],
is_temp
=
True
)
if
is_async
:
node
.
enter_call
=
ExprNodes
.
AwaitExprNode
(
pos
,
arg
=
node
.
enter_call
)
if
target
is
not
None
:
body
=
Nodes
.
StatListNode
(
pos
,
stats
=
[
...
...
@@ -1245,7 +1249,8 @@ class WithTransform(CythonTransform, SkipDeclarations):
pos
,
operand
=
ExprNodes
.
WithExitCallNode
(
pos
,
with_stat
=
node
,
test_if_run
=
False
,
args
=
excinfo_target
)),
args
=
excinfo_target
,
await
=
ExprNodes
.
AwaitExprNode
(
pos
,
arg
=
None
)
if
is_async
else
None
)),
body
=
Nodes
.
ReraiseStatNode
(
pos
),
),
],
...
...
@@ -1266,8 +1271,8 @@ class WithTransform(CythonTransform, SkipDeclarations):
pos
,
with_stat
=
node
,
test_if_run
=
True
,
args
=
ExprNodes
.
TupleNode
(
pos
,
args
=
[
ExprNodes
.
NoneNode
(
pos
)
for
_
in
range
(
3
)]
)
)),
pos
,
args
=
[
ExprNodes
.
NoneNode
(
pos
)
for
_
in
range
(
3
)]
),
await
=
ExprNodes
.
AwaitExprNode
(
pos
,
arg
=
None
)
if
is_async
else
None
)),
handle_error_case
=
False
,
)
return
node
...
...
@@ -2205,6 +2210,7 @@ class YieldNodeCollector(TreeVisitor):
def
__init__
(
self
):
super
(
YieldNodeCollector
,
self
).
__init__
()
self
.
yields
=
[]
self
.
awaits
=
[]
self
.
returns
=
[]
self
.
has_return_value
=
False
...
...
@@ -2215,6 +2221,10 @@ class YieldNodeCollector(TreeVisitor):
self
.
yields
.
append
(
node
)
self
.
visitchildren
(
node
)
def
visit_AwaitExprNode
(
self
,
node
):
self
.
awaits
.
append
(
node
)
self
.
visitchildren
(
node
)
def
visit_ReturnStatNode
(
self
,
node
):
self
.
visitchildren
(
node
)
if
node
.
value
:
...
...
@@ -2250,27 +2260,36 @@ class MarkClosureVisitor(CythonTransform):
collector
=
YieldNodeCollector
()
collector
.
visitchildren
(
node
)
if
collector
.
yields
:
if
isinstance
(
node
,
Nodes
.
CFuncDefNode
):
# Will report error later
return
node
for
i
,
yield_expr
in
enumerate
(
collector
.
yields
,
1
):
yield_expr
.
label_num
=
i
for
retnode
in
collector
.
returns
:
retnode
.
in_generator
=
True
if
node
.
is_async_def
:
if
collector
.
yields
:
error
(
collector
.
yields
[
0
].
pos
,
"'yield' not allowed in async coroutines (use 'await')"
)
yields
=
collector
.
awaits
elif
collector
.
yields
:
if
collector
.
awaits
:
error
(
collector
.
yields
[
0
].
pos
,
"'await' not allowed in generators (use 'yield')"
)
yields
=
collector
.
yields
else
:
return
node
gbody
=
Nodes
.
GeneratorBodyDefNode
(
pos
=
node
.
pos
,
name
=
node
.
name
,
body
=
node
.
body
)
generator
=
Nodes
.
GeneratorDefNode
(
pos
=
node
.
pos
,
name
=
node
.
name
,
args
=
node
.
args
,
star_arg
=
node
.
star_arg
,
starstar_arg
=
node
.
starstar_arg
,
doc
=
node
.
doc
,
decorators
=
node
.
decorators
,
gbody
=
gbody
,
lambda_name
=
node
.
lambda_name
)
return
generator
return
node
for
i
,
yield_expr
in
enumerate
(
yields
,
1
):
yield_expr
.
label_num
=
i
for
retnode
in
collector
.
returns
:
retnode
.
in_generator
=
True
gbody
=
Nodes
.
GeneratorBodyDefNode
(
pos
=
node
.
pos
,
name
=
node
.
name
,
body
=
node
.
body
)
coroutine
=
(
Nodes
.
AsyncDefNode
if
node
.
is_async_def
else
Nodes
.
GeneratorDefNode
)(
pos
=
node
.
pos
,
name
=
node
.
name
,
args
=
node
.
args
,
star_arg
=
node
.
star_arg
,
starstar_arg
=
node
.
starstar_arg
,
doc
=
node
.
doc
,
decorators
=
node
.
decorators
,
gbody
=
gbody
,
lambda_name
=
node
.
lambda_name
)
return
coroutine
def
visit_CFuncDefNode
(
self
,
node
):
self
.
visit_FuncDefNode
(
node
)
self
.
needs_closure
=
False
self
.
visitchildren
(
node
)
node
.
needs_closure
=
self
.
needs_closure
self
.
needs_closure
=
True
if
node
.
needs_closure
and
node
.
overridable
:
error
(
node
.
pos
,
"closures inside cpdef functions not yet supported"
)
return
node
...
...
@@ -2287,6 +2306,7 @@ class MarkClosureVisitor(CythonTransform):
self
.
needs_closure
=
True
return
node
class
CreateClosureClasses
(
CythonTransform
):
# Output closure classes in module scope for all functions
# that really need it.
...
...
Cython/Compiler/Parsing.pxd
View file @
a129d192
...
...
@@ -44,6 +44,7 @@ cdef p_typecast(PyrexScanner s)
cdef
p_sizeof
(
PyrexScanner
s
)
cdef
p_yield_expression
(
PyrexScanner
s
)
cdef
p_yield_statement
(
PyrexScanner
s
)
cdef
p_async_statement
(
PyrexScanner
s
,
ctx
)
cdef
p_power
(
PyrexScanner
s
)
cdef
p_new_expr
(
PyrexScanner
s
)
cdef
p_trailer
(
PyrexScanner
s
,
node1
)
...
...
@@ -107,18 +108,18 @@ 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
)
cdef
p_with_statement
(
PyrexScanner
s
)
cdef
p_with_items
(
PyrexScanner
s
)
cdef
p_with_items
(
PyrexScanner
s
,
bint
is_async
=*
)
cdef
p_with_template
(
PyrexScanner
s
)
cdef
p_simple_statement
(
PyrexScanner
s
,
bint
first_statement
=
*
)
cdef
p_simple_statement_list
(
PyrexScanner
s
,
ctx
,
bint
first_statement
=
*
)
...
...
@@ -128,7 +129,7 @@ cdef p_IF_statement(PyrexScanner s, ctx)
cdef
p_statement
(
PyrexScanner
s
,
ctx
,
bint
first_statement
=
*
)
cdef
p_statement_list
(
PyrexScanner
s
,
ctx
,
bint
first_statement
=
*
)
cdef
p_suite
(
PyrexScanner
s
,
ctx
=
*
)
cdef
tuple
p_suite_with_docstring
(
PyrexScanner
s
,
ctx
,
with_doc_only
=
*
)
cdef
tuple
p_suite_with_docstring
(
PyrexScanner
s
,
ctx
,
bint
with_doc_only
=
*
)
cdef
tuple
_extract_docstring
(
node
)
cdef
p_positional_and_keyword_args
(
PyrexScanner
s
,
end_sy_set
,
templates
=
*
)
...
...
@@ -176,7 +177,7 @@ cdef p_c_modifiers(PyrexScanner s)
cdef
p_c_func_or_var_declaration
(
PyrexScanner
s
,
pos
,
ctx
)
cdef
p_ctypedef_statement
(
PyrexScanner
s
,
ctx
)
cdef
p_decorators
(
PyrexScanner
s
)
cdef
p_def_statement
(
PyrexScanner
s
,
list
decorators
=
*
)
cdef
p_def_statement
(
PyrexScanner
s
,
list
decorators
=*
,
bint
is_async_def
=
*
)
cdef
p_varargslist
(
PyrexScanner
s
,
terminator
=*
,
bint
annotated
=
*
)
cdef
p_py_arg_decl
(
PyrexScanner
s
,
bint
annotated
=
*
)
cdef
p_class_statement
(
PyrexScanner
s
,
decorators
)
...
...
Cython/Compiler/Parsing.py
View file @
a129d192
...
...
@@ -55,6 +55,7 @@ class Ctx(object):
d
.
update
(
kwds
)
return
ctx
def
p_ident
(
s
,
message
=
"Expected an identifier"
):
if
s
.
sy
==
'IDENT'
:
name
=
s
.
systring
...
...
@@ -350,6 +351,7 @@ def p_sizeof(s):
s
.
expect
(
')'
)
return
node
def
p_yield_expression
(
s
):
# s.sy == "yield"
pos
=
s
.
position
()
...
...
@@ -370,19 +372,47 @@ def p_yield_expression(s):
else
:
return
ExprNodes
.
YieldExprNode
(
pos
,
arg
=
arg
)
def
p_yield_statement
(
s
):
# s.sy == "yield"
yield_expr
=
p_yield_expression
(
s
)
return
Nodes
.
ExprStatNode
(
yield_expr
.
pos
,
expr
=
yield_expr
)
#power: atom trailer* ('**' factor)*
def
p_async_statement
(
s
,
ctx
,
decorators
):
# s.sy >> 'async' ...
if
s
.
sy
==
'def'
:
# 'async def' statements aren't allowed in pxd files
if
'pxd'
in
ctx
.
level
:
s
.
error
(
'def statement not allowed here'
)
s
.
level
=
ctx
.
level
return
p_def_statement
(
s
,
decorators
,
is_async_def
=
True
)
elif
decorators
:
s
.
error
(
"Decorators can only be followed by functions or classes"
)
elif
s
.
sy
==
'for'
:
return
p_for_statement
(
s
,
is_async
=
True
)
elif
s
.
sy
==
'with'
:
s
.
next
()
return
p_with_items
(
s
,
is_async
=
True
)
else
:
s
.
error
(
"expected one of 'def', 'for', 'with' after 'async'"
)
#power: atom_expr ('**' factor)*
#atom_expr: ['await'] atom trailer*
def
p_power
(
s
):
if
s
.
systring
==
'new'
and
s
.
peek
()[
0
]
==
'IDENT'
:
return
p_new_expr
(
s
)
await_pos
=
None
if
s
.
sy
==
'await'
:
await_pos
=
s
.
position
()
s
.
next
()
n1
=
p_atom
(
s
)
while
s
.
sy
in
(
'('
,
'['
,
'.'
):
n1
=
p_trailer
(
s
,
n1
)
if
await_pos
:
n1
=
ExprNodes
.
AwaitExprNode
(
await_pos
,
arg
=
n1
)
if
s
.
sy
==
'**'
:
pos
=
s
.
position
()
s
.
next
()
...
...
@@ -390,6 +420,7 @@ def p_power(s):
n1
=
ExprNodes
.
binop_node
(
pos
,
'**'
,
n1
,
n2
)
return
n1
def
p_new_expr
(
s
):
# s.systring == 'new'.
pos
=
s
.
position
()
...
...
@@ -1568,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
)
...
...
@@ -1654,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'
...
...
@@ -1745,17 +1781,21 @@ def p_include_statement(s, ctx):
else
:
return
Nodes
.
PassStatNode
(
pos
)
def
p_with_statement
(
s
):
s
.
next
()
# 'with'
s
.
next
()
# 'with'
if
s
.
systring
==
'template'
and
not
s
.
in_python_file
:
node
=
p_with_template
(
s
)
else
:
node
=
p_with_items
(
s
)
return
node
def
p_with_items
(
s
):
def
p_with_items
(
s
,
is_async
=
False
):
pos
=
s
.
position
()
if
not
s
.
in_python_file
and
s
.
sy
==
'IDENT'
and
s
.
systring
in
(
'nogil'
,
'gil'
):
if
is_async
:
s
.
error
(
"with gil/nogil cannot be async"
)
state
=
s
.
systring
s
.
next
()
if
s
.
sy
==
','
:
...
...
@@ -1763,7 +1803,7 @@ def p_with_items(s):
body
=
p_with_items
(
s
)
else
:
body
=
p_suite
(
s
)
return
Nodes
.
GILStatNode
(
pos
,
state
=
state
,
body
=
body
)
return
Nodes
.
GILStatNode
(
pos
,
state
=
state
,
body
=
body
)
else
:
manager
=
p_test
(
s
)
target
=
None
...
...
@@ -1772,11 +1812,11 @@ def p_with_items(s):
target
=
p_starred_expr
(
s
)
if
s
.
sy
==
','
:
s
.
next
()
body
=
p_with_items
(
s
)
body
=
p_with_items
(
s
,
is_async
=
is_async
)
else
:
body
=
p_suite
(
s
)
return
Nodes
.
WithStatNode
(
pos
,
manager
=
manager
,
target
=
target
,
body
=
body
)
return
Nodes
.
WithStatNode
(
pos
,
manager
=
manager
,
target
=
target
,
body
=
body
,
is_async
=
is_async
)
def
p_with_template
(
s
):
pos
=
s
.
position
()
...
...
@@ -1929,12 +1969,14 @@ def p_statement(s, ctx, first_statement = 0):
s
.
error
(
'decorator not allowed here'
)
s
.
level
=
ctx
.
level
decorators
=
p_decorators
(
s
)
bad_toks
=
'def'
,
'cdef'
,
'cpdef'
,
'class'
if
not
ctx
.
allow_struct_enum_decorator
and
s
.
sy
not
in
bad_toks
:
s
.
error
(
"Decorators can only be followed by functions or classes"
)
if
not
ctx
.
allow_struct_enum_decorator
and
s
.
sy
not
in
(
'def'
,
'cdef'
,
'cpdef'
,
'class'
):
if
s
.
sy
==
'IDENT'
and
s
.
systring
==
'async'
:
pass
# handled below
else
:
s
.
error
(
"Decorators can only be followed by functions or classes"
)
elif
s
.
sy
==
'pass'
and
cdef_flag
:
# empty cdef block
return
p_pass_statement
(
s
,
with_newline
=
1
)
return
p_pass_statement
(
s
,
with_newline
=
1
)
overridable
=
0
if
s
.
sy
==
'cdef'
:
...
...
@@ -1948,11 +1990,11 @@ def p_statement(s, ctx, first_statement = 0):
if
ctx
.
level
not
in
(
'module'
,
'module_pxd'
,
'function'
,
'c_class'
,
'c_class_pxd'
):
s
.
error
(
'cdef statement not allowed here'
)
s
.
level
=
ctx
.
level
node
=
p_cdef_statement
(
s
,
ctx
(
overridable
=
overridable
))
node
=
p_cdef_statement
(
s
,
ctx
(
overridable
=
overridable
))
if
decorators
is
not
None
:
tup
=
Nodes
.
CFuncDefNode
,
Nodes
.
CVarDefNode
,
Nodes
.
CClassDefNode
tup
=
(
Nodes
.
CFuncDefNode
,
Nodes
.
CVarDefNode
,
Nodes
.
CClassDefNode
)
if
ctx
.
allow_struct_enum_decorator
:
tup
+=
Nodes
.
CStructOrUnionDefNode
,
Nodes
.
CEnumDefNode
tup
+=
(
Nodes
.
CStructOrUnionDefNode
,
Nodes
.
CEnumDefNode
)
if
not
isinstance
(
node
,
tup
):
s
.
error
(
"Decorators can only be followed by functions or classes"
)
node
.
decorators
=
decorators
...
...
@@ -1995,9 +2037,25 @@ def p_statement(s, ctx, first_statement = 0):
return
p_try_statement
(
s
)
elif
s
.
sy
==
'with'
:
return
p_with_statement
(
s
)
elif
s
.
sy
==
'async'
:
s
.
next
()
return
p_async_statement
(
s
,
ctx
,
decorators
)
else
:
return
p_simple_statement_list
(
s
,
ctx
,
first_statement
=
first_statement
)
if
s
.
sy
==
'IDENT'
and
s
.
systring
==
'async'
:
# PEP 492 enables the async/await keywords when it spots "async def ..."
s
.
next
()
if
s
.
sy
==
'def'
:
s
.
enable_keyword
(
'async'
)
s
.
enable_keyword
(
'await'
)
result
=
p_async_statement
(
s
,
ctx
,
decorators
)
s
.
enable_keyword
(
'await'
)
s
.
disable_keyword
(
'async'
)
return
result
elif
decorators
:
s
.
error
(
"Decorators can only be followed by functions or classes"
)
s
.
put_back
(
'IDENT'
,
'async'
)
return
p_simple_statement_list
(
s
,
ctx
,
first_statement
=
first_statement
)
def
p_statement_list
(
s
,
ctx
,
first_statement
=
0
):
# Parse a series of statements separated by newlines.
...
...
@@ -3002,7 +3060,8 @@ def p_decorators(s):
s
.
expect_newline
(
"Expected a newline after decorator"
)
return
decorators
def
p_def_statement
(
s
,
decorators
=
None
):
def
p_def_statement
(
s
,
decorators
=
None
,
is_async_def
=
False
):
# s.sy == 'def'
pos
=
s
.
position
()
s
.
next
()
...
...
@@ -3017,10 +3076,11 @@ def p_def_statement(s, decorators=None):
s
.
next
()
return_type_annotation
=
p_test
(
s
)
doc
,
body
=
p_suite_with_docstring
(
s
,
Ctx
(
level
=
'function'
))
return
Nodes
.
DefNode
(
pos
,
name
=
name
,
args
=
args
,
star_arg
=
star_arg
,
starstar_arg
=
starstar_arg
,
doc
=
doc
,
body
=
body
,
decorators
=
decorators
,
return_type_annotation
=
return_type_annotation
)
return
Nodes
.
DefNode
(
pos
,
name
=
name
,
args
=
args
,
star_arg
=
star_arg
,
starstar_arg
=
starstar_arg
,
doc
=
doc
,
body
=
body
,
decorators
=
decorators
,
is_async_def
=
is_async_def
,
return_type_annotation
=
return_type_annotation
)
def
p_varargslist
(
s
,
terminator
=
')'
,
annotated
=
1
):
args
=
p_c_arg_list
(
s
,
in_pyfunc
=
1
,
nonempty_declarators
=
1
,
...
...
Cython/Compiler/Pipeline.py
View file @
a129d192
...
...
@@ -172,12 +172,12 @@ def create_pipeline(context, mode, exclude_classes=()):
InterpretCompilerDirectives
(
context
,
context
.
compiler_directives
),
ParallelRangeTransform
(
context
),
AdjustDefByDirectives
(
context
),
WithTransform
(
context
),
MarkClosureVisitor
(
context
),
_align_function_definitions
,
RemoveUnreachableCode
(
context
),
ConstantFolding
(),
FlattenInListTransform
(),
WithTransform
(
context
),
DecoratorTransform
(
context
),
ForwardDeclareTypes
(
context
),
AnalyseDeclarationsTransform
(
context
),
...
...
Cython/Compiler/Scanning.pxd
View file @
a129d192
...
...
@@ -30,6 +30,7 @@ cdef class PyrexScanner(Scanner):
cdef
public
bint
in_python_file
cdef
public
source_encoding
cdef
set
keywords
cdef
public
dict
keywords_stack
cdef
public
list
indentation_stack
cdef
public
indentation_char
cdef
public
int
bracket_nesting_level
...
...
@@ -57,3 +58,5 @@ cdef class PyrexScanner(Scanner):
cdef
expect_indent
(
self
)
cdef
expect_dedent
(
self
)
cdef
expect_newline
(
self
,
message
=*
,
bint
ignore_semicolon
=*
)
cdef
enable_keyword
(
self
,
name
)
cdef
disable_keyword
(
self
,
name
)
Cython/Compiler/Scanning.py
View file @
a129d192
...
...
@@ -319,6 +319,7 @@ class PyrexScanner(Scanner):
self
.
in_python_file
=
False
self
.
keywords
=
set
(
pyx_reserved_words
)
self
.
trace
=
trace_scanner
self
.
keywords_stack
=
{}
self
.
indentation_stack
=
[
0
]
self
.
indentation_char
=
None
self
.
bracket_nesting_level
=
0
...
...
@@ -497,3 +498,18 @@ class PyrexScanner(Scanner):
self
.
expect
(
'NEWLINE'
,
message
)
if
useless_trailing_semicolon
is
not
None
:
warning
(
useless_trailing_semicolon
,
"useless trailing semicolon"
)
def
enable_keyword
(
self
,
name
):
if
name
in
self
.
keywords_stack
:
self
.
keywords_stack
[
name
]
+=
1
else
:
self
.
keywords_stack
[
name
]
=
1
self
.
keywords
.
add
(
name
)
def
disable_keyword
(
self
,
name
):
count
=
self
.
keywords_stack
.
get
(
name
,
1
)
if
count
==
1
:
self
.
keywords
.
discard
(
name
)
del
self
.
keywords_stack
[
name
]
else
:
self
.
keywords_stack
[
name
]
=
count
-
1
Cython/Compiler/TypeSlots.py
View file @
a129d192
...
...
@@ -431,8 +431,8 @@ class SuiteSlot(SlotDescriptor):
#
# sub_slots [SlotDescriptor]
def
__init__
(
self
,
sub_slots
,
slot_type
,
slot_name
):
SlotDescriptor
.
__init__
(
self
,
slot_name
)
def
__init__
(
self
,
sub_slots
,
slot_type
,
slot_name
,
ifdef
=
None
):
SlotDescriptor
.
__init__
(
self
,
slot_name
,
ifdef
=
ifdef
)
self
.
sub_slots
=
sub_slots
self
.
slot_type
=
slot_type
substructures
.
append
(
self
)
...
...
@@ -454,6 +454,8 @@ class SuiteSlot(SlotDescriptor):
def
generate_substructure
(
self
,
scope
,
code
):
if
not
self
.
is_empty
(
scope
):
code
.
putln
(
""
)
if
self
.
ifdef
:
code
.
putln
(
"#if %s"
%
self
.
ifdef
)
code
.
putln
(
"static %s %s = {"
%
(
self
.
slot_type
,
...
...
@@ -461,6 +463,8 @@ class SuiteSlot(SlotDescriptor):
for
slot
in
self
.
sub_slots
:
slot
.
generate
(
scope
,
code
)
code
.
putln
(
"};"
)
if
self
.
ifdef
:
code
.
putln
(
"#endif"
)
substructures
=
[]
# List of all SuiteSlot instances
...
...
@@ -506,6 +510,29 @@ class BaseClassSlot(SlotDescriptor):
base_type
.
typeptr_cname
))
class
AlternativeSlot
(
SlotDescriptor
):
"""Slot descriptor that delegates to different slots using C macros."""
def
__init__
(
self
,
alternatives
):
SlotDescriptor
.
__init__
(
self
,
""
)
self
.
alternatives
=
alternatives
def
generate
(
self
,
scope
,
code
):
# state machine: "#if ... (#elif ...)* #else ... #endif"
test
=
'if'
for
guard
,
slot
in
self
.
alternatives
:
if
guard
:
assert
test
in
(
'if'
,
'elif'
),
test
else
:
assert
test
==
'elif'
,
test
test
=
'else'
code
.
putln
(
"#%s %s"
%
(
test
,
guard
))
slot
.
generate
(
scope
,
code
)
if
test
==
'if'
:
test
=
'elif'
assert
test
==
'else'
,
test
code
.
putln
(
"#endif"
)
# The following dictionary maps __xxx__ method names to slot descriptors.
method_name_to_slot
=
{}
...
...
@@ -748,6 +775,12 @@ PyBufferProcs = (
MethodSlot
(
releasebufferproc
,
"bf_releasebuffer"
,
"__releasebuffer__"
)
)
PyAsyncMethods
=
(
MethodSlot
(
unaryfunc
,
"am_await"
,
"__await__"
),
MethodSlot
(
unaryfunc
,
"am_aiter"
,
"__aiter__"
),
MethodSlot
(
unaryfunc
,
"am_anext"
,
"__anext__"
),
)
#------------------------------------------------------------------------------------------
#
# The main slot table. This table contains descriptors for all the
...
...
@@ -761,7 +794,11 @@ slot_table = (
EmptySlot
(
"tp_print"
),
#MethodSlot(printfunc, "tp_print", "__print__"),
EmptySlot
(
"tp_getattr"
),
EmptySlot
(
"tp_setattr"
),
MethodSlot
(
cmpfunc
,
"tp_compare"
,
"__cmp__"
,
py3
=
'<RESERVED>'
),
AlternativeSlot
([
(
"PY_MAJOR_VERSION < 3"
,
MethodSlot
(
cmpfunc
,
"tp_compare"
,
"__cmp__"
)),
(
"PY_VERSION_HEX < 0x030500B1"
,
EmptySlot
(
"tp_reserved"
)),
(
""
,
SuiteSlot
(
PyAsyncMethods
,
"PyAsyncMethods"
,
"tp_as_async"
,
ifdef
=
"PY_VERSION_HEX >= 0x030500B1"
)),
]),
MethodSlot
(
reprfunc
,
"tp_repr"
,
"__repr__"
),
SuiteSlot
(
PyNumberMethods
,
"PyNumberMethods"
,
"tp_as_number"
),
...
...
Cython/Parser/Grammar
View file @
a129d192
...
...
@@ -13,7 +13,8 @@ eval_input: testlist NEWLINE* ENDMARKER
decorator: '@' dotted_PY_NAME [ '(' [arglist] ')' ] NEWLINE
decorators: decorator+
decorated: decorators (classdef | funcdef | cdef_stmt)
decorated: decorators (classdef | funcdef | async_funcdef | cdef_stmt)
async_funcdef: 'async' funcdef
funcdef: 'def' PY_NAME parameters ['->' test] ':' suite
parameters: '(' [typedargslist] ')'
typedargslist: (tfpdef ['=' (test | '*')] (',' tfpdef ['=' (test | '*')])* [','
...
...
@@ -96,7 +97,8 @@ shift_expr: arith_expr (('<<'|'>>') arith_expr)*
arith_expr: term (('+'|'-') term)*
term: factor (('*'|'/'|'%'|'//') factor)*
factor: ('+'|'-'|'~') factor | power | address | size_of | cast
power: atom trailer* ['**' factor]
power: atom_expr ['**' factor]
atom_expr: ['await'] atom trailer*
atom: ('(' [yield_expr|testlist_comp] ')' |
'[' [testlist_comp] ']' |
'{' [dictorsetmaker] '}' |
...
...
Cython/Utility/
Generator
.c
→
Cython/Utility/
Coroutine
.c
View file @
a129d192
This diff is collapsed.
Click to expand it.
Cython/Utility/CythonFunction.c
View file @
a129d192
...
...
@@ -68,7 +68,7 @@ static CYTHON_INLINE void __Pyx_CyFunction_SetAnnotationsDict(PyObject *m,
PyObject
*
dict
);
static
int
__
P
yx_CyFunction_init
(
void
);
static
int
__
p
yx_CyFunction_init
(
void
);
//////////////////// CythonFunction ////////////////////
//@substitute: naming
...
...
@@ -693,7 +693,7 @@ static PyTypeObject __pyx_CyFunctionType_type = {
};
static
int
__
P
yx_CyFunction_init
(
void
)
{
static
int
__
p
yx_CyFunction_init
(
void
)
{
#if !CYTHON_COMPILING_IN_PYPY
// avoid a useless level of call indirection
__pyx_CyFunctionType_type
.
tp_call
=
PyCFunction_Call
;
...
...
tests/errors/pep492_badsyntax_async1.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
def
foo
(
a
=
await
list
()):
pass
_ERRORS
=
"""
5:14: 'await' not supported here
"""
tests/errors/pep492_badsyntax_async2.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
def
foo
(
a
:
await
list
()):
pass
_ERRORS
=
"""
5:14: 'await' not supported here
5:14: 'await' not supported here
"""
tests/errors/pep492_badsyntax_async3.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
[
i
async
for
i
in
els
]
_ERRORS
=
"""
5:7: Expected ']', found 'async'
"""
tests/errors/pep492_badsyntax_async4.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
async
def
foo
():
await
list
()
_ERRORS
=
"""
# ??? - this fails in CPython, not sure why ...
"""
tests/errors/pep492_badsyntax_async5.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
def
foo
():
await
list
()
_ERRORS
=
"""
5:10: Syntax error in simple statement list
"""
tests/errors/pep492_badsyntax_async6.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
yield
_ERRORS
=
"""
5:4: 'yield' not allowed in async coroutines (use 'await')
5:4: 'yield' not supported here
"""
tests/errors/pep492_badsyntax_async7.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
yield
from
[]
_ERRORS
=
"""
5:4: 'yield from' not supported here
5:4: 'yield' not allowed in async coroutines (use 'await')
"""
tests/errors/pep492_badsyntax_async8.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
await
await
fut
_ERRORS
=
"""
5:10: Expected an identifier or literal
"""
tests/errors/pep492_badsyntax_async9.pyx
0 → 100644
View file @
a129d192
# mode: error
# tag: pep492, async
async
def
foo
():
await
_ERRORS
=
"""
5:9: Expected an identifier or literal
"""
tests/run/async_iter_pep492.pyx
0 → 100644
View file @
a129d192
# mode: run
# tag: pep492, asyncfor, await
import
sys
if
sys
.
version_info
>=
(
3
,
5
,
0
,
'beta'
):
# pass Cython implemented AsyncIter() into a Python async-for loop
__doc__
=
u"""
>>> def test_py35():
... buffer = []
... async def coro():
... async for i1, i2 in AsyncIter(1):
... buffer.append(i1 + i2)
... return coro, buffer
>>> testfunc, buffer = test_py35()
>>> buffer
[]
>>> yielded, _ = run_async(testfunc(), check_type=False)
>>> yielded == [i * 100 for i in range(1, 11)] or yielded
True
>>> buffer == [i*2 for i in range(1, 101)] or buffer
True
"""
cdef
class
AsyncYieldFrom
:
cdef
object
obj
def
__init__
(
self
,
obj
):
self
.
obj
=
obj
def
__await__
(
self
):
yield
from
self
.
obj
cdef
class
AsyncYield
:
cdef
object
value
def
__init__
(
self
,
value
):
self
.
value
=
value
def
__await__
(
self
):
yield
self
.
value
def
run_async
(
coro
,
check_type
=
'coroutine'
):
if
check_type
:
assert
coro
.
__class__
.
__name__
==
check_type
,
\
'type(%s) != %s'
%
(
coro
.
__class__
,
check_type
)
buffer
=
[]
result
=
None
while
True
:
try
:
buffer
.
append
(
coro
.
send
(
None
))
except
StopIteration
as
ex
:
result
=
ex
.
args
[
0
]
if
ex
.
args
else
None
break
return
buffer
,
result
cdef
class
AsyncIter
:
cdef
long
i
cdef
long
aiter_calls
cdef
long
max_iter_calls
def
__init__
(
self
,
long
max_iter_calls
=
1
):
self
.
i
=
0
self
.
aiter_calls
=
0
self
.
max_iter_calls
=
max_iter_calls
async
def
__aiter__
(
self
):
self
.
aiter_calls
+=
1
return
self
async
def
__anext__
(
self
):
self
.
i
+=
1
assert
self
.
aiter_calls
<=
self
.
max_iter_calls
if
not
(
self
.
i
%
10
):
await
AsyncYield
(
self
.
i
*
10
)
if
self
.
i
>
100
:
raise
StopAsyncIteration
return
self
.
i
,
self
.
i
def
test_for_1
():
"""
>>> testfunc, buffer = test_for_1()
>>> buffer
[]
>>> yielded, _ = run_async(testfunc())
>>> yielded == [i * 100 for i in range(1, 11)] or yielded
True
>>> buffer == [i*2 for i in range(1, 101)] or buffer
True
"""
buffer
=
[]
async
def
test1
():
async
for
i1
,
i2
in
AsyncIter
(
1
):
buffer
.
append
(
i1
+
i2
)
return
test1
,
buffer
def
test_for_2
():
"""
>>> testfunc, buffer = test_for_2()
>>> buffer
[]
>>> yielded, _ = run_async(testfunc())
>>> yielded == [100, 200] or yielded
True
>>> buffer == [i for i in range(1, 21)] + ['end'] or buffer
True
"""
buffer
=
[]
async
def
test2
():
nonlocal
buffer
async
for
i
in
AsyncIter
(
2
):
buffer
.
append
(
i
[
0
])
if
i
[
0
]
==
20
:
break
else
:
buffer
.
append
(
'what?'
)
buffer
.
append
(
'end'
)
return
test2
,
buffer
def
test_for_3
():
"""
>>> testfunc, buffer = test_for_3()
>>> buffer
[]
>>> yielded, _ = run_async(testfunc())
>>> yielded == [i * 100 for i in range(1, 11)] or yielded
True
>>> buffer == [i for i in range(1, 21)] + ['what?', 'end'] or buffer
True
"""
buffer
=
[]
async
def
test3
():
nonlocal
buffer
async
for
i
in
AsyncIter
(
3
):
if
i
[
0
]
>
20
:
continue
buffer
.
append
(
i
[
0
])
else
:
buffer
.
append
(
'what?'
)
buffer
.
append
(
'end'
)
return
test3
,
buffer
cdef
class
NonAwaitableFromAnext
:
async
def
__aiter__
(
self
):
return
self
def
__anext__
(
self
):
return
123
def
test_broken_anext
():
"""
>>> testfunc = test_broken_anext()
>>> try: run_async(testfunc())
... except TypeError as exc:
... assert ' int ' in str(exc)
... else:
... print("NOT RAISED!")
"""
async
def
foo
():
async
for
i
in
NonAwaitableFromAnext
():
print
(
'never going to happen'
)
return
foo
cdef
class
Manager
:
cdef
readonly
list
counter
def
__init__
(
self
,
counter
):
self
.
counter
=
counter
async
def
__aenter__
(
self
):
self
.
counter
[
0
]
+=
10000
async
def
__aexit__
(
self
,
*
args
):
self
.
counter
[
0
]
+=
100000
cdef
class
Iterable
:
cdef
long
i
def
__init__
(
self
):
self
.
i
=
0
async
def
__aiter__
(
self
):
return
self
async
def
__anext__
(
self
):
if
self
.
i
>
10
:
raise
StopAsyncIteration
self
.
i
+=
1
return
self
.
i
def
test_with_for
():
"""
>>> test_with_for()
111011
333033
20555255
"""
I
=
[
0
]
manager
=
Manager
(
I
)
iterable
=
Iterable
()
mrefs_before
=
sys
.
getrefcount
(
manager
)
irefs_before
=
sys
.
getrefcount
(
iterable
)
async
def
main
():
async
with
manager
:
async
for
i
in
iterable
:
I
[
0
]
+=
1
I
[
0
]
+=
1000
run_async
(
main
())
print
(
I
[
0
])
assert
sys
.
getrefcount
(
manager
)
==
mrefs_before
assert
sys
.
getrefcount
(
iterable
)
==
irefs_before
##############
async
def
main
():
nonlocal
I
async
with
Manager
(
I
):
async
for
i
in
Iterable
():
I
[
0
]
+=
1
I
[
0
]
+=
1000
async
with
Manager
(
I
):
async
for
i
in
Iterable
():
I
[
0
]
+=
1
I
[
0
]
+=
1000
run_async
(
main
())
print
(
I
[
0
])
##############
async
def
main
():
async
with
Manager
(
I
):
I
[
0
]
+=
100
async
for
i
in
Iterable
():
I
[
0
]
+=
1
else
:
I
[
0
]
+=
10000000
I
[
0
]
+=
1000
async
with
Manager
(
I
):
I
[
0
]
+=
100
async
for
i
in
Iterable
():
I
[
0
]
+=
1
else
:
I
[
0
]
+=
10000000
I
[
0
]
+=
1000
run_async
(
main
())
print
(
I
[
0
])
cdef
class
AI
:
async
def
__aiter__
(
self
):
1
/
0
def
test_aiter_raises
():
"""
>>> test_aiter_raises()
RAISED
0
"""
CNT
=
0
async
def
foo
():
nonlocal
CNT
async
for
i
in
AI
():
CNT
+=
1
CNT
+=
10
try
:
run_async
(
foo
())
except
ZeroDivisionError
:
print
(
"RAISED"
)
else
:
print
(
"NOT RAISED"
)
return
CNT
tests/run/test_coroutines_pep492.pyx
0 → 100644
View file @
a129d192
This diff is collapsed.
Click to expand it.
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