From: Stefan Behnel Date: Tue, 6 Jul 2010 05:20:22 +0000 (+0200) Subject: do not let set/dict comprehensions leak in Py2, only list comprehensions X-Git-Tag: 0.13.beta0~2^2~21 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=c400bbd5fd088b514a5560d0f113a23b1fe0a44e;p=cython.git do not let set/dict comprehensions leak in Py2, only list comprehensions --- diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index f5fc0425..2f33e9cf 100755 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3940,8 +3940,10 @@ class ComprehensionNode(ScopedExprNode): subexprs = ["target"] child_attrs = ["loop", "append"] - # different behaviour in Py2 and Py3: leak loop variables or not? - has_local_scope = False # Py2 behaviour as default + # leak loop variables or not? non-leaking Py3 behaviour is + # default, except for list comprehensions where the behaviour + # differs in Py2 and Py3 (see Parsing.py) + has_local_scope = True def infer_type(self, env): return self.target.infer_type(env) diff --git a/Cython/Compiler/Parsing.py b/Cython/Compiler/Parsing.py index 22204db0..1cd0a67e 100644 --- a/Cython/Compiler/Parsing.py +++ b/Cython/Compiler/Parsing.py @@ -780,7 +780,9 @@ def p_list_maker(s): loop = p_comp_for(s, append) s.expect(']') return ExprNodes.ComprehensionNode( - pos, loop=loop, append=append, target=target) + pos, loop=loop, append=append, target=target, + # list comprehensions leak their loop variable in Py2 + has_local_scope = s.context.language_level > 2) else: if s.sy == ',': s.next() diff --git a/tests/run/cython3.pyx b/tests/run/cython3.pyx index 37442e92..8c79cec6 100644 --- a/tests/run/cython3.pyx +++ b/tests/run/cython3.pyx @@ -1,5 +1,13 @@ # cython: language_level=3 +try: + sorted +except NameError: + def sorted(seq): + seq = list(seq) + seq.sort() + return seq + def print_function(*args): """ >>> print_function(1,2,3) @@ -17,3 +25,33 @@ def unicode_literals(): """ print(isinstance(ustring, unicode) or type(ustring)) return ustring + +def list_comp(): + """ + >>> list_comp() + [0, 4, 8] + """ + x = 'abc' + result = [x*2 for x in range(5) if x % 2 == 0] + assert x == 'abc' # don't leak in Py3 code + return result + +def set_comp(): + """ + >>> sorted(set_comp()) + [0, 4, 8] + """ + x = 'abc' + result = {x*2 for x in range(5) if x % 2 == 0} + assert x == 'abc' # don't leak + return result + +def dict_comp(): + """ + >>> sorted(dict_comp().items()) + [(0, 0), (2, 4), (4, 8)] + """ + x = 'abc' + result = {x:x*2 for x in range(5) if x % 2 == 0} + assert x == 'abc' # don't leak + return result diff --git a/tests/run/dictcomp.pyx b/tests/run/dictcomp.pyx index d53310f3..08a6dca7 100644 --- a/tests/run/dictcomp.pyx +++ b/tests/run/dictcomp.pyx @@ -12,7 +12,7 @@ def dictcomp(): result = { x+2:x*2 for x in range(5) if x % 2 == 0 } - assert x != 'abc' + assert x == 'abc' # do not leak! return result @cython.test_fail_if_path_exists( diff --git a/tests/run/setcomp.pyx b/tests/run/setcomp.pyx index a89ca515..ea6a2901 100644 --- a/tests/run/setcomp.pyx +++ b/tests/run/setcomp.pyx @@ -13,9 +13,12 @@ def setcomp(): >>> sorted(setcomp()) [0, 4, 8] """ - return { x*2 + x = 'abc' + result = { x*2 for x in range(5) if x % 2 == 0 } + assert x == 'abc' # do not leak + return result @cython.test_fail_if_path_exists( "//GeneratorExpressionNode", @@ -30,9 +33,12 @@ def genexp_set(): >>> sorted(genexp_set()) [0, 4, 8] """ - return set( x*2 - for x in range(5) - if x % 2 == 0 ) + x = 'abc' + result = set( x*2 + for x in range(5) + if x % 2 == 0 ) + assert x == 'abc' # do not leak + return result cdef class A: def __repr__(self): return u"A"