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
7ef3e52c
Commit
7ef3e52c
authored
Apr 02, 2011
by
Stefan Behnel
Browse files
Options
Browse Files
Download
Plain Diff
merged in Vitek's generators branch
parents
b2b337b2
fb3ac076
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
1237 additions
and
97 deletions
+1237
-97
Cython/Compiler/Code.pxd
Cython/Compiler/Code.pxd
+2
-0
Cython/Compiler/Code.py
Cython/Compiler/Code.py
+33
-0
Cython/Compiler/ExprNodes.py
Cython/Compiler/ExprNodes.py
+194
-37
Cython/Compiler/ModuleNode.py
Cython/Compiler/ModuleNode.py
+12
-3
Cython/Compiler/Naming.py
Cython/Compiler/Naming.py
+2
-4
Cython/Compiler/Nodes.py
Cython/Compiler/Nodes.py
+216
-35
Cython/Compiler/Optimize.py
Cython/Compiler/Optimize.py
+3
-2
Cython/Compiler/ParseTreeTransforms.pxd
Cython/Compiler/ParseTreeTransforms.pxd
+7
-0
Cython/Compiler/ParseTreeTransforms.py
Cython/Compiler/ParseTreeTransforms.py
+151
-13
Cython/Compiler/Parsing.pxd
Cython/Compiler/Parsing.pxd
+1
-0
Cython/Compiler/Parsing.py
Cython/Compiler/Parsing.py
+8
-0
Cython/Compiler/Scanning.py
Cython/Compiler/Scanning.py
+1
-1
Cython/Compiler/Symtab.py
Cython/Compiler/Symtab.py
+25
-0
Cython/Compiler/TypeInference.py
Cython/Compiler/TypeInference.py
+2
-1
tests/bugs.txt
tests/bugs.txt
+1
-1
tests/errors/e_generators.pyx
tests/errors/e_generators.pyx
+19
-0
tests/errors/e_nonlocal_T490.pyx
tests/errors/e_nonlocal_T490.pyx
+35
-0
tests/run/closure_self.pyx
tests/run/closure_self.pyx
+38
-0
tests/run/generator_type_inference.pyx
tests/run/generator_type_inference.pyx
+13
-0
tests/run/generators.pyx
tests/run/generators.pyx
+317
-0
tests/run/nonlocal_T490.pyx
tests/run/nonlocal_T490.pyx
+157
-0
No files found.
Cython/Compiler/Code.pxd
View file @
7ef3e52c
...
@@ -34,6 +34,8 @@ cdef class FunctionState:
...
@@ -34,6 +34,8 @@ cdef class FunctionState:
cdef
public
dict
temps_used_type
cdef
public
dict
temps_used_type
cdef
public
size_t
temp_counter
cdef
public
size_t
temp_counter
cdef
public
object
closure_temps
@
cython
.
locals
(
n
=
size_t
)
@
cython
.
locals
(
n
=
size_t
)
cpdef
new_label
(
self
,
name
=*
)
cpdef
new_label
(
self
,
name
=*
)
cpdef
tuple
get_loop_labels
(
self
)
cpdef
tuple
get_loop_labels
(
self
)
...
...
Cython/Compiler/Code.py
View file @
7ef3e52c
...
@@ -117,6 +117,7 @@ class FunctionState(object):
...
@@ -117,6 +117,7 @@ class FunctionState(object):
self
.
temps_free
=
{}
# (type, manage_ref) -> list of free vars with same type/managed status
self
.
temps_free
=
{}
# (type, manage_ref) -> list of free vars with same type/managed status
self
.
temps_used_type
=
{}
# name -> (type, manage_ref)
self
.
temps_used_type
=
{}
# name -> (type, manage_ref)
self
.
temp_counter
=
0
self
.
temp_counter
=
0
self
.
closure_temps
=
None
# labels
# labels
...
@@ -270,6 +271,9 @@ class FunctionState(object):
...
@@ -270,6 +271,9 @@ class FunctionState(object):
if
manage_ref
if
manage_ref
for
cname
in
freelist
]
for
cname
in
freelist
]
def
init_closure_temps
(
self
,
scope
):
self
.
closure_temps
=
ClosureTempAllocator
(
scope
)
class
IntConst
(
object
):
class
IntConst
(
object
):
"""Global info about a Python integer constant held by GlobalState.
"""Global info about a Python integer constant held by GlobalState.
...
@@ -475,6 +479,7 @@ class GlobalState(object):
...
@@ -475,6 +479,7 @@ class GlobalState(object):
w.enter_cfunc_scope()
w.enter_cfunc_scope()
w.putln("")
w.putln("")
w.putln("
static
int
__Pyx_InitCachedConstants
(
void
)
{
")
w.putln("
static
int
__Pyx_InitCachedConstants
(
void
)
{
")
w.put_declare_refcount_context()
w.put_setup_refcount_context("
__Pyx_InitCachedConstants
")
w.put_setup_refcount_context("
__Pyx_InitCachedConstants
")
w = self.parts['init_globals']
w = self.parts['init_globals']
...
@@ -1297,6 +1302,8 @@ class CCodeWriter(object):
...
@@ -1297,6 +1302,8 @@ class CCodeWriter(object):
#if entry.type.is_extension_type:
#if entry.type.is_extension_type:
# code = "((PyObject*)%s)" % code
# code = "((PyObject*)%s)" % code
self
.
put_init_to_py_none
(
code
,
entry
.
type
,
nanny
)
self
.
put_init_to_py_none
(
code
,
entry
.
type
,
nanny
)
if
entry
.
in_closure
:
self
.
put_giveref
(
'Py_None'
)
def
put_pymethoddef
(
self
,
entry
,
term
,
allow_skip
=
True
):
def
put_pymethoddef
(
self
,
entry
,
term
,
allow_skip
=
True
):
if
entry
.
is_special
or
entry
.
name
==
'__getattribute__'
:
if
entry
.
is_special
or
entry
.
name
==
'__getattribute__'
:
...
@@ -1366,6 +1373,9 @@ class CCodeWriter(object):
...
@@ -1366,6 +1373,9 @@ class CCodeWriter(object):
def
lookup_filename
(
self
,
filename
):
def
lookup_filename
(
self
,
filename
):
return
self
.
globalstate
.
lookup_filename
(
filename
)
return
self
.
globalstate
.
lookup_filename
(
filename
)
def
put_declare_refcount_context
(
self
):
self
.
putln
(
'__Pyx_RefNannyDeclareContext;'
)
def
put_setup_refcount_context
(
self
,
name
):
def
put_setup_refcount_context
(
self
,
name
):
self
.
putln
(
'__Pyx_RefNannySetupContext("%s");'
%
name
)
self
.
putln
(
'__Pyx_RefNannySetupContext("%s");'
%
name
)
...
@@ -1402,3 +1412,26 @@ class PyrexCodeWriter(object):
...
@@ -1402,3 +1412,26 @@ class PyrexCodeWriter(object):
def
dedent
(
self
):
def
dedent
(
self
):
self
.
level
-=
1
self
.
level
-=
1
class
ClosureTempAllocator
(
object
):
def
__init__
(
self
,
klass
):
self
.
klass
=
klass
self
.
temps_allocated
=
{}
self
.
temps_free
=
{}
self
.
temps_count
=
0
def
reset
(
self
):
for
type
,
cnames
in
self
.
temps_allocated
.
items
():
self
.
temps_free
[
type
]
=
list
(
cnames
)
def
allocate_temp
(
self
,
type
):
if
not
type
in
self
.
temps_allocated
:
self
.
temps_allocated
[
type
]
=
[]
self
.
temps_free
[
type
]
=
[]
elif
self
.
temps_free
[
type
]:
return
self
.
temps_free
[
type
].
pop
(
0
)
cname
=
'%s%d'
%
(
Naming
.
codewriter_temp_prefix
,
self
.
temps_count
)
self
.
klass
.
declare_var
(
pos
=
None
,
name
=
cname
,
cname
=
cname
,
type
=
type
,
is_cdef
=
True
)
self
.
temps_allocated
[
type
].
append
(
cname
)
self
.
temps_count
+=
1
return
cname
Cython/Compiler/ExprNodes.py
View file @
7ef3e52c
...
@@ -77,12 +77,15 @@ class ExprNode(Node):
...
@@ -77,12 +77,15 @@ class ExprNode(Node):
# [ExprNode or [ExprNode or None] or None]
# [ExprNode or [ExprNode or None] or None]
# Cached result of subexpr_nodes()
# Cached result of subexpr_nodes()
# use_managed_ref boolean use ref-counted temps/assignments/etc.
# use_managed_ref boolean use ref-counted temps/assignments/etc.
# result_is_used boolean indicates that the result will be dropped and the
# result_code/temp_result can safely be set to None
result_ctype
=
None
result_ctype
=
None
type
=
None
type
=
None
temp_code
=
None
temp_code
=
None
old_temp
=
None
# error checker for multiple frees etc.
old_temp
=
None
# error checker for multiple frees etc.
use_managed_ref
=
True
# can be set by optimisation transforms
use_managed_ref
=
True
# can be set by optimisation transforms
result_is_used
=
True
# The Analyse Expressions phase for expressions is split
# The Analyse Expressions phase for expressions is split
# into two sub-phases:
# into two sub-phases:
...
@@ -452,6 +455,9 @@ class ExprNode(Node):
...
@@ -452,6 +455,9 @@ class ExprNode(Node):
def
release_temp_result
(
self
,
code
):
def
release_temp_result
(
self
,
code
):
if
not
self
.
temp_code
:
if
not
self
.
temp_code
:
if
not
self
.
result_is_used
:
# not used anyway, so ignore if not set up
return
if
self
.
old_temp
:
if
self
.
old_temp
:
raise
RuntimeError
(
"temp %s released multiple times in %s"
%
(
raise
RuntimeError
(
"temp %s released multiple times in %s"
%
(
self
.
old_temp
,
self
.
__class__
.
__name__
))
self
.
old_temp
,
self
.
__class__
.
__name__
))
...
@@ -497,7 +503,7 @@ class ExprNode(Node):
...
@@ -497,7 +503,7 @@ class ExprNode(Node):
def
generate_disposal_code
(
self
,
code
):
def
generate_disposal_code
(
self
,
code
):
if
self
.
is_temp
:
if
self
.
is_temp
:
if
self
.
type
.
is_pyobject
:
if
self
.
type
.
is_pyobject
and
self
.
result
()
:
code
.
put_decref_clear
(
self
.
result
(),
self
.
ctype
())
code
.
put_decref_clear
(
self
.
result
(),
self
.
ctype
())
else
:
else
:
# Already done if self.is_temp
# Already done if self.is_temp
...
@@ -1628,9 +1634,10 @@ class NameNode(AtomicExprNode):
...
@@ -1628,9 +1634,10 @@ class NameNode(AtomicExprNode):
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
#print "...RHS type", rhs.type, "ctype", rhs.ctype() ###
if
self
.
use_managed_ref
:
if
self
.
use_managed_ref
:
rhs
.
make_owned_reference
(
code
)
rhs
.
make_owned_reference
(
code
)
if
entry
.
is_cglobal
:
is_external_ref
=
entry
.
is_cglobal
or
self
.
entry
.
in_closure
or
self
.
entry
.
from_closure
code
.
put_gotref
(
self
.
py_result
())
if
not
self
.
lhs_of_first_assignment
:
if
not
self
.
lhs_of_first_assignment
:
if
is_external_ref
:
code
.
put_gotref
(
self
.
py_result
())
if
entry
.
is_local
and
not
Options
.
init_local_none
:
if
entry
.
is_local
and
not
Options
.
init_local_none
:
initialized
=
entry
.
scope
.
control_flow
.
get_state
((
entry
.
name
,
'initialized'
),
self
.
pos
)
initialized
=
entry
.
scope
.
control_flow
.
get_state
((
entry
.
name
,
'initialized'
),
self
.
pos
)
if
initialized
is
True
:
if
initialized
is
True
:
...
@@ -1639,7 +1646,7 @@ class NameNode(AtomicExprNode):
...
@@ -1639,7 +1646,7 @@ class NameNode(AtomicExprNode):
code
.
put_xdecref
(
self
.
result
(),
self
.
ctype
())
code
.
put_xdecref
(
self
.
result
(),
self
.
ctype
())
else
:
else
:
code
.
put_decref
(
self
.
result
(),
self
.
ctype
())
code
.
put_decref
(
self
.
result
(),
self
.
ctype
())
if
entry
.
is_cglobal
:
if
is_external_ref
:
code
.
put_giveref
(
rhs
.
py_result
())
code
.
put_giveref
(
rhs
.
py_result
())
code
.
putln
(
'%s = %s;'
%
(
self
.
result
(),
code
.
putln
(
'%s = %s;'
%
(
self
.
result
(),
...
@@ -4442,15 +4449,18 @@ class DictComprehensionAppendNode(ComprehensionAppendNode):
...
@@ -4442,15 +4449,18 @@ class DictComprehensionAppendNode(ComprehensionAppendNode):
self
.
value_expr
.
annotate
(
code
)
self
.
value_expr
.
annotate
(
code
)
class
GeneratorExpressionNode
(
ScopedExprNode
):
class
InlinedGeneratorExpressionNode
(
ScopedExprNode
):
# A generator expression, e.g. (i for i in range(10))
# An inlined generator expression for which the result is
#
# calculated inside of the loop. This will only be created by
# Result is a generator.
# transforms when replacing builtin calls on generator
# expressions.
#
#
# loop ForStatNode the for-loop, containing a YieldExprNode
# loop ForStatNode the for-loop, not containing any YieldExprNodes
# result_node ResultRefNode the reference to the result value temp
# orig_func String the name of the builtin function this node replaces
child_attrs
=
[
"loop"
]
child_attrs
=
[
"loop"
]
loop_analysed
=
False
type
=
py_object_type
type
=
py_object_type
def
analyse_scoped_declarations
(
self
,
env
):
def
analyse_scoped_declarations
(
self
,
env
):
...
@@ -4461,30 +4471,12 @@ class GeneratorExpressionNode(ScopedExprNode):
...
@@ -4461,30 +4471,12 @@ class GeneratorExpressionNode(ScopedExprNode):
self
.
loop
.
analyse_expressions
(
env
)
self
.
loop
.
analyse_expressions
(
env
)
self
.
is_temp
=
True
self
.
is_temp
=
True
def
analyse_scoped_expressions
(
self
,
env
):
if
self
.
has_local_scope
:
self
.
loop
.
analyse_expressions
(
env
)
def
may_be_none
(
self
):
def
may_be_none
(
self
):
return
False
return
False
def
annotate
(
self
,
code
):
def
annotate
(
self
,
code
):
self
.
loop
.
annotate
(
code
)
self
.
loop
.
annotate
(
code
)
class
InlinedGeneratorExpressionNode
(
GeneratorExpressionNode
):
# An inlined generator expression for which the result is
# calculated inside of the loop. This will only be created by
# transforms when replacing builtin calls on generator
# expressions.
#
# loop ForStatNode the for-loop, not containing any YieldExprNodes
# result_node ResultRefNode the reference to the result value temp
# orig_func String the name of the builtin function this node replaces
child_attrs
=
[
"loop"
]
loop_analysed
=
False
def
infer_type
(
self
,
env
):
def
infer_type
(
self
,
env
):
return
self
.
result_node
.
infer_type
(
env
)
return
self
.
result_node
.
infer_type
(
env
)
...
@@ -4497,7 +4489,8 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
...
@@ -4497,7 +4489,8 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
def
analyse_scoped_expressions
(
self
,
env
):
def
analyse_scoped_expressions
(
self
,
env
):
self
.
loop_analysed
=
True
self
.
loop_analysed
=
True
GeneratorExpressionNode
.
analyse_scoped_expressions
(
self
,
env
)
if
self
.
has_local_scope
:
self
.
loop
.
analyse_expressions
(
env
)
def
coerce_to
(
self
,
dst_type
,
env
):
def
coerce_to
(
self
,
dst_type
,
env
):
if
self
.
orig_func
==
'sum'
and
dst_type
.
is_numeric
and
not
self
.
loop_analysed
:
if
self
.
orig_func
==
'sum'
and
dst_type
.
is_numeric
and
not
self
.
loop_analysed
:
...
@@ -4508,7 +4501,7 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
...
@@ -4508,7 +4501,7 @@ class InlinedGeneratorExpressionNode(GeneratorExpressionNode):
# assignments.
# assignments.
self
.
result_node
.
type
=
self
.
type
=
dst_type
self
.
result_node
.
type
=
self
.
type
=
dst_type
return
self
return
self
return
GeneratorExpressionNode
.
coerce_to
(
self
,
dst_type
,
env
)
return
super
(
InlinedGeneratorExpressionNode
,
self
).
coerce_to
(
dst_type
,
env
)
def
generate_result_code
(
self
,
code
):
def
generate_result_code
(
self
,
code
):
self
.
result_node
.
result_code
=
self
.
result
()
self
.
result_node
.
result_code
=
self
.
result
()
...
@@ -5055,32 +5048,98 @@ class LambdaNode(InnerFunctionNode):
...
@@ -5055,32 +5048,98 @@ class LambdaNode(InnerFunctionNode):
self
.
pymethdef_cname
=
self
.
def_node
.
entry
.
pymethdef_cname
self
.
pymethdef_cname
=
self
.
def_node
.
entry
.
pymethdef_cname
env
.
add_lambda_def
(
self
.
def_node
)
env
.
add_lambda_def
(
self
.
def_node
)
class
GeneratorExpressionNode
(
LambdaNode
):
# A generator expression, e.g. (i for i in range(10))
#
# Result is a generator.
#
# loop ForStatNode the for-loop, containing a YieldExprNode
# def_node DefNode the underlying generator 'def' node
name
=
StringEncoding
.
EncodedString
(
'genexpr'
)
binding
=
False
def
analyse_declarations
(
self
,
env
):
self
.
def_node
.
no_assignment_synthesis
=
True
self
.
def_node
.
analyse_declarations
(
env
)
env
.
add_lambda_def
(
self
.
def_node
)
def
generate_result_code
(
self
,
code
):
code
.
putln
(
'%s = %s(%s, NULL); %s'
%
(
self
.
result
(),
self
.
def_node
.
entry
.
func_cname
,
self
.
self_result_code
(),
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_gotref
(
self
.
py_result
())
class
YieldExprNode
(
ExprNode
):
class
YieldExprNode
(
ExprNode
):
# Yield expression node
# Yield expression node
#
#
# 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
subexprs
=
[
'arg'
]
subexprs
=
[
'arg'
]
type
=
py_object_type
type
=
py_object_type
label_num
=
0
def
analyse_types
(
self
,
env
):
def
analyse_types
(
self
,
env
):
if
not
self
.
label_num
:
error
(
self
.
pos
,
"'yield' not supported here"
)
self
.
is_temp
=
1
self
.
is_temp
=
1
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
.
arg
=
self
.
arg
.
coerce_to_pyobject
(
env
)
self
.
arg
=
self
.
arg
.
coerce_to_pyobject
(
env
)
e
rror
(
self
.
pos
,
"Generators are not supported"
)
e
nv
.
use_utility_code
(
generator_utility_code
)
def
generate_
result
_code
(
self
,
code
):
def
generate_
evaluation
_code
(
self
,
code
):
self
.
label_name
=
code
.
new_label
(
'resume_from_yield'
)
self
.
label_name
=
code
.
new_label
(
'resume_from_yield'
)
code
.
use_label
(
self
.
label_name
)
code
.
use_label
(
self
.
label_name
)
code
.
putln
(
"/* FIXME: save temporary variables */"
)
if
self
.
arg
:
code
.
putln
(
"/* FIXME: return from function, yielding value */"
)
self
.
arg
.
generate_evaluation_code
(
code
)
self
.
arg
.
make_owned_reference
(
code
)
code
.
putln
(
"%s = %s;"
%
(
Naming
.
retval_cname
,
self
.
arg
.
result_as
(
py_object_type
)))
self
.
arg
.
generate_post_assignment_code
(
code
)
#self.arg.generate_disposal_code(code)
self
.
arg
.
free_temps
(
code
)
else
:
code
.
put_init_to_py_none
(
Naming
.
retval_cname
,
py_object_type
)
saved
=
[]
code
.
funcstate
.
closure_temps
.
reset
()
for
cname
,
type
,
manage_ref
in
code
.
funcstate
.
temps_in_use
():
save_cname
=
code
.
funcstate
.
closure_temps
.
allocate_temp
(
type
)
saved
.
append
((
cname
,
save_cname
,
type
))
if
type
.
is_pyobject
:
code
.
put_xgiveref
(
cname
)
code
.
putln
(
'%s->%s = %s;'
%
(
Naming
.
cur_scope_cname
,
save_cname
,
cname
))
code
.
put_xgiveref
(
Naming
.
retval_cname
)
code
.
put_finish_refcount_context
()
code
.
putln
(
"/* return from generator, yielding value */"
)
code
.
putln
(
"%s->%s.resume_label = %d;"
%
(
Naming
.
cur_scope_cname
,
Naming
.
obj_base_cname
,
self
.
label_num
))
code
.
putln
(
"return %s;"
%
Naming
.
retval_cname
);
code
.
put_label
(
self
.
label_name
)
code
.
put_label
(
self
.
label_name
)
code
.
putln
(
"/* FIXME: restore temporary variables and */"
)
for
cname
,
save_cname
,
type
in
saved
:
code
.
putln
(
"/* FIXME: extract sent value from closure */"
)
code
.
putln
(
'%s = %s->%s;'
%
(
cname
,
Naming
.
cur_scope_cname
,
save_cname
))
if
type
.
is_pyobject
:
code
.
putln
(
'%s->%s = 0;'
%
(
Naming
.
cur_scope_cname
,
save_cname
))
if
type
.
is_pyobject
:
code
.
put_xgotref
(
cname
)
if
self
.
result_is_used
:
self
.
allocate_temp_result
(
code
)
code
.
putln
(
'%s = %s; %s'
%
(
self
.
result
(),
Naming
.
sent_value_cname
,
code
.
error_goto_if_null
(
self
.
result
(),
self
.
pos
)))
code
.
put_incref
(
self
.
result
(),
py_object_type
)
else
:
code
.
putln
(
code
.
error_goto_if_null
(
Naming
.
sent_value_cname
,
self
.
pos
))
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#
#
...
@@ -8403,3 +8462,101 @@ static int %(binding_cfunc)s_init(void) {
...
@@ -8403,3 +8462,101 @@ static int %(binding_cfunc)s_init(void) {
}
}
"""
%
Naming
.
__dict__
)
"""
%
Naming
.
__dict__
)
generator_utility_code
=
UtilityCode
(
proto
=
"""
static PyObject *__Pyx_Generator_Next(PyObject *self);
static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value);
static PyObject *__Pyx_Generator_Close(PyObject *self);
static PyObject *__Pyx_Generator_Throw(PyObject *gen, PyObject *args, CYTHON_UNUSED PyObject *kwds);
typedef PyObject *(*__pyx_generator_body_t)(PyObject *, PyObject *);
"""
,
impl
=
"""
static CYTHON_INLINE PyObject *__Pyx_Generator_SendEx(struct __pyx_Generator_object *self, PyObject *value)
{
PyObject *retval;
if (self->is_running) {
PyErr_SetString(PyExc_ValueError,
"generator already executing");
return NULL;
}
if (self->resume_label == 0) {
if (value && value != Py_None) {
PyErr_SetString(PyExc_TypeError,
"can't send non-None value to a "
"just-started generator");
return NULL;
}
}
if (self->resume_label == -1) {
PyErr_SetNone(PyExc_StopIteration);
return NULL;
}
self->is_running = 1;
retval = self->body((PyObject *) self, value);
self->is_running = 0;
return retval;
}
static PyObject *__Pyx_Generator_Next(PyObject *self)
{
return __Pyx_Generator_SendEx((struct __pyx_Generator_object *) self, Py_None);
}
static PyObject *__Pyx_Generator_Send(PyObject *self, PyObject *value)
{
return __Pyx_Generator_SendEx((struct __pyx_Generator_object *) self, value);
}
static PyObject *__Pyx_Generator_Close(PyObject *self)
{
struct __pyx_Generator_object *generator = (struct __pyx_Generator_object *) self;
PyObject *retval;
#if PY_VERSION_HEX < 0x02050000
PyErr_SetNone(PyExc_StopIteration);
#else
PyErr_SetNone(PyExc_GeneratorExit);
#endif
retval = __Pyx_Generator_SendEx(generator, NULL);
if (retval) {
Py_DECREF(retval);
PyErr_SetString(PyExc_RuntimeError,
"generator ignored GeneratorExit");
return NULL;
}
#if PY_VERSION_HEX < 0x02050000
if (PyErr_ExceptionMatches(PyExc_StopIteration))
#else
if (PyErr_ExceptionMatches(PyExc_StopIteration)
|| PyErr_ExceptionMatches(PyExc_GeneratorExit))
#endif
{
PyErr_Clear(); /* ignore these errors */
Py_INCREF(Py_None);
return Py_None;
}
return NULL;
}
static PyObject *__Pyx_Generator_Throw(PyObject *self, PyObject *args, CYTHON_UNUSED PyObject *kwds)
{
struct __pyx_Generator_object *generator = (struct __pyx_Generator_object *) self;
PyObject *typ;
PyObject *tb = NULL;
PyObject *val = NULL;
if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb))
return NULL;
__Pyx_Raise(typ, val, tb);
return __Pyx_Generator_SendEx(generator, NULL);
}
"""
,
proto_block
=
'utility_code_proto_before_types'
,
requires
=
[
Nodes
.
raise_utility_code
],
)
Cython/Compiler/ModuleNode.py
View file @
7ef3e52c
...
@@ -964,9 +964,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
...
@@ -964,9 +964,13 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
type
.
vtabstruct_cname
,
type
.
vtabstruct_cname
,
type
.
vtabslot_cname
))
type
.
vtabslot_cname
))
for
attr
in
type
.
scope
.
var_entries
:
for
attr
in
type
.
scope
.
var_entries
:
if
attr
.
is_declared_generic
:
attr_type
=
py_object_type
else
:
attr_type
=
attr
.
type
code
.
putln
(
code
.
putln
(
"%s;"
%
"%s;"
%
attr
.
type
.
declaration_code
(
attr
.
cname
))
attr
_
type
.
declaration_code
(
attr
.
cname
))
code
.
putln
(
footer
)
code
.
putln
(
footer
)
if
type
.
objtypedef_cname
is
not
None
:
if
type
.
objtypedef_cname
is
not
None
:
# Only for exposing public typedef name.
# Only for exposing public typedef name.
...
@@ -1265,7 +1269,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
...
@@ -1265,7 +1269,10 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode):
for
entry
in
py_attrs
:
for
entry
in
py_attrs
:
name
=
"p->%s"
%
entry
.
cname
name
=
"p->%s"
%
entry
.
cname
code
.
putln
(
"tmp = ((PyObject*)%s);"
%
name
)
code
.
putln
(
"tmp = ((PyObject*)%s);"
%
name
)
code
.
put_init_to_py_none
(
name
,
entry
.
type
,
nanny
=
False
)
if
entry
.
is_declared_generic
:
code
.
put_init_to_py_none
(
name
,
py_object_type
,
nanny
=
False
)
else
:
code
.
put_init_to_py_none
(
name
,
entry
.
type
,
nanny
=
False
)
code
.
putln
(
"Py_XDECREF(tmp);"
)
code
.
putln
(
"Py_XDECREF(tmp);"
)
code
.
putln
(
code
.
putln
(
"return 0;"
)
"return 0;"
)
...
@@ -2762,8 +2769,9 @@ refnanny_utility_code = UtilityCode(proto="""
...
@@ -2762,8 +2769,9 @@ refnanny_utility_code = UtilityCode(proto="""
Py_XDECREF(m);
Py_XDECREF(m);
return (__Pyx_RefNannyAPIStruct *)r;
return (__Pyx_RefNannyAPIStruct *)r;
}
}
#define __Pyx_RefNannyDeclareContext void *__pyx_refnanny;
#define __Pyx_RefNannySetupContext(name)
\
#define __Pyx_RefNannySetupContext(name)
\
void *
__pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
__pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
#define __Pyx_RefNannyFinishContext()
\
#define __Pyx_RefNannyFinishContext()
\
__Pyx_RefNanny->FinishContext(&__pyx_refnanny)
__Pyx_RefNanny->FinishContext(&__pyx_refnanny)
#define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
#define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
...
@@ -2772,6 +2780,7 @@ refnanny_utility_code = UtilityCode(proto="""
...
@@ -2772,6 +2780,7 @@ refnanny_utility_code = UtilityCode(proto="""
#define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
#define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
#define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r);} } while(0)
#define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r);} } while(0)
#else
#else
#define __Pyx_RefNannyDeclareContext
#define __Pyx_RefNannySetupContext(name)
#define __Pyx_RefNannySetupContext(name)
#define __Pyx_RefNannyFinishContext()
#define __Pyx_RefNannyFinishContext()
#define __Pyx_INCREF(r) Py_INCREF(r)
#define __Pyx_INCREF(r) Py_INCREF(r)
...
...
Cython/Compiler/Naming.py
View file @
7ef3e52c
...
@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
...
@@ -19,6 +19,7 @@ funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix
=
pyrex_prefix
+
"e_"
enum_prefix
=
pyrex_prefix
+
"e_"
func_prefix
=
pyrex_prefix
+
"f_"
func_prefix
=
pyrex_prefix
+
"f_"
pyfunc_prefix
=
pyrex_prefix
+
"pf_"
pyfunc_prefix
=
pyrex_prefix
+
"pf_"
genbody_prefix
=
pyrex_prefix
+
"gb_"
gstab_prefix
=
pyrex_prefix
+
"getsets_"
gstab_prefix
=
pyrex_prefix
+
"getsets_"
prop_get_prefix
=
pyrex_prefix
+
"getprop_"
prop_get_prefix
=
pyrex_prefix
+
"getprop_"
const_prefix
=
pyrex_prefix
+
"k_"
const_prefix
=
pyrex_prefix
+
"k_"
...
@@ -51,6 +52,7 @@ lambda_func_prefix = pyrex_prefix + "lambda_"
...
@@ -51,6 +52,7 @@ lambda_func_prefix = pyrex_prefix + "lambda_"
module_is_main
=
pyrex_prefix
+
"module_is_main_"
module_is_main
=
pyrex_prefix
+
"module_is_main_"
args_cname
=
pyrex_prefix
+
"args"
args_cname
=
pyrex_prefix
+
"args"
sent_value_cname
=
pyrex_prefix
+
"sent_value"
pykwdlist_cname
=
pyrex_prefix
+
"pyargnames"
pykwdlist_cname
=
pyrex_prefix
+
"pyargnames"
obj_base_cname
=
pyrex_prefix
+
"base"
obj_base_cname
=
pyrex_prefix
+
"base"
builtins_cname
=
pyrex_prefix
+
"b"
builtins_cname
=
pyrex_prefix
+
"b"
...
@@ -107,10 +109,6 @@ exc_lineno_name = pyrex_prefix + "exc_lineno"
...
@@ -107,10 +109,6 @@ exc_lineno_name = pyrex_prefix + "exc_lineno"
exc_vars
=
(
exc_type_name
,
exc_value_name
,
exc_tb_name
)
exc_vars
=
(
exc_type_name
,
exc_value_name
,
exc_tb_name
)
exc_save_vars
=
(
pyrex_prefix
+
'save_exc_type'
,
pyrex_prefix
+
'save_exc_value'
,
pyrex_prefix
+
'save_exc_tb'
)
api_name
=
pyrex_prefix
+
"capi__"
api_name
=
pyrex_prefix
+
"capi__"
h_guard_prefix
=
"__PYX_HAVE__"
h_guard_prefix
=
"__PYX_HAVE__"
...
...
Cython/Compiler/Nodes.py
View file @
7ef3e52c
...
@@ -23,7 +23,7 @@ from PyrexTypes import py_object_type, error_type, CFuncType
...
@@ -23,7 +23,7 @@ from PyrexTypes import py_object_type, error_type, CFuncType
from
Symtab
import
ModuleScope
,
LocalScope
,
ClosureScope
,
\
from
Symtab
import
ModuleScope
,
LocalScope
,
ClosureScope
,
\
StructOrUnionScope
,
PyClassScope
,
CClassScope
,
CppClassScope
StructOrUnionScope
,
PyClassScope
,
CClassScope
,
CppClassScope
from
Cython.Utils
import
open_new_file
,
replace_suffix
from
Cython.Utils
import
open_new_file
,
replace_suffix
from
Code
import
UtilityCode
from
Code
import
UtilityCode
,
ClosureTempAllocator
from
StringEncoding
import
EncodedString
,
escape_byte_string
,
split_string_literal
from
StringEncoding
import
EncodedString
,
escape_byte_string
,
split_string_literal
import
Options
import
Options
import
ControlFlow
import
ControlFlow
...
@@ -1177,6 +1177,8 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1177,6 +1177,8 @@ class FuncDefNode(StatNode, BlockNode):
assmt
=
None
assmt
=
None
needs_closure
=
False
needs_closure
=
False
needs_outer_scope
=
False
needs_outer_scope
=
False
is_generator
=
False
is_generator_body
=
False
modifiers
=
[]
modifiers
=
[]
def
analyse_default_values
(
self
,
env
):
def
analyse_default_values
(
self
,
env
):
...
@@ -1236,6 +1238,9 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1236,6 +1238,9 @@ class FuncDefNode(StatNode, BlockNode):
lenv
.
directives
=
env
.
directives
lenv
.
directives
=
env
.
directives
return
lenv
return
lenv
def
generate_function_body
(
self
,
env
,
code
):
self
.
body
.
generate_execution_code
(
code
)
def
generate_function_definitions
(
self
,
env
,
code
):
def
generate_function_definitions
(
self
,
env
,
code
):
import
Buffer
import
Buffer
...
@@ -1323,6 +1328,8 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1323,6 +1328,8 @@ class FuncDefNode(StatNode, BlockNode):
(
self
.
return_type
.
declaration_code
(
Naming
.
retval_cname
),
(
self
.
return_type
.
declaration_code
(
Naming
.
retval_cname
),
init
))
init
))
tempvardecl_code
=
code
.
insertion_point
()
tempvardecl_code
=
code
.
insertion_point
()
if
not
lenv
.
nogil
:
code
.
put_declare_refcount_context
()
self
.
generate_keyword_list
(
code
)
self
.
generate_keyword_list
(
code
)
if
profile
:
if
profile
:
code
.
put_trace_declarations
()
code
.
put_trace_declarations
()
...
@@ -1402,7 +1409,7 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1402,7 +1409,7 @@ class FuncDefNode(StatNode, BlockNode):
# -------------------------
# -------------------------
# ----- Function body -----
# ----- Function body -----
# -------------------------
# -------------------------
self
.
body
.
generate_execution_code
(
code
)
self
.
generate_function_body
(
env
,
code
)
# ----- Default return value
# ----- Default return value
code
.
putln
(
""
)
code
.
putln
(
""
)
...
@@ -1484,14 +1491,10 @@ class FuncDefNode(StatNode, BlockNode):
...
@@ -1484,14 +1491,10 @@ class FuncDefNode(StatNode, BlockNode):
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
used
and
not
entry
.
in_closure
:
if
entry
.
used
and
not
entry
.
in_closure
:
code
.
put_var_decref
(
entry
)
code
.
put_var_decref
(
entry
)
elif
entry
.
in_closure
and
self
.
needs_closure
:
code
.
put_giveref
(
entry
.
cname
)
# Decref any increfed args
# Decref any increfed args
for
entry
in
lenv
.
arg_entries
:
for
entry
in
lenv
.
arg_entries
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
type
.
is_pyobject
:
if
entry
.
in_closure
:
if
(
acquire_gil
or
entry
.
assignments
)
and
not
entry
.
in_closure
:
code
.
put_var_giveref
(
entry
)
elif
acquire_gil
or
entry
.
assignments
:
code
.
put_var_decref
(
entry
)
code
.
put_var_decref
(
entry
)
if
self
.
needs_closure
:
if
self
.
needs_closure
:
code
.
put_decref
(
Naming
.
cur_scope_cname
,
lenv
.
scope_class
.
type
)
code
.
put_decref
(
Naming
.
cur_scope_cname
,
lenv
.
scope_class
.
type
)
...
@@ -1920,6 +1923,7 @@ class DefNode(FuncDefNode):
...
@@ -1920,6 +1923,7 @@ class DefNode(FuncDefNode):
num_required_kw_args
=
0
num_required_kw_args
=
0
reqd_kw_flags_cname
=
"0"
reqd_kw_flags_cname
=
"0"
is_wrapper
=
0
is_wrapper
=
0
no_assignment_synthesis
=
0
decorators
=
None
decorators
=
None
return_type_annotation
=
None
return_type_annotation
=
None
entry
=
None
entry
=
None
...
@@ -2250,6 +2254,8 @@ class DefNode(FuncDefNode):
...
@@ -2250,6 +2254,8 @@ class DefNode(FuncDefNode):
self
.
synthesize_assignment_node
(
env
)
self
.
synthesize_assignment_node
(
env
)
def
needs_assignment_synthesis
(
self
,
env
,
code
=
None
):
def
needs_assignment_synthesis
(
self
,
env
,
code
=
None
):
if
self
.
no_assignment_synthesis
:
return
False
# Should enable for module level as well, that will require more testing...
# Should enable for module level as well, that will require more testing...
if
self
.
entry
.
is_anonymous
:
if
self
.
entry
.
is_anonymous
:
return
True
return
True
...
@@ -2353,8 +2359,8 @@ class DefNode(FuncDefNode):
...
@@ -2353,8 +2359,8 @@ class DefNode(FuncDefNode):
code
.
putln
(
"0};"
)
code
.
putln
(
"0};"
)
def
generate_argument_parsing_code
(
self
,
env
,
code
):
def
generate_argument_parsing_code
(
self
,
env
,
code
):
# Generate
PyArg_ParseTuple call for generic
# Generate
fast equivalent of PyArg_ParseTuple call for
#
arguments, if any.
#
generic arguments, if any, including args/kwargs
if
self
.
entry
.
signature
.
has_dummy_arg
and
not
self
.
self_in_stararg
:
if
self
.
entry
.
signature
.
has_dummy_arg
and
not
self
.
self_in_stararg
:
# get rid of unused argument warning
# get rid of unused argument warning
code
.
putln
(
"%s = %s;"
%
(
Naming
.
self_cname
,
Naming
.
self_cname
))
code
.
putln
(
"%s = %s;"
%
(
Naming
.
self_cname
,
Naming
.
self_cname
))
...
@@ -2431,14 +2437,24 @@ class DefNode(FuncDefNode):
...
@@ -2431,14 +2437,24 @@ class DefNode(FuncDefNode):
if
code
.
label_used
(
end_label
):
if
code
.
label_used
(
end_label
):
code
.
put_label
(
end_label
)
code
.
put_label
(
end_label
)
# fix refnanny view on closure variables here, instead of
# doing it separately for each arg parsing special case
if
self
.
star_arg
and
self
.
star_arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
self
.
star_arg
.
entry
)
if
self
.
starstar_arg
and
self
.
starstar_arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
self
.
starstar_arg
.
entry
)
for
arg
in
self
.
args
:
if
arg
.
type
.
is_pyobject
and
arg
.
entry
.
in_closure
:
code
.
put_var_giveref
(
arg
.
entry
)
def
generate_arg_assignment
(
self
,
arg
,
item
,
code
):
def
generate_arg_assignment
(
self
,
arg
,
item
,
code
):
if
arg
.
type
.
is_pyobject
:
if
arg
.
type
.
is_pyobject
:
if
arg
.
is_generic
:
if
arg
.
is_generic
:
item
=
PyrexTypes
.
typecast
(
arg
.
type
,
PyrexTypes
.
py_object_type
,
item
)
item
=
PyrexTypes
.
typecast
(
arg
.
type
,
PyrexTypes
.
py_object_type
,
item
)
entry
=
arg
.
entry
entry
=
arg
.
entry
code
.
putln
(
"%s = %s;"
%
(
entry
.
cname
,
item
))
if
entry
.
in_closure
:
if
entry
.
in_closure
:
code
.
put_var_incref
(
entry
)
code
.
put_incref
(
item
,
PyrexTypes
.
py_object_type
)
code
.
putln
(
"%s = %s;"
%
(
entry
.
cname
,
item
))
else
:
else
:
func
=
arg
.
type
.
from_py_function
func
=
arg
.
type
.
from_py_function
if
func
:
if
func
:
...
@@ -2657,19 +2673,18 @@ class DefNode(FuncDefNode):
...
@@ -2657,19 +2673,18 @@ class DefNode(FuncDefNode):
code
.
putln
(
'if (PyTuple_GET_SIZE(%s) > %d) {'
%
(
code
.
putln
(
'if (PyTuple_GET_SIZE(%s) > %d) {'
%
(
Naming
.
args_cname
,
Naming
.
args_cname
,
max_positional_args
))
max_positional_args
))
code
.
put
(
'%s = PyTuple_GetSlice(%s, %d, PyTuple_GET_SIZE(%s));
'
%
(
code
.
put
ln
(
'%s = PyTuple_GetSlice(%s, %d, PyTuple_GET_SIZE(%s));
'
%
(
self
.
star_arg
.
entry
.
cname
,
Naming
.
args_cname
,
self
.
star_arg
.
entry
.
cname
,
Naming
.
args_cname
,
max_positional_args
,
Naming
.
args_cname
))
max_positional_args
,
Naming
.
args_cname
))
code
.
put
_gotref
(
self
.
star_arg
.
entry
.
cname
)
code
.
put
ln
(
"if (unlikely(!%s)) {"
%
self
.
star_arg
.
entry
.
cname
)
if
self
.
starstar_arg
:
if
self
.
starstar_arg
:
code
.
putln
(
""
)
code
.
putln
(
"if (unlikely(!%s)) {"
%
self
.
star_arg
.
entry
.
cname
)
code
.
put_decref_clear
(
self
.
starstar_arg
.
entry
.
cname
,
py_object_type
)
code
.
put_decref_clear
(
self
.
starstar_arg
.
entry
.
cname
,
py_object_type
)
code
.
putln
(
'return %s;'
%
self
.
error_value
())
if
self
.
needs_closure
:
code
.
putln
(
'}'
)
code
.
put_decref
(
Naming
.
cur_scope_cname
,
self
.
local_scope
.
scope_class
.
type
)
else
:
code
.
put_finish_refcount_context
()
code
.
putln
(
"if (unlikely(!%s)) return %s;"
%
(
code
.
putln
(
'return %s;'
%
self
.
error_value
())
self
.
star_arg
.
entry
.
cname
,
self
.
error_value
()))
code
.
putln
(
'}'
)
code
.
put_gotref
(
self
.
star_arg
.
entry
.
cname
)
code
.
putln
(
'} else {'
)
code
.
putln
(
'} else {'
)
code
.
put
(
"%s = %s; "
%
(
self
.
star_arg
.
entry
.
cname
,
Naming
.
empty_tuple
))
code
.
put
(
"%s = %s; "
%
(
self
.
star_arg
.
entry
.
cname
,
Naming
.
empty_tuple
))
code
.
put_incref
(
Naming
.
empty_tuple
,
py_object_type
)
code
.
put_incref
(
Naming
.
empty_tuple
,
py_object_type
)
...
@@ -2839,9 +2854,9 @@ class DefNode(FuncDefNode):
...
@@ -2839,9 +2854,9 @@ class DefNode(FuncDefNode):
if
arg
.
needs_conversion
:
if
arg
.
needs_conversion
:
self
.
generate_arg_conversion
(
arg
,
code
)
self
.
generate_arg_conversion
(
arg
,
code
)
elif
arg
.
entry
.
in_closure
:
elif
arg
.
entry
.
in_closure
:
code
.
putln
(
'%s = %s;'
%
(
arg
.
entry
.
cname
,
arg
.
hdr_cname
))
if
arg
.
type
.
is_pyobject
:
if
arg
.
type
.
is_pyobject
:
code
.
put_var_incref
(
arg
.
entry
)
code
.
put_incref
(
arg
.
hdr_cname
,
py_object_type
)
code
.
putln
(
'%s = %s;'
%
(
arg
.
entry
.
cname
,
arg
.
hdr_cname
))
def
generate_arg_conversion
(
self
,
arg
,
code
):
def
generate_arg_conversion
(
self
,
arg
,
code
):
# Generate conversion code for one argument.
# Generate conversion code for one argument.
...
@@ -2914,6 +2929,146 @@ class DefNode(FuncDefNode):
...
@@ -2914,6 +2929,146 @@ class DefNode(FuncDefNode):
def
caller_will_check_exceptions
(
self
):
def
caller_will_check_exceptions
(
self
):
return
1
return
1
class
GeneratorDefNode
(
DefNode
):
# Generator DefNode.
#
# gbody GeneratorBodyDefNode
#
is_generator
=
True
needs_closure
=
True
child_attrs
=
DefNode
.
child_attrs
+
[
"gbody"
]
def
__init__
(
self
,
**
kwargs
):
# XXX: don't actually needs a body
kwargs
[
'body'
]
=
StatListNode
(
kwargs
[
'pos'
],
stats
=
[])
super
(
GeneratorDefNode
,
self
).
__init__
(
**
kwargs
)
def
analyse_declarations
(
self
,
env
):
super
(
GeneratorDefNode
,
self
).
analyse_declarations
(
env
)
self
.
gbody
.
local_scope
=
self
.
local_scope
self
.
gbody
.
analyse_declarations
(
env
)
def
generate_function_body
(
self
,
env
,
code
):
body_cname
=
self
.
gbody
.
entry
.
func_cname
generator_cname
=
'%s->%s'
%
(
Naming
.
cur_scope_cname
,
Naming
.
obj_base_cname
)
code
.
putln
(
'%s.resume_label = 0;'
%
generator_cname
)
code
.
putln
(
'%s.body = (__pyx_generator_body_t) %s;'
%
(
generator_cname
,
body_cname
))
code
.
put_giveref
(
Naming
.
cur_scope_cname
)
code
.
put_finish_refcount_context
()
code
.
putln
(
"return (PyObject *) %s;"
%
Naming
.
cur_scope_cname
);
def
generate_function_definitions
(
self
,
env
,
code
):
self
.
gbody
.
generate_function_header
(
code
,
proto
=
True
)
super
(
GeneratorDefNode
,
self
).
generate_function_definitions
(
env
,
code
)
self
.
gbody
.
generate_function_definitions
(
env
,
code
)
class
GeneratorBodyDefNode
(
DefNode
):
# Generator body DefNode.
#
is_generator_body
=
True
def
__init__
(
self
,
pos
=
None
,
name
=
None
,
body
=
None
):
super
(
GeneratorBodyDefNode
,
self
).
__init__
(
pos
=
pos
,
body
=
body
,
name
=
name
,
doc
=
None
,
args
=
[],
star_arg
=
None
,
starstar_arg
=
None
)
def
declare_generator_body
(
self
,
env
):
prefix
=
env
.
next_id
(
env
.
scope_prefix
)
name
=
env
.
next_id
(
'generator'
)
entry
=
env
.
declare_var
(
prefix
+
name
,
py_object_type
,
self
.
pos
,
visibility
=
'private'
)
entry
.
func_cname
=
Naming
.
genbody_prefix
+
prefix
+
name
entry
.
qualified_name
=
EncodedString
(
self
.
name
)
self
.
entry
=
entry
def
analyse_declarations
(
self
,
env
):
self
.
analyse_argument_types
(
env
)
self
.
declare_generator_body
(
env
)
def
generate_function_header
(
self
,
code
,
proto
=
False
):
header
=
"static PyObject *%s(%s, PyObject *%s)"
%
(
self
.
entry
.
func_cname
,
self
.
local_scope
.
scope_class
.
type
.
declaration_code
(
Naming
.
cur_scope_cname
),
Naming
.
sent_value_cname
)
if
proto
:
code
.
putln
(
'%s; /* proto */'
%
header
)
else
:
code
.
putln
(
'%s /* generator body */
\
n
{'
%
header
);
def
generate_function_definitions
(
self
,
env
,
code
):
lenv
=
self
.
local_scope
# Generate closure function definitions
self
.
body
.
generate_function_definitions
(
lenv
,
code
)
# Generate C code for header and body of function
code
.
enter_cfunc_scope
()
code
.
return_from_error_cleanup_label
=
code
.
new_label
()
# ----- Top-level constants used by this function
code
.
mark_pos
(
self
.
pos
)
self
.
generate_cached_builtins_decls
(
lenv
,
code
)
# ----- Function header
code
.
putln
(
""
)
self
.
generate_function_header
(
code
)
# ----- Local variables
code
.
putln
(
"PyObject *%s = NULL;"
%
Naming
.
retval_cname
)
tempvardecl_code
=
code
.
insertion_point
()
code
.
put_declare_refcount_context
()
code
.
put_setup_refcount_context
(
self
.
entry
.
name
)
# ----- Resume switch point.
code
.
funcstate
.
init_closure_temps
(
lenv
.
scope_class
.
type
.
scope
)
resume_code
=
code
.
insertion_point
()
first_run_label
=
code
.
new_label
(
'first_run'
)
code
.
use_label
(
first_run_label
)
code
.
put_label
(
first_run_label
)
code
.
putln
(
'%s'
%
(
code
.
error_goto_if_null
(
Naming
.
sent_value_cname
,
self
.
pos
)))
# ----- Function body
self
.
generate_function_body
(
env
,
code
)
code
.
putln
(
'PyErr_SetNone(PyExc_StopIteration); %s'
%
code
.
error_goto
(
self
.
pos
))
# ----- Error cleanup
if
code
.
error_label
in
code
.
labels_used
:
code
.
put_goto
(
code
.
return_label
)
code
.
put_label
(
code
.
error_label
)
for
cname
,
type
in
code
.
funcstate
.
all_managed_temps
():
code
.
put_xdecref
(
cname
,
type
)
code
.
putln
(
'__Pyx_AddTraceback("%s");'
%
self
.
entry
.
qualified_name
)
# ----- Non-error return cleanup
code
.
put_label
(
code
.
return_label
)
code
.
put_xdecref
(
Naming
.
retval_cname
,
py_object_type
)
code
.
putln
(
'%s->%s.resume_label = -1;'
%
(
Naming
.
cur_scope_cname
,
Naming
.
obj_base_cname
))
code
.
put_finish_refcount_context
()
code
.
putln
(
'return NULL;'
);
code
.
putln
(
"}"
)
# ----- Go back and insert temp variable declarations
tempvardecl_code
.
put_temp_declarations
(
code
.
funcstate
)
# ----- Generator resume code
resume_code
.
putln
(
"switch (%s->%s.resume_label) {"
%
(
Naming
.
cur_scope_cname
,
Naming
.
obj_base_cname
));
resume_code
.
putln
(
"case 0: goto %s;"
%
first_run_label
)
from
ParseTreeTransforms
import
YieldNodeCollector
collector
=
YieldNodeCollector
()
collector
.
visitchildren
(
self
)
for
yield_expr
in
collector
.
yields
:
resume_code
.
putln
(
"case %d: goto %s;"
%
(
yield_expr
.
label_num
,
yield_expr
.
label_name
));
resume_code
.
putln
(
"default: /* CPython raises the right error here */"
);
resume_code
.
put_finish_refcount_context
()
resume_code
.
putln
(
"return NULL;"
);
resume_code
.
putln
(
"}"
);
code
.
exit_cfunc_scope
()
class
OverrideCheckNode
(
StatNode
):
class
OverrideCheckNode
(
StatNode
):
# A Node for dispatching to the def method if it
# A Node for dispatching to the def method if it
# is overriden.
# is overriden.
...
@@ -3352,6 +3507,24 @@ class GlobalNode(StatNode):
...
@@ -3352,6 +3507,24 @@ class GlobalNode(StatNode):
pass
pass
class
NonlocalNode
(
StatNode
):
# Nonlocal variable declaration via the 'nonlocal' keyword.
#
# names [string]
child_attrs
=
[]
def
analyse_declarations
(
self
,
env
):
for
name
in
self
.
names
:
env
.
declare_nonlocal
(
name
,
self
.
pos
)
def
analyse_expressions
(
self
,
env
):
pass
def
generate_execution_code
(
self
,
code
):
pass
class
ExprStatNode
(
StatNode
):
class
ExprStatNode
(
StatNode
):
# Expression used as a statement.
# Expression used as a statement.
#
#
...
@@ -3376,6 +3549,7 @@ class ExprStatNode(StatNode):
...
@@ -3376,6 +3549,7 @@ class ExprStatNode(StatNode):
self
.
__class__
=
PassStatNode
self
.
__class__
=
PassStatNode
def
analyse_expressions
(
self
,
env
):
def
analyse_expressions
(
self
,
env
):
self
.
expr
.
result_is_used
=
False
# hint that .result() may safely be left empty
self
.
expr
.
analyse_expressions
(
env
)
self
.
expr
.
analyse_expressions
(
env
)
def
nogil_check
(
self
,
env
):
def
nogil_check
(
self
,
env
):
...
@@ -4705,12 +4879,12 @@ class TryExceptStatNode(StatNode):
...
@@ -4705,12 +4879,12 @@ class TryExceptStatNode(StatNode):
try_continue_label
=
code
.
new_label
(
'try_continue'
)
try_continue_label
=
code
.
new_label
(
'try_continue'
)
try_end_label
=
code
.
new_label
(
'try_end'
)
try_end_label
=
code
.
new_label
(
'try_end'
)
exc_save_vars
=
[
code
.
funcstate
.
allocate_temp
(
py_object_type
,
False
)
for
i
in
xrange
(
3
)]
code
.
putln
(
"{"
)
code
.
putln
(
"{"
)
code
.
putln
(
"PyObject %s;"
%
', '
.
join
([
'*%s'
%
var
for
var
in
Naming
.
exc_save_vars
]))
code
.
putln
(
"__Pyx_ExceptionSave(%s);"
%
code
.
putln
(
"__Pyx_ExceptionSave(%s);"
%
', '
.
join
([
'&%s'
%
var
for
var
in
Naming
.
exc_save_vars
]))
', '
.
join
([
'&%s'
%
var
for
var
in
exc_save_vars
]))
for
var
in
Naming
.
exc_save_vars
:
for
var
in
exc_save_vars
:
code
.
put_xgotref
(
var
)
code
.
put_xgotref
(
var
)
code
.
putln
(
code
.
putln
(
"/*try:*/ {"
)
"/*try:*/ {"
)
...
@@ -4729,14 +4903,15 @@ class TryExceptStatNode(StatNode):
...
@@ -4729,14 +4903,15 @@ class TryExceptStatNode(StatNode):
self
.
else_clause
.
generate_execution_code
(
code
)
self
.
else_clause
.
generate_execution_code
(
code
)
code
.
putln
(
code
.
putln
(
"}"
)
"}"
)
for
var
in
Naming
.
exc_save_vars
:
for
var
in
exc_save_vars
:
code
.
put_xdecref_clear
(
var
,
py_object_type
)
code
.
put_xdecref_clear
(
var
,
py_object_type
)
code
.
put_goto
(
try_end_label
)
code
.
put_goto
(
try_end_label
)
if
code
.
label_used
(
try_return_label
):
if
code
.
label_used
(
try_return_label
):
code
.
put_label
(
try_return_label
)
code
.
put_label
(
try_return_label
)
for
var
in
Naming
.
exc_save_vars
:
code
.
put_xgiveref
(
var
)
for
var
in
exc_save_vars
:
code
.
put_xgiveref
(
var
)
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
', '
.
join
(
Naming
.
exc_save_vars
))
', '
.
join
(
exc_save_vars
))
code
.
put_goto
(
old_return_label
)
code
.
put_goto
(
old_return_label
)
code
.
put_label
(
our_error_label
)
code
.
put_label
(
our_error_label
)
for
temp_name
,
type
in
temps_to_clean_up
:
for
temp_name
,
type
in
temps_to_clean_up
:
...
@@ -4748,9 +4923,10 @@ class TryExceptStatNode(StatNode):
...
@@ -4748,9 +4923,10 @@ class TryExceptStatNode(StatNode):
if
error_label_used
or
not
self
.
has_default_clause
:
if
error_label_used
or
not
self
.
has_default_clause
:
if
error_label_used
:
if
error_label_used
:
code
.
put_label
(
except_error_label
)
code
.
put_label
(
except_error_label
)
for
var
in
Naming
.
exc_save_vars
:
code
.
put_xgiveref
(
var
)
for
var
in
exc_save_vars
:
code
.
put_xgiveref
(
var
)
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
', '
.
join
(
Naming
.
exc_save_vars
))
', '
.
join
(
exc_save_vars
))
code
.
put_goto
(
old_error_label
)
code
.
put_goto
(
old_error_label
)
for
exit_label
,
old_label
in
zip
(
for
exit_label
,
old_label
in
zip
(
...
@@ -4759,19 +4935,24 @@ class TryExceptStatNode(StatNode):
...
@@ -4759,19 +4935,24 @@ class TryExceptStatNode(StatNode):
if
code
.
label_used
(
exit_label
):
if
code
.
label_used
(
exit_label
):
code
.
put_label
(
exit_label
)
code
.
put_label
(
exit_label
)
for
var
in
Naming
.
exc_save_vars
:
code
.
put_xgiveref
(
var
)
for
var
in
exc_save_vars
:
code
.
put_xgiveref
(
var
)
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
', '
.
join
(
Naming
.
exc_save_vars
))
', '
.
join
(
exc_save_vars
))
code
.
put_goto
(
old_label
)
code
.
put_goto
(
old_label
)
if
code
.
label_used
(
except_end_label
):
if
code
.
label_used
(
except_end_label
):
code
.
put_label
(
except_end_label
)
code
.
put_label
(
except_end_label
)
for
var
in
Naming
.
exc_save_vars
:
code
.
put_xgiveref
(
var
)
for
var
in
exc_save_vars
:
code
.
put_xgiveref
(
var
)
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
code
.
putln
(
"__Pyx_ExceptionReset(%s);"
%
', '
.
join
(
Naming
.
exc_save_vars
))
', '
.
join
(
exc_save_vars
))
code
.
put_label
(
try_end_label
)
code
.
put_label
(
try_end_label
)
code
.
putln
(
"}"
)
code
.
putln
(
"}"
)
for
cname
in
exc_save_vars
:
code
.
funcstate
.
release_temp
(
cname
)
code
.
return_label
=
old_return_label
code
.
return_label
=
old_return_label
code
.
break_label
=
old_break_label
code
.
break_label
=
old_break_label
code
.
continue_label
=
old_continue_label
code
.
continue_label
=
old_continue_label
...
...
Cython/Compiler/Optimize.py
View file @
7ef3e52c
...
@@ -1170,11 +1170,12 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
...
@@ -1170,11 +1170,12 @@ class EarlyReplaceBuiltinCalls(Visitor.EnvTransform):
self
.
yield_nodes
=
[]
self
.
yield_nodes
=
[]
visit_Node
=
Visitor
.
TreeVisitor
.
visitchildren
visit_Node
=
Visitor
.
TreeVisitor
.
visitchildren
def
visit_YieldExprNode
(
self
,
node
):
# XXX: disable inlining while it's not back supported
def
__visit_YieldExprNode
(
self
,
node
):
self
.
yield_nodes
.
append
(
node
)
self
.
yield_nodes
.
append
(
node
)
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
def
visit_ExprStatNode
(
self
,
node
):
def
__
visit_ExprStatNode
(
self
,
node
):
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
if
node
.
expr
in
self
.
yield_nodes
:
if
node
.
expr
in
self
.
yield_nodes
:
self
.
yield_stat_nodes
[
node
.
expr
]
=
node
self
.
yield_stat_nodes
[
node
.
expr
]
=
node
...
...
Cython/Compiler/ParseTreeTransforms.pxd
View file @
7ef3e52c
...
@@ -19,6 +19,7 @@ cdef class NormalizeTree(CythonTransform):
...
@@ -19,6 +19,7 @@ cdef class NormalizeTree(CythonTransform):
cdef
class
PostParse
(
ScopeTrackingTransform
):
cdef
class
PostParse
(
ScopeTrackingTransform
):
cdef
dict
specialattribute_handlers
cdef
dict
specialattribute_handlers
cdef
size_t
lambda_counter
cdef
size_t
lambda_counter
cdef
size_t
genexpr_counter
cdef
_visit_assignment_node
(
self
,
node
,
list
expr_list
)
cdef
_visit_assignment_node
(
self
,
node
,
list
expr_list
)
...
@@ -45,6 +46,11 @@ cdef class AlignFunctionDefinitions(CythonTransform):
...
@@ -45,6 +46,11 @@ cdef class AlignFunctionDefinitions(CythonTransform):
cdef
dict
directives
cdef
dict
directives
cdef
scope
cdef
scope
cdef
class
YieldNodeCollector
(
TreeVisitor
):
cdef
public
list
yields
cdef
public
list
returns
cdef
public
bint
has_return_value
cdef
class
MarkClosureVisitor
(
CythonTransform
):
cdef
class
MarkClosureVisitor
(
CythonTransform
):
cdef
bint
needs_closure
cdef
bint
needs_closure
...
@@ -52,6 +58,7 @@ cdef class CreateClosureClasses(CythonTransform):
...
@@ -52,6 +58,7 @@ cdef class CreateClosureClasses(CythonTransform):
cdef
list
path
cdef
list
path
cdef
bint
in_lambda
cdef
bint
in_lambda
cdef
module_scope
cdef
module_scope
cdef
generator_class
cdef
class
GilCheck
(
VisitorTransform
):
cdef
class
GilCheck
(
VisitorTransform
):
cdef
list
env_stack
cdef
list
env_stack
...
...
Cython/Compiler/ParseTreeTransforms.py
View file @
7ef3e52c
...
@@ -182,6 +182,7 @@ class PostParse(ScopeTrackingTransform):
...
@@ -182,6 +182,7 @@ class PostParse(ScopeTrackingTransform):
def
visit_ModuleNode
(
self
,
node
):
def
visit_ModuleNode
(
self
,
node
):
self
.
lambda_counter
=
1
self
.
lambda_counter
=
1
self
.
genexpr_counter
=
1
return
super
(
PostParse
,
self
).
visit_ModuleNode
(
node
)
return
super
(
PostParse
,
self
).
visit_ModuleNode
(
node
)
def
visit_LambdaNode
(
self
,
node
):
def
visit_LambdaNode
(
self
,
node
):
...
@@ -189,14 +190,34 @@ class PostParse(ScopeTrackingTransform):
...
@@ -189,14 +190,34 @@ class PostParse(ScopeTrackingTransform):
lambda_id
=
self
.
lambda_counter
lambda_id
=
self
.
lambda_counter
self
.
lambda_counter
+=
1
self
.
lambda_counter
+=
1
node
.
lambda_name
=
EncodedString
(
u'lambda%d'
%
lambda_id
)
node
.
lambda_name
=
EncodedString
(
u'lambda%d'
%
lambda_id
)
collector
=
YieldNodeCollector
()
body
=
Nodes
.
ReturnStatNode
(
collector
.
visitchildren
(
node
.
result_expr
)
node
.
result_expr
.
pos
,
value
=
node
.
result_expr
)
if
collector
.
yields
or
isinstance
(
node
.
result_expr
,
ExprNodes
.
YieldExprNode
):
body
=
ExprNodes
.
YieldExprNode
(
node
.
result_expr
.
pos
,
arg
=
node
.
result_expr
)
body
=
Nodes
.
ExprStatNode
(
node
.
result_expr
.
pos
,
expr
=
body
)
else
:
body
=
Nodes
.
ReturnStatNode
(
node
.
result_expr
.
pos
,
value
=
node
.
result_expr
)
node
.
def_node
=
Nodes
.
DefNode
(
node
.
def_node
=
Nodes
.
DefNode
(
node
.
pos
,
name
=
node
.
name
,
lambda_name
=
node
.
lambda_name
,
node
.
pos
,
name
=
node
.
name
,
lambda_name
=
node
.
lambda_name
,
args
=
node
.
args
,
star_arg
=
node
.
star_arg
,
args
=
node
.
args
,
star_arg
=
node
.
star_arg
,
starstar_arg
=
node
.
starstar_arg
,
starstar_arg
=
node
.
starstar_arg
,
body
=
body
)
body
=
body
,
doc
=
None
)
self
.
visitchildren
(
node
)
return
node
def
visit_GeneratorExpressionNode
(
self
,
node
):
# unpack a generator expression into the corresponding DefNode
genexpr_id
=
self
.
genexpr_counter
self
.
genexpr_counter
+=
1
node
.
genexpr_name
=
EncodedString
(
u'genexpr%d'
%
genexpr_id
)
node
.
def_node
=
Nodes
.
DefNode
(
node
.
pos
,
name
=
node
.
name
,
doc
=
None
,
args
=
[],
star_arg
=
None
,
starstar_arg
=
None
,
body
=
node
.
loop
)
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
return
node
return
node
...
@@ -1408,6 +1429,42 @@ class AlignFunctionDefinitions(CythonTransform):
...
@@ -1408,6 +1429,42 @@ class AlignFunctionDefinitions(CythonTransform):
return
node
return
node
class
YieldNodeCollector
(
TreeVisitor
):
def
__init__
(
self
):
super
(
YieldNodeCollector
,
self
).
__init__
()
self
.
yields
=
[]
self
.
returns
=
[]
self
.
has_return_value
=
False
def
visit_Node
(
self
,
node
):
return
self
.
visitchildren
(
node
)
def
visit_YieldExprNode
(
self
,
node
):
if
self
.
has_return_value
:
error
(
node
.
pos
,
"'yield' outside function"
)
self
.
yields
.
append
(
node
)
self
.
visitchildren
(
node
)
def
visit_ReturnStatNode
(
self
,
node
):
if
node
.
value
:
self
.
has_return_value
=
True
if
self
.
yields
:
error
(
node
.
pos
,
"'return' with argument inside generator"
)
self
.
returns
.
append
(
node
)
def
visit_ClassDefNode
(
self
,
node
):
pass
def
visit_DefNode
(
self
,
node
):
pass
def
visit_LambdaNode
(
self
,
node
):
pass
def
visit_GeneratorExpressionNode
(
self
,
node
):
pass
class
MarkClosureVisitor
(
CythonTransform
):
class
MarkClosureVisitor
(
CythonTransform
):
def
visit_ModuleNode
(
self
,
node
):
def
visit_ModuleNode
(
self
,
node
):
...
@@ -1420,6 +1477,27 @@ class MarkClosureVisitor(CythonTransform):
...
@@ -1420,6 +1477,27 @@ class MarkClosureVisitor(CythonTransform):
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
node
.
needs_closure
=
self
.
needs_closure
node
.
needs_closure
=
self
.
needs_closure
self
.
needs_closure
=
True
self
.
needs_closure
=
True
collector
=
YieldNodeCollector
()
collector
.
visitchildren
(
node
)
if
collector
.
yields
:
for
i
,
yield_expr
in
enumerate
(
collector
.
yields
):
yield_expr
.
label_num
=
i
+
1
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
return
node
def
visit_CFuncDefNode
(
self
,
node
):
def
visit_CFuncDefNode
(
self
,
node
):
...
@@ -1440,7 +1518,6 @@ class MarkClosureVisitor(CythonTransform):
...
@@ -1440,7 +1518,6 @@ class MarkClosureVisitor(CythonTransform):
self
.
needs_closure
=
True
self
.
needs_closure
=
True
return
node
return
node
class
CreateClosureClasses
(
CythonTransform
):
class
CreateClosureClasses
(
CythonTransform
):
# Output closure classes in module scope for all functions
# Output closure classes in module scope for all functions
# that really need it.
# that really need it.
...
@@ -1449,24 +1526,78 @@ class CreateClosureClasses(CythonTransform):
...
@@ -1449,24 +1526,78 @@ class CreateClosureClasses(CythonTransform):
super
(
CreateClosureClasses
,
self
).
__init__
(
context
)
super
(
CreateClosureClasses
,
self
).
__init__
(
context
)
self
.
path
=
[]
self
.
path
=
[]
self
.
in_lambda
=
False
self
.
in_lambda
=
False
self
.
generator_class
=
None
def
visit_ModuleNode
(
self
,
node
):
def
visit_ModuleNode
(
self
,
node
):
self
.
module_scope
=
node
.
scope
self
.
module_scope
=
node
.
scope
self
.
visitchildren
(
node
)
self
.
visitchildren
(
node
)
return
node
return
node
def
get_scope_use
(
self
,
node
):
def
create_generator_class
(
self
,
target_module_scope
,
pos
):
if
self
.
generator_class
:
return
self
.
generator_class
# XXX: make generator class creation cleaner
entry
=
target_module_scope
.
declare_c_class
(
name
=
'__pyx_Generator'
,
objstruct_cname
=
'__pyx_Generator_object'
,
typeobj_cname
=
'__pyx_Generator_type'
,
pos
=
pos
,
defining
=
True
,
implementing
=
True
)
klass
=
entry
.
type
.
scope
klass
.
is_internal
=
True
klass
.
directives
=
{
'final'
:
True
}
body_type
=
PyrexTypes
.
create_typedef_type
(
'generator_body'
,
PyrexTypes
.
c_void_ptr_type
,
'__pyx_generator_body_t'
)
klass
.
declare_var
(
pos
=
pos
,
name
=
'body'
,
cname
=
'body'
,
type
=
body_type
,
is_cdef
=
True
)
klass
.
declare_var
(
pos
=
pos
,
name
=
'is_running'
,
cname
=
'is_running'
,
type
=
PyrexTypes
.
c_int_type
,
is_cdef
=
True
)
klass
.
declare_var
(
pos
=
pos
,
name
=
'resume_label'
,
cname
=
'resume_label'
,
type
=
PyrexTypes
.
c_int_type
,
is_cdef
=
True
)
import
TypeSlots
e
=
klass
.
declare_pyfunction
(
'send'
,
pos
)
e
.
func_cname
=
'__Pyx_Generator_Send'
e
.
signature
=
TypeSlots
.
binaryfunc
e
=
klass
.
declare_pyfunction
(
'close'
,
pos
)
e
.
func_cname
=
'__Pyx_Generator_Close'
e
.
signature
=
TypeSlots
.
unaryfunc
e
=
klass
.
declare_pyfunction
(
'throw'
,
pos
)
e
.
func_cname
=
'__Pyx_Generator_Throw'
e
.
signature
=
TypeSlots
.
pyfunction_signature
e
=
klass
.
declare_var
(
'__iter__'
,
PyrexTypes
.
py_object_type
,
pos
,
visibility
=
'public'
)
e
.
func_cname
=
'PyObject_SelfIter'
e
=
klass
.
declare_var
(
'__next__'
,
PyrexTypes
.
py_object_type
,
pos
,
visibility
=
'public'
)
e
.
func_cname
=
'__Pyx_Generator_Next'
self
.
generator_class
=
entry
.
type
return
self
.
generator_class
def
find_entries_used_in_closures
(
self
,
node
):
from_closure
=
[]
from_closure
=
[]
in_closure
=
[]
in_closure
=
[]
for
name
,
entry
in
node
.
local_scope
.
entries
.
items
():
for
name
,
entry
in
node
.
local_scope
.
entries
.
items
():
if
entry
.
from_closure
:
if
entry
.
from_closure
:
from_closure
.
append
((
name
,
entry
))
from_closure
.
append
((
name
,
entry
))
elif
entry
.
in_closure
and
not
entry
.
from_closure
:
elif
entry
.
in_closure
:
in_closure
.
append
((
name
,
entry
))
in_closure
.
append
((
name
,
entry
))
return
from_closure
,
in_closure
return
from_closure
,
in_closure
def
create_class_from_scope
(
self
,
node
,
target_module_scope
,
inner_node
=
None
):
def
create_class_from_scope
(
self
,
node
,
target_module_scope
,
inner_node
=
None
):
from_closure
,
in_closure
=
self
.
get_scope_use
(
node
)
# skip generator body
if
node
.
is_generator_body
:
return
# move local variables into closure
if
node
.
is_generator
:
for
entry
in
node
.
local_scope
.
entries
.
values
():
if
not
entry
.
from_closure
:
entry
.
in_closure
=
True
from_closure
,
in_closure
=
self
.
find_entries_used_in_closures
(
node
)
in_closure
.
sort
()
in_closure
.
sort
()
# Now from the begining
# Now from the begining
...
@@ -1485,8 +1616,11 @@ class CreateClosureClasses(CythonTransform):
...
@@ -1485,8 +1616,11 @@ class CreateClosureClasses(CythonTransform):
inner_node
=
node
.
assmt
.
rhs
inner_node
=
node
.
assmt
.
rhs
inner_node
.
needs_self_code
=
False
inner_node
.
needs_self_code
=
False
node
.
needs_outer_scope
=
False
node
.
needs_outer_scope
=
False
# Simple cases
if
not
in_closure
and
not
from_closure
:
base_type
=
None
if
node
.
is_generator
:
base_type
=
self
.
create_generator_class
(
target_module_scope
,
node
.
pos
)
elif
not
in_closure
and
not
from_closure
:
return
return
elif
not
in_closure
:
elif
not
in_closure
:
func_scope
.
is_passthrough
=
True
func_scope
.
is_passthrough
=
True
...
@@ -1496,8 +1630,10 @@ class CreateClosureClasses(CythonTransform):
...
@@ -1496,8 +1630,10 @@ class CreateClosureClasses(CythonTransform):
as_name
=
'%s_%s'
%
(
target_module_scope
.
next_id
(
Naming
.
closure_class_prefix
),
node
.
entry
.
cname
)
as_name
=
'%s_%s'
%
(
target_module_scope
.
next_id
(
Naming
.
closure_class_prefix
),
node
.
entry
.
cname
)
entry
=
target_module_scope
.
declare_c_class
(
name
=
as_name
,
entry
=
target_module_scope
.
declare_c_class
(
pos
=
node
.
pos
,
defining
=
True
,
implementing
=
True
)
name
=
as_name
,
pos
=
node
.
pos
,
defining
=
True
,
implementing
=
True
,
base_type
=
base_type
)
func_scope
.
scope_class
=
entry
func_scope
.
scope_class
=
entry
class_scope
=
entry
.
type
.
scope
class_scope
=
entry
.
type
.
scope
class_scope
.
is_internal
=
True
class_scope
.
is_internal
=
True
...
@@ -1512,11 +1648,13 @@ class CreateClosureClasses(CythonTransform):
...
@@ -1512,11 +1648,13 @@ class CreateClosureClasses(CythonTransform):
is_cdef
=
True
)
is_cdef
=
True
)
node
.
needs_outer_scope
=
True
node
.
needs_outer_scope
=
True
for
name
,
entry
in
in_closure
:
for
name
,
entry
in
in_closure
:
class_scope
.
declare_var
(
pos
=
entry
.
pos
,
cl
osure_entry
=
cl
ass_scope
.
declare_var
(
pos
=
entry
.
pos
,
name
=
entry
.
name
,
name
=
entry
.
name
,
cname
=
entry
.
cname
,
cname
=
entry
.
cname
,
type
=
entry
.
type
,
type
=
entry
.
type
,
is_cdef
=
True
)
is_cdef
=
True
)
if
entry
.
is_declared_generic
:
closure_entry
.
is_declared_generic
=
1
node
.
needs_closure
=
True
node
.
needs_closure
=
True
# Do it here because other classes are already checked
# Do it here because other classes are already checked
target_module_scope
.
check_c_class
(
func_scope
.
scope_class
)
target_module_scope
.
check_c_class
(
func_scope
.
scope_class
)
...
...
Cython/Compiler/Parsing.pxd
View file @
7ef3e52c
...
@@ -84,6 +84,7 @@ cdef p_genexp(PyrexScanner s, expr)
...
@@ -84,6 +84,7 @@ cdef p_genexp(PyrexScanner s, expr)
#-------------------------------------------------------
#-------------------------------------------------------
cdef
p_global_statement
(
PyrexScanner
s
)
cdef
p_global_statement
(
PyrexScanner
s
)
cdef
p_nonlocal_statement
(
PyrexScanner
s
)
cdef
p_expression_or_assignment
(
PyrexScanner
s
)
cdef
p_expression_or_assignment
(
PyrexScanner
s
)
cdef
p_print_statement
(
PyrexScanner
s
)
cdef
p_print_statement
(
PyrexScanner
s
)
cdef
p_exec_statement
(
PyrexScanner
s
)
cdef
p_exec_statement
(
PyrexScanner
s
)
...
...
Cython/Compiler/Parsing.py
View file @
7ef3e52c
...
@@ -1045,6 +1045,12 @@ def p_global_statement(s):
...
@@ -1045,6 +1045,12 @@ def p_global_statement(s):
names
=
p_ident_list
(
s
)
names
=
p_ident_list
(
s
)
return
Nodes
.
GlobalNode
(
pos
,
names
=
names
)
return
Nodes
.
GlobalNode
(
pos
,
names
=
names
)
def
p_nonlocal_statement
(
s
):
pos
=
s
.
position
()
s
.
next
()
names
=
p_ident_list
(
s
)
return
Nodes
.
NonlocalNode
(
pos
,
names
=
names
)
def
p_expression_or_assignment
(
s
):
def
p_expression_or_assignment
(
s
):
expr_list
=
[
p_testlist_star_expr
(
s
)]
expr_list
=
[
p_testlist_star_expr
(
s
)]
while
s
.
sy
==
'='
:
while
s
.
sy
==
'='
:
...
@@ -1598,6 +1604,8 @@ def p_simple_statement(s, first_statement = 0):
...
@@ -1598,6 +1604,8 @@ def p_simple_statement(s, first_statement = 0):
#print "p_simple_statement:", s.sy, s.systring ###
#print "p_simple_statement:", s.sy, s.systring ###
if
s
.
sy
==
'global'
:
if
s
.
sy
==
'global'
:
node
=
p_global_statement
(
s
)
node
=
p_global_statement
(
s
)
elif
s
.
sy
==
'nonlocal'
:
node
=
p_nonlocal_statement
(
s
)
elif
s
.
sy
==
'print'
:
elif
s
.
sy
==
'print'
:
node
=
p_print_statement
(
s
)
node
=
p_print_statement
(
s
)
elif
s
.
sy
==
'exec'
:
elif
s
.
sy
==
'exec'
:
...
...
Cython/Compiler/Scanning.py
View file @
7ef3e52c
...
@@ -36,7 +36,7 @@ def get_lexicon():
...
@@ -36,7 +36,7 @@ def get_lexicon():
#------------------------------------------------------------------
#------------------------------------------------------------------
py_reserved_words
=
[
py_reserved_words
=
[
"global"
,
"def"
,
"class"
,
"print"
,
"del"
,
"pass"
,
"break"
,
"global"
,
"
nonlocal"
,
"
def"
,
"class"
,
"print"
,
"del"
,
"pass"
,
"break"
,
"continue"
,
"return"
,
"raise"
,
"import"
,
"exec"
,
"try"
,
"continue"
,
"return"
,
"raise"
,
"import"
,
"exec"
,
"try"
,
"except"
,
"finally"
,
"while"
,
"if"
,
"elif"
,
"else"
,
"for"
,
"except"
,
"finally"
,
"while"
,
"if"
,
"elif"
,
"else"
,
"for"
,
"in"
,
"assert"
,
"and"
,
"or"
,
"not"
,
"is"
,
"in"
,
"lambda"
,
"in"
,
"assert"
,
"and"
,
"or"
,
"not"
,
"is"
,
"in"
,
"lambda"
,
...
...
Cython/Compiler/Symtab.py
View file @
7ef3e52c
...
@@ -1309,6 +1309,16 @@ class LocalScope(Scope):
...
@@ -1309,6 +1309,16 @@ class LocalScope(Scope):
entry
=
self
.
global_scope
().
lookup_target
(
name
)
entry
=
self
.
global_scope
().
lookup_target
(
name
)
self
.
entries
[
name
]
=
entry
self
.
entries
[
name
]
=
entry
def
declare_nonlocal
(
self
,
name
,
pos
):
# Pull entry from outer scope into local scope
orig_entry
=
self
.
lookup_here
(
name
)
if
orig_entry
and
orig_entry
.
scope
is
self
and
not
orig_entry
.
from_closure
:
error
(
pos
,
"'%s' redeclared as nonlocal"
%
name
)
else
:
entry
=
self
.
lookup
(
name
)
if
entry
is
None
or
not
entry
.
from_closure
:
error
(
pos
,
"no binding for nonlocal '%s' found"
%
name
)
def
lookup
(
self
,
name
):
def
lookup
(
self
,
name
):
# Look up name in this scope or an enclosing one.
# Look up name in this scope or an enclosing one.
# Return None if not found.
# Return None if not found.
...
@@ -1326,6 +1336,7 @@ class LocalScope(Scope):
...
@@ -1326,6 +1336,7 @@ class LocalScope(Scope):
inner_entry
.
is_variable
=
True
inner_entry
.
is_variable
=
True
inner_entry
.
outer_entry
=
entry
inner_entry
.
outer_entry
=
entry
inner_entry
.
from_closure
=
True
inner_entry
.
from_closure
=
True
inner_entry
.
is_declared_generic
=
entry
.
is_declared_generic
self
.
entries
[
name
]
=
inner_entry
self
.
entries
[
name
]
=
inner_entry
return
inner_entry
return
inner_entry
return
entry
return
entry
...
@@ -1479,6 +1490,20 @@ class PyClassScope(ClassScope):
...
@@ -1479,6 +1490,20 @@ class PyClassScope(ClassScope):
entry
.
is_pyclass_attr
=
1
entry
.
is_pyclass_attr
=
1
return
entry
return
entry
def
declare_nonlocal
(
self
,
name
,
pos
):
# Pull entry from outer scope into local scope
orig_entry
=
self
.
lookup_here
(
name
)
if
orig_entry
and
orig_entry
.
scope
is
self
and
not
orig_entry
.
from_closure
:
error
(
pos
,
"'%s' redeclared as nonlocal"
%
name
)
else
:
entry
=
self
.
lookup
(
name
)
if
entry
is
None
:
error
(
pos
,
"no binding for nonlocal '%s' found"
%
name
)
else
:
# FIXME: this works, but it's unclear if it's the
# right thing to do
self
.
entries
[
name
]
=
entry
def
add_default_value
(
self
,
type
):
def
add_default_value
(
self
,
type
):
return
self
.
outer_scope
.
add_default_value
(
type
)
return
self
.
outer_scope
.
add_default_value
(
type
)
...
...
Cython/Compiler/TypeInference.py
View file @
7ef3e52c
...
@@ -221,7 +221,8 @@ class SimpleAssignmentTypeInferer(object):
...
@@ -221,7 +221,8 @@ class SimpleAssignmentTypeInferer(object):
# TODO: Implement a real type inference algorithm.
# TODO: Implement a real type inference algorithm.
# (Something more powerful than just extending this one...)
# (Something more powerful than just extending this one...)
def
infer_types
(
self
,
scope
):
def
infer_types
(
self
,
scope
):
enabled
=
not
scope
.
is_closure_scope
and
scope
.
directives
[
'infer_types'
]
closure_or_inner
=
scope
.
is_closure_scope
or
(
scope
.
outer_scope
and
scope
.
outer_scope
.
is_closure_scope
)
enabled
=
not
closure_or_inner
and
scope
.
directives
[
'infer_types'
]
verbose
=
scope
.
directives
[
'infer_types.verbose'
]
verbose
=
scope
.
directives
[
'infer_types.verbose'
]
if
enabled
==
True
:
if
enabled
==
True
:
spanning_type
=
aggressive_spanning_type
spanning_type
=
aggressive_spanning_type
...
...
tests/bugs.txt
View file @
7ef3e52c
...
@@ -10,7 +10,6 @@ cfunc_call_tuple_args_T408
...
@@ -10,7 +10,6 @@ cfunc_call_tuple_args_T408
compile.cpp_operators
compile.cpp_operators
cpp_templated_ctypedef
cpp_templated_ctypedef
cpp_structs
cpp_structs
genexpr_T491
with_statement_module_level_T536
with_statement_module_level_T536
function_as_method_T494
function_as_method_T494
closure_inside_cdef_T554
closure_inside_cdef_T554
...
@@ -19,6 +18,7 @@ genexpr_iterable_lookup_T600
...
@@ -19,6 +18,7 @@ genexpr_iterable_lookup_T600
for_from_pyvar_loop_T601
for_from_pyvar_loop_T601
decorators_T593
decorators_T593
temp_sideeffects_T654
temp_sideeffects_T654
generator_type_inference
# CPython regression tests that don't current work:
# CPython regression tests that don't current work:
pyregr.test_threadsignals
pyregr.test_threadsignals
...
...
tests/errors/e_generators.pyx
0 → 100644
View file @
7ef3e52c
def
foo
():
yield
return
0
def
bar
(
a
):
return
0
yield
yield
class
Foo
:
yield
_ERRORS
=
u"""
3:4: 'return' with argument inside generator
7:4: 'yield' outside function
9:0: 'yield' not supported here
12:4: 'yield' not supported here
"""
tests/errors/e_nonlocal_T490.pyx
0 → 100644
View file @
7ef3e52c
def
test_non_existant
():
nonlocal
no_such_name
no_such_name
=
1
def
redef
():
x
=
1
def
f
():
x
=
2
nonlocal
x
global_name
=
5
def
ref_to_global
():
nonlocal
global_name
global_name
=
6
def
global_in_class_scope
():
class
Test
():
nonlocal
global_name
global_name
=
6
def
redef_in_class_scope
():
x
=
1
class
Test
():
x
=
2
nonlocal
x
_ERRORS
=
u"""
3:4: no binding for nonlocal 'no_such_name' found
10:8: 'x' redeclared as nonlocal
15:4: no binding for nonlocal 'global_name' found
27:8: 'x' redeclared as nonlocal
"""
tests/run/closure_self.pyx
0 → 100644
View file @
7ef3e52c
cdef
class
Test
:
cdef
int
x
cdef
class
SelfInClosure
(
object
):
cdef
Test
_t
cdef
int
x
def
plain
(
self
):
"""
>>> o = SelfInClosure()
>>> o.plain()
1
"""
self
.
x
=
1
return
self
.
x
def
closure_method
(
self
):
"""
>>> o = SelfInClosure()
>>> o.closure_method()() == o
True
"""
def
nested
():
return
self
return
nested
def
closure_method_cdef_attr
(
self
,
Test
t
):
"""
>>> o = SelfInClosure()
>>> o.closure_method_cdef_attr(Test())()
(1, 2)
"""
t
.
x
=
2
self
.
_t
=
t
self
.
x
=
1
def
nested
():
return
self
.
x
,
t
.
x
return
nested
tests/run/generator_type_inference.pyx
0 → 100644
View file @
7ef3e52c
# mode: run
# tag: typeinference, generators
cimport
cython
def
test_type_inference
():
"""
>>> [ item for item in test_type_inference() ]
[(2.0, 'double'), (2.0, 'double'), (2.0, 'double')]
"""
x
=
1.0
for
i
in
range
(
3
):
yield
x
*
2.0
,
cython
.
typeof
(
x
)
tests/run/generators.pyx
0 → 100644
View file @
7ef3e52c
try
:
from
builtins
import
next
# Py3k
except
ImportError
:
def
next
(
it
):
return
it
.
next
()
if
hasattr
(
__builtins__
,
'GeneratorExit'
):
GeneratorExit
=
__builtins__
.
GeneratorExit
else
:
# < 2.5
GeneratorExit
=
StopIteration
def
very_simple
():
"""
>>> x = very_simple()
>>> next(x)
1
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> x = very_simple()
>>> x.send(1)
Traceback (most recent call last):
TypeError: can't send non-None value to a just-started generator
"""
yield
1
def
simple
():
"""
>>> x = simple()
>>> list(x)
[1, 2, 3]
"""
yield
1
yield
2
yield
3
def
simple_seq
(
seq
):
"""
>>> x = simple_seq("abc")
>>> list(x)
['a', 'b', 'c']
"""
for
i
in
seq
:
yield
i
def
simple_send
():
"""
>>> x = simple_send()
>>> next(x)
>>> x.send(1)
1
>>> x.send(2)
2
>>> x.send(3)
3
"""
i
=
None
while
True
:
i
=
yield
i
def
raising
():
"""
>>> x = raising()
>>> next(x)
Traceback (most recent call last):
KeyError: 'foo'
>>> next(x)
Traceback (most recent call last):
StopIteration
"""
yield
{}[
'foo'
]
def
with_outer
(
*
args
):
"""
>>> x = with_outer(1, 2, 3)
>>> list(x())
[1, 2, 3]
"""
def
generator
():
for
i
in
args
:
yield
i
return
generator
def
with_outer_raising
(
*
args
):
"""
>>> x = with_outer_raising(1, 2, 3)
>>> list(x())
[1, 2, 3]
"""
def
generator
():
for
i
in
args
:
yield
i
raise
StopIteration
return
generator
def
test_close
():
"""
>>> x = test_close()
>>> x.close()
>>> x = test_close()
>>> next(x)
>>> x.close()
>>> next(x)
Traceback (most recent call last):
StopIteration
"""
while
True
:
yield
def
test_ignore_close
():
"""
>>> x = test_ignore_close()
>>> x.close()
>>> x = test_ignore_close()
>>> next(x)
>>> x.close()
Traceback (most recent call last):
RuntimeError: generator ignored GeneratorExit
"""
try
:
yield
except
GeneratorExit
:
yield
def
check_throw
():
"""
>>> x = check_throw()
>>> x.throw(ValueError)
Traceback (most recent call last):
ValueError
>>> next(x)
Traceback (most recent call last):
StopIteration
>>> x = check_throw()
>>> next(x)
>>> x.throw(ValueError)
>>> next(x)
>>> x.throw(IndexError, "oops")
Traceback (most recent call last):
IndexError: oops
>>> next(x)
Traceback (most recent call last):
StopIteration
"""
while
True
:
try
:
yield
except
ValueError
:
pass
def
test_first_assignment
():
"""
>>> gen = test_first_assignment()
>>> next(gen)
5
>>> next(gen)
10
>>> next(gen)
(5, 10)
"""
cdef
x
=
5
# first
yield
x
cdef
y
=
10
# first
yield
y
yield
(
x
,
y
)
def
test_swap_assignment
():
"""
>>> gen = test_swap_assignment()
>>> next(gen)
(5, 10)
>>> next(gen)
(10, 5)
"""
x
,
y
=
5
,
10
yield
(
x
,
y
)
x
,
y
=
y
,
x
# no ref-counting here
yield
(
x
,
y
)
class
Foo
(
object
):
"""
>>> obj = Foo()
>>> list(obj.simple(1, 2, 3))
[1, 2, 3]
"""
def
simple
(
self
,
*
args
):
for
i
in
args
:
yield
i
def
generator_nonlocal
():
"""
>>> g = generator_nonlocal()
>>> list(g(5))
[2, 3, 4, 5, 6]
"""
def
f
(
x
):
def
g
(
y
):
nonlocal
x
for
i
in
range
(
y
):
x
+=
1
yield
x
return
g
return
f
(
1
)
def
test_nested
(
a
,
b
,
c
):
"""
>>> obj = test_nested(1, 2, 3)
>>> [i() for i in obj]
[1, 2, 3, 4]
"""
def
one
():
return
a
def
two
():
return
b
def
three
():
return
c
def
new_closure
(
a
,
b
):
def
sum
():
return
a
+
b
return
sum
yield
one
yield
two
yield
three
yield
new_closure
(
a
,
c
)
def
tolist
(
func
):
def
wrapper
(
*
args
,
**
kwargs
):
return
list
(
func
(
*
args
,
**
kwargs
))
return
wrapper
@
tolist
def
test_decorated
(
*
args
):
"""
>>> test_decorated(1, 2, 3)
[1, 2, 3]
"""
for
i
in
args
:
yield
i
def
test_return
(
a
):
"""
>>> d = dict()
>>> obj = test_return(d)
>>> next(obj)
1
>>> next(obj)
Traceback (most recent call last):
StopIteration
>>> d['i_was_here']
True
"""
yield
1
a
[
'i_was_here'
]
=
True
return
def
test_copied_yield
(
foo
):
"""
>>> class Manager(object):
... def __enter__(self):
... return self
... def __exit__(self, type, value, tb):
... pass
>>> list(test_copied_yield(Manager()))
[1]
"""
with
foo
:
yield
1
def
test_nested_yield
():
"""
>>> obj = test_nested_yield()
>>> next(obj)
1
>>> obj.send(2)
2
>>> obj.send(3)
3
>>> obj.send(4)
Traceback (most recent call last):
StopIteration
"""
yield
(
yield
(
yield
1
))
def
test_inside_lambda
():
"""
>>> obj = test_inside_lambda()()
>>> next(obj)
1
>>> obj.send('a')
2
>>> obj.send('b')
('a', 'b')
"""
return
lambda
:((
yield
1
),
(
yield
2
))
def
test_nested_gen
(
int
n
):
"""
>>> [list(a) for a in test_nested_gen(5)]
[[], [0], [0, 1], [0, 1, 2], [0, 1, 2, 3]]
"""
for
a
in
range
(
n
):
yield
(
b
for
b
in
range
(
a
))
def
test_lambda
(
n
):
"""
>>> [i() for i in test_lambda(3)]
[0, 1, 2]
"""
for
i
in
range
(
n
):
yield
lambda
:
i
tests/run/nonlocal_T490.pyx
0 → 100644
View file @
7ef3e52c
def
simple
():
"""
>>> simple()
1
2
"""
x
=
1
y
=
2
def
f
():
nonlocal
x
nonlocal
x
,
y
print
(
x
)
print
(
y
)
f
()
def
assign
():
"""
>>> assign()
1
"""
xx
=
0
def
ff
():
nonlocal
xx
xx
+=
1
print
(
xx
)
ff
()
def
nested
():
"""
>>> nested()
1
"""
x
=
0
def
fx
():
def
gx
():
nonlocal
x
x
=
1
print
(
x
)
return
gx
fx
()()
def
arg
(
x
):
"""
>>> arg('x')
xyy
"""
def
appendy
():
nonlocal
x
x
+=
'y'
x
+=
'y'
appendy
()
print
x
return
def
argtype
(
int
n
):
"""
>>> argtype(0)
1
"""
def
inc
():
nonlocal
n
n
+=
1
inc
()
print
n
return
def
ping_pong
():
"""
>>> f = ping_pong()
>>> inc, dec = f(0)
>>> inc()
1
>>> inc()
2
>>> dec()
1
>>> inc()
2
>>> dec()
1
>>> dec()
0
"""
def
f
(
x
):
def
inc
():
nonlocal
x
x
+=
1
return
x
def
dec
():
nonlocal
x
x
-=
1
return
x
return
inc
,
dec
return
f
def
methods
():
"""
>>> f = methods()
>>> c = f(0)
>>> c.inc()
1
>>> c.inc()
2
>>> c.dec()
1
>>> c.dec()
0
"""
def
f
(
x
):
class
c
:
def
inc
(
self
):
nonlocal
x
x
+=
1
return
x
def
dec
(
self
):
nonlocal
x
x
-=
1
return
x
return
c
()
return
f
def
class_body
(
int
x
,
y
):
"""
>>> c = class_body(2,99)
>>> c.z
(3, 2)
>>> c.x #doctest: +ELLIPSIS
Traceback (most recent call last):
AttributeError: ...
>>> c.y #doctest: +ELLIPSIS
Traceback (most recent call last):
AttributeError: ...
"""
class
c
(
object
):
nonlocal
x
nonlocal
y
y
=
2
x
+=
1
z
=
x
,
y
return
c
()
def
nested_nonlocals
(
x
):
"""
>>> g = nested_nonlocals(1)
>>> h = g()
>>> h()
3
"""
def
g
():
nonlocal
x
x
-=
2
def
h
():
nonlocal
x
x
+=
4
return
x
return
h
return
g
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