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
3e310ce3
Commit
3e310ce3
authored
Mar 23, 2012
by
scoder
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #96 from scoder/_yield_from
implementation of PEP 380 (yield from)
parents
f98829fb
123f1c85
Changes
9
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1436 additions
and
65 deletions
+1436
-65
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+57
-4
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+11
-0
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+2
-4
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+10
-1
Cython/Utility/Generator.c
Cython/Utility/Generator.c
+316
-54
runtests.py
runtests.py
+1
-0
tests/errors/e_generators.pyx
tests/errors/e_generators.pyx
+2
-2
tests/run/generators_in_refcycles.pyx
tests/run/generators_in_refcycles.pyx
+29
-0
tests/run/yield_from_pep380.pyx
tests/run/yield_from_pep380.pyx
+1008
-0
No files found.
Cython/Compiler/ExprNodes.py
View file @
3e310ce3
...
@@ -6538,10 +6538,12 @@ class YieldExprNode(ExprNode):
...
@@ -6538,10 +6538,12 @@ class YieldExprNode(ExprNode):
# arg ExprNode the value to return from the generator
# arg ExprNode the value to return from the generator
# label_name string name of the C label used for this yield
# label_name string name of the C label used for this yield
# label_num integer yield label number
# label_num integer yield label number
# is_yield_from boolean is a YieldFromExprNode to delegate to another generator
subexprs
=
[
'arg'
]
subexprs
=
[
'arg'
]
type
=
py_object_type
type
=
py_object_type
label_num
=
0
label_num
=
0
is_yield_from
=
False
def
analyse_types
(
self
,
env
):
def
analyse_types
(
self
,
env
):
if
not
self
.
label_num
:
if
not
self
.
label_num
:
...
@@ -6550,11 +6552,12 @@ class YieldExprNode(ExprNode):
...
@@ -6550,11 +6552,12 @@ class YieldExprNode(ExprNode):
if
self
.
arg
is
not
None
:
if
self
.
arg
is
not
None
:
self
.
arg
.
analyse_types
(
env
)
self
.
arg
.
analyse_types
(
env
)
if
not
self
.
arg
.
type
.
is_pyobject
:
if
not
self
.
arg
.
type
.
is_pyobject
:
self
.
coerce_yield_argument
(
env
)
def
coerce_yield_argument
(
self
,
env
):
self
.
arg
=
self
.
arg
.
coerce_to_pyobject
(
env
)
self
.
arg
=
self
.
arg
.
coerce_to_pyobject
(
env
)
def
generate_evaluation_code
(
self
,
code
):
def
generate_evaluation_code
(
self
,
code
):
self
.
label_name
=
code
.
new_label
(
'resume_from_yield'
)
code
.
use_label
(
self
.
label_name
)
if
self
.
arg
:
if
self
.
arg
:
self
.
arg
.
generate_evaluation_code
(
code
)
self
.
arg
.
generate_evaluation_code
(
code
)
self
.
arg
.
make_owned_reference
(
code
)
self
.
arg
.
make_owned_reference
(
code
)
...
@@ -6563,10 +6566,19 @@ class YieldExprNode(ExprNode):
...
@@ -6563,10 +6566,19 @@ class YieldExprNode(ExprNode):
Naming
.
retval_cname
,
Naming
.
retval_cname
,
self
.
arg
.
result_as
(
py_object_type
)))
self
.
arg
.
result_as
(
py_object_type
)))
self
.
arg
.
generate_post_assignment_code
(
code
)
self
.
arg
.
generate_post_assignment_code
(
code
)
#self.arg.generate_disposal_code(code)
self
.
arg
.
free_temps
(
code
)
self
.
arg
.
free_temps
(
code
)
else
:
else
:
code
.
put_init_to_py_none
(
Naming
.
retval_cname
,
py_object_type
)
code
.
put_init_to_py_none
(
Naming
.
retval_cname
,
py_object_type
)
self
.
generate_yield_code
(
code
)
def
generate_yield_code
(
self
,
code
):
"""
Generate the code to return the argument in 'Naming.retval_cname'
and to continue at the yield label.
"""
self
.
label_name
=
code
.
new_label
(
'resume_from_yield'
)
code
.
use_label
(
self
.
label_name
)
saved
=
[]
saved
=
[]
code
.
funcstate
.
closure_temps
.
reset
()
code
.
funcstate
.
closure_temps
.
reset
()
for
cname
,
type
,
manage_ref
in
code
.
funcstate
.
temps_in_use
():
for
cname
,
type
,
manage_ref
in
code
.
funcstate
.
temps_in_use
():
...
@@ -6582,6 +6594,7 @@ class YieldExprNode(ExprNode):
...
@@ -6582,6 +6594,7 @@ class YieldExprNode(ExprNode):
code
.
putln
(
"%s->resume_label = %d;"
%
(
code
.
putln
(
"%s->resume_label = %d;"
%
(
Naming
.
generator_cname
,
self
.
label_num
))
Naming
.
generator_cname
,
self
.
label_num
))
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
);
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
);
code
.
put_label
(
self
.
label_name
)
code
.
put_label
(
self
.
label_name
)
for
cname
,
save_cname
,
type
in
saved
:
for
cname
,
save_cname
,
type
in
saved
:
code
.
putln
(
'%s = %s->%s;'
%
(
cname
,
Naming
.
cur_scope_cname
,
save_cname
))
code
.
putln
(
'%s = %s->%s;'
%
(
cname
,
Naming
.
cur_scope_cname
,
save_cname
))
...
@@ -6599,6 +6612,46 @@ class YieldExprNode(ExprNode):
...
@@ -6599,6 +6612,46 @@ class YieldExprNode(ExprNode):
code
.
putln
(
code
.
error_goto_if_null
(
Naming
.
sent_value_cname
,
self
.
pos
))
code
.
putln
(
code
.
error_goto_if_null
(
Naming
.
sent_value_cname
,
self
.
pos
))
class
YieldFromExprNode
(
YieldExprNode
):
# "yield from GEN" expression
is_yield_from
=
True
def
coerce_yield_argument
(
self
,
env
):
if
not
self
.
arg
.
type
.
is_string
:
# FIXME: support C arrays and C++ iterators?
error
(
self
.
pos
,
"yielding from non-Python object not supported"
)
self
.
arg
=
self
.
arg
.
coerce_to_pyobject
(
env
)
def
generate_evaluation_code
(
self
,
code
):
code
.
globalstate
.
use_utility_code
(
UtilityCode
.
load_cached
(
"YieldFrom"
,
"Generator.c"
))
self
.
arg
.
generate_evaluation_code
(
code
)
code
.
putln
(
"%s = __Pyx_Generator_Yield_From(%s, %s);"
%
(
Naming
.
retval_cname
,
Naming
.
generator_cname
,
self
.
arg
.
result_as
(
py_object_type
)))
self
.
arg
.
generate_disposal_code
(
code
)
self
.
arg
.
free_temps
(
code
)
code
.
put_xgotref
(
Naming
.
retval_cname
)
code
.
putln
(
"if (likely(%s)) {"
%
Naming
.
retval_cname
)
self
.
generate_yield_code
(
code
)
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
(
"if (__Pyx_PyGen_FetchStopIterationValue(&%s) < 0) %s"
%
(
self
.
result
(),
code
.
error_goto
(
self
.
pos
)))
else
:
code
.
putln
(
"PyObject* exc_type = PyErr_Occurred();"
)
code
.
putln
(
"if (exc_type) {"
)
code
.
putln
(
"if (!PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration)) %s"
%
code
.
error_goto
(
self
.
pos
))
code
.
putln
(
"PyErr_Clear();"
)
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
class
GlobalsExprNode
(
AtomicExprNode
):
class
GlobalsExprNode
(
AtomicExprNode
):
type
=
dict_type
type
=
dict_type
is_temp
=
1
is_temp
=
1
...
...
Cython/Compiler/Nodes.py
View file @
3e310ce3
...
@@ -4182,6 +4182,8 @@ class GeneratorBodyDefNode(DefNode):
...
@@ -4182,6 +4182,8 @@ class GeneratorBodyDefNode(DefNode):
code
.
put_label
(
code
.
return_label
)
code
.
put_label
(
code
.
return_label
)
code
.
put_xdecref
(
Naming
.
retval_cname
,
py_object_type
)
code
.
put_xdecref
(
Naming
.
retval_cname
,
py_object_type
)
code
.
putln
(
'%s->resume_label = -1;'
%
Naming
.
generator_cname
)
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
.
put_finish_refcount_context
()
code
.
put_finish_refcount_context
()
code
.
putln
(
'return NULL;'
)
code
.
putln
(
'return NULL;'
)
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
...
@@ -5297,9 +5299,11 @@ class ReturnStatNode(StatNode):
...
@@ -5297,9 +5299,11 @@ class ReturnStatNode(StatNode):
#
#
# value ExprNode or None
# value ExprNode or None
# return_type PyrexType
# return_type PyrexType
# in_generator return inside of generator => raise StopIteration
child_attrs
=
[
"value"
]
child_attrs
=
[
"value"
]
is_terminator
=
True
is_terminator
=
True
in_generator
=
False
# Whether we are in a parallel section
# Whether we are in a parallel section
in_parallel
=
False
in_parallel
=
False
...
@@ -5349,6 +5353,13 @@ class ReturnStatNode(StatNode):
...
@@ -5349,6 +5353,13 @@ class ReturnStatNode(StatNode):
rhs
=
self
.
value
,
rhs
=
self
.
value
,
code
=
code
,
code
=
code
,
have_gil
=
self
.
in_nogil_context
)
have_gil
=
self
.
in_nogil_context
)
elif
self
.
in_generator
:
# return value == raise StopIteration(value), but uncatchable
code
.
putln
(
"%s = NULL; PyErr_SetObject(PyExc_StopIteration, %s);"
%
(
Naming
.
retval_cname
,
self
.
value
.
result_as
(
self
.
return_type
)))
self
.
value
.
generate_disposal_code
(
code
)
else
:
else
:
self
.
value
.
make_owned_reference
(
code
)
self
.
value
.
make_owned_reference
(
code
)
code
.
putln
(
code
.
putln
(
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
3e310ce3
...
@@ -2025,16 +2025,12 @@ class YieldNodeCollector(TreeVisitor):
...
@@ -2025,16 +2025,12 @@ class YieldNodeCollector(TreeVisitor):
return
self
.
visitchildren
(
node
)
return
self
.
visitchildren
(
node
)
def
visit_YieldExprNode
(
self
,
node
):
def
visit_YieldExprNode
(
self
,
node
):
if
self
.
has_return_value
:
error
(
node
.
pos
,
"'yield' outside function"
)
self
.
yields
.
append
(
node
)
self
.
yields
.
append
(
node
)
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
def
visit_ReturnStatNode
(
self
,
node
):
def
visit_ReturnStatNode
(
self
,
node
):
if
node
.
value
:
if
node
.
value
:
self
.
has_return_value
=
True
self
.
has_return_value
=
True
if
self
.
yields
:
error
(
node
.
pos
,
"'return' with argument inside generator"
)
self
.
returns
.
append
(
node
)
self
.
returns
.
append
(
node
)
def
visit_ClassDefNode
(
self
,
node
):
def
visit_ClassDefNode
(
self
,
node
):
...
@@ -2071,6 +2067,8 @@ class MarkClosureVisitor(CythonTransform):
...
@@ -2071,6 +2067,8 @@ class MarkClosureVisitor(CythonTransform):
return
node
return
node
for
i
,
yield_expr
in
enumerate
(
collector
.
yields
):
for
i
,
yield_expr
in
enumerate
(
collector
.
yields
):
yield_expr
.
label_num
=
i
+
1
yield_expr
.
label_num
=
i
+
1
for
retnode
in
collector
.
returns
:
retnode
.
in_generator
=
True
gbody
=
Nodes
.
GeneratorBodyDefNode
(
gbody
=
Nodes
.
GeneratorBodyDefNode
(
pos
=
node
.
pos
,
name
=
node
.
name
,
body
=
node
.
body
)
pos
=
node
.
pos
,
name
=
node
.
name
,
body
=
node
.
body
)
...
...
Cython/Compiler/Parsing.py
View file @
3e310ce3
...
@@ -340,10 +340,19 @@ def p_yield_expression(s):
...
@@ -340,10 +340,19 @@ def p_yield_expression(s):
# s.sy == "yield"
# s.sy == "yield"
pos
=
s
.
position
()
pos
=
s
.
position
()
s
.
next
()
s
.
next
()
is_yield_from
=
False
if
s
.
sy
==
'from'
:
is_yield_from
=
True
s
.
next
()
if
s
.
sy
!=
')'
and
s
.
sy
not
in
statement_terminators
:
if
s
.
sy
!=
')'
and
s
.
sy
not
in
statement_terminators
:
arg
=
p_testlist
(
s
)
arg
=
p_testlist
(
s
)
else
:
else
:
if
is_yield_from
:
s
.
error
(
"'yield from' requires a source argument"
,
pos
=
pos
)
arg
=
None
arg
=
None
if
is_yield_from
:
return
ExprNodes
.
YieldFromExprNode
(
pos
,
arg
=
arg
)
else
:
return
ExprNodes
.
YieldExprNode
(
pos
,
arg
=
arg
)
return
ExprNodes
.
YieldExprNode
(
pos
,
arg
=
arg
)
def
p_yield_statement
(
s
):
def
p_yield_statement
(
s
):
...
...
Cython/Utility/Generator.c
View file @
3e310ce3
This diff is collapsed.
Click to expand it.
runtests.py
View file @
3e310ce3
...
@@ -166,6 +166,7 @@ VER_DEP_MODULES = {
...
@@ -166,6 +166,7 @@ VER_DEP_MODULES = {
]),
]),
(2,5) : (operator.lt, lambda x: x in ['run.any',
(2,5) : (operator.lt, lambda x: x in ['run.any',
'run.all',
'run.all',
'run.yield_from_pep380', # GeneratorExit
'run.relativeimport_T542',
'run.relativeimport_T542',
'run.relativeimport_star_T542',
'run.relativeimport_star_T542',
]),
]),
...
...
tests/errors/e_generators.pyx
View file @
3e310ce3
...
@@ -14,8 +14,8 @@ class Foo:
...
@@ -14,8 +14,8 @@ class Foo:
yield
yield
_ERRORS
=
u"""
_ERRORS
=
u"""
5:4: 'return' with argument inside generator
#
5:4: 'return' with argument inside generator
9:4: 'yield' outside function
#
9:4: 'yield' outside function
11:0: 'yield' not supported here
11:0: 'yield' not supported here
14:4: 'yield' not supported here
14:4: 'yield' not supported here
"""
"""
tests/run/generators_in_refcycles.pyx
0 → 100644
View file @
3e310ce3
def
test_reference_cycle_cleanup
():
"""
>>> import gc
>>> delegator, gen, next, deleted = test_reference_cycle_cleanup()
>>> next(delegator(gen()))
123
>>> _ = gc.collect(); print(sorted(deleted))
['bar', 'foo']
"""
deleted
=
[]
class
Destructed
(
object
):
def
__init__
(
self
,
name
):
self
.
name
=
name
def
__del__
(
self
):
deleted
.
append
(
self
.
name
)
def
delegator
(
c
):
d
=
Destructed
(
'foo'
)
yield
from
c
def
gen
():
d
=
Destructed
(
'bar'
)
while
True
:
yield
123
return
delegator
,
gen
,
next
,
deleted
tests/run/yield_from_pep380.pyx
0 → 100644
View file @
3e310ce3
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