Author: Robert Zaremba <robert.zare...@scale-it.pl> Branch: py3.5-fix-globals Changeset: r90411:802f8b2e090c Date: 2017-02-27 17:56 +0100 http://bitbucket.org/pypy/pypy/changeset/802f8b2e090c/
Log: (stevie, robert-zaremba) Fix symtable for nonlocal Added tests and updated functionality for symtable construction for nonlocal declarations. diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py --- a/pypy/interpreter/astcompiler/symtable.py +++ b/pypy/interpreter/astcompiler/symtable.py @@ -8,7 +8,7 @@ # These are for internal use only: SYM_BLANK = 0 SYM_GLOBAL = 1 -SYM_ASSIGNED = 2 # Or deleted actually. +SYM_ASSIGNED = 2 # (DEF_LOCAL in CPython3). Or deleted actually. SYM_PARAM = 2 << 1 SYM_NONLOCAL = 2 << 2 SYM_USED = 2 << 3 @@ -123,9 +123,6 @@ def _finalize_name(self, name, flags, local, bound, free, globs): """Decide on the scope of a name.""" if flags & SYM_GLOBAL: - if flags & SYM_NONLOCAL: - err = "name '%s' is nonlocal and global" % (name,) - raise SyntaxError(err, self.lineno, self.col_offset) self.symbols[name] = SCOPE_GLOBAL_EXPLICIT globs[name] = None if bound: @@ -134,12 +131,6 @@ except KeyError: pass elif flags & SYM_NONLOCAL: - if flags & SYM_PARAM: - err = "name '%s' is parameter and nonlocal" % (name,) - raise SyntaxError(err, self.lineno, self.col_offset) - if bound is None: - err = "nonlocal declaration not allowed at module level" - raise SyntaxError(err, self.lineno, self.col_offset) if name not in bound: err = "no binding for nonlocal '%s' found" % (name,) raise SyntaxError(err, self.lineno, self.col_offset) @@ -497,6 +488,9 @@ if old_role & SYM_PARAM: msg = "name '%s' is parameter and global" % (name,) raise SyntaxError(msg, glob.lineno, glob.col_offset) + if old_role & SYM_NONLOCAL: + msg = "name '%s' is nonlocal and global" % (name,) + raise SyntaxError(msg, glob.lineno, glob.col_offset) if old_role & (SYM_USED | SYM_ASSIGNED): if old_role & SYM_ASSIGNED: @@ -513,6 +507,16 @@ def visit_Nonlocal(self, nonl): for name in nonl.names: old_role = self.scope.lookup_role(name) + msg = "" + if old_role & SYM_GLOBAL: + msg = "name '%s' is nonlocal and global" % (name,) + if old_role & SYM_PARAM: + msg = "name '%s' is parameter and nonlocal" % (name,) + if type(self.scope) == ModuleScope: + msg = "nonlocal declaration not allowed at module level" + if msg is not "": + raise SyntaxError(msg, nonl.lineno, nonl.col_offset) + if (old_role & (SYM_USED | SYM_ASSIGNED) and not (name == '__class__' and self.scope._hide_bound_from_nested_scopes)): @@ -525,6 +529,7 @@ misc.syntax_warning(self.space, msg, self.compile_info.filename, nonl.lineno, nonl.col_offset) + self.note_symbol(name, SYM_NONLOCAL) def visit_Lambda(self, lamb): diff --git a/pypy/interpreter/astcompiler/test/test_symtable.py b/pypy/interpreter/astcompiler/test/test_symtable.py --- a/pypy/interpreter/astcompiler/test/test_symtable.py +++ b/pypy/interpreter/astcompiler/test/test_symtable.py @@ -295,17 +295,17 @@ assert xscp.lookup("y") == symtable.SCOPE_GLOBAL_EXPLICIT assert zscp.lookup("y") == symtable.SCOPE_FREE - code = "def f(x):\n global x" - exc = py.test.raises(SyntaxError, self.func_scope, code).value + src = "def f(x):\n global x" + exc = py.test.raises(SyntaxError, self.func_scope, src).value assert exc.lineno == 2 assert exc.msg == "name 'x' is parameter and global" def test_global_nested(self): - code = """ + src = """ def f(x): def g(x): global x""" - exc = py.test.raises(SyntaxError, self.func_scope, code).value + exc = py.test.raises(SyntaxError, self.func_scope, src).value assert exc.lineno == 4 assert exc.msg == "name 'x' is parameter and global" @@ -319,14 +319,13 @@ assert x == symtable.SYM_GLOBAL def test_nonlocal(self): - src = str(py.code.Source(""" - def f(): - nonlocal x - global x - """)) + src = """ +x = 1 +def f(): + nonlocal x""" exc = py.test.raises(SyntaxError, self.func_scope, src).value - assert exc.msg == "name 'x' is nonlocal and global" - # + assert exc.msg == "no binding for nonlocal 'x' found" + src = str(py.code.Source(""" def f(x): nonlocal x @@ -344,6 +343,58 @@ src = "nonlocal x" exc = py.test.raises(SyntaxError, self.func_scope, src).value assert exc.msg == "nonlocal declaration not allowed at module level" + assert exc.lineno == 1 + + src = "x = 2\nnonlocal x" + exc = py.test.raises(SyntaxError, self.func_scope, src).value + assert exc.msg == "nonlocal declaration not allowed at module level" + assert exc.lineno == 2 + + def test_nonlocal_and_global(self): + """This test differs from CPython3 behaviour. CPython points to the + first occurance of the global/local expression. PyPy will point to the + last expression which makes the problem.""" + src = """ +def f(): + nonlocal x + global x""" + exc = py.test.raises(SyntaxError, self.func_scope, src).value + assert exc.msg == "name 'x' is nonlocal and global" + assert exc.lineno == 4 + + src = """ +def f(): + global x + nonlocal x """ + exc = py.test.raises(SyntaxError, self.func_scope, src).value + assert exc.msg == "name 'x' is nonlocal and global" + assert exc.lineno == 4 + + def test_nonlocal_nested(self): + scp = self.func_scope(""" +def f(x): + def g(): + nonlocal x""") + g = scp.children[0] + x = g.lookup_role('x') + assert x == symtable.SYM_NONLOCAL + + scp = self.func_scope(""" +def f(): + def g(): + nonlocal x + x = 1""") + g = scp.children[0] + x = g.lookup_role('x') + assert x == symtable.SYM_NONLOCAL + + src = """ +def f(x): + def g(x): + nonlocal x""" + exc = py.test.raises(SyntaxError, self.func_scope, src).value + assert exc.msg == "name 'x' is parameter and nonlocal" + assert exc.lineno == 4 def test_optimization(self): assert not self.mod_scope("").can_be_optimized _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit