[pypy-commit] pypy py3.5-fix-globals: (stevie, robert-zaremba) Fix symtable for nonlocal
robert-zaremba
pypy.commits at gmail.com
Tue Feb 28 04:59:12 EST 2017
Author: Robert Zaremba <robert.zaremba at 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
More information about the pypy-commit
mailing list