[pypy-svn] r66096 - in pypy/branch/parser-compiler/pypy/interpreter/astcompiler: . test
benjamin at codespeak.net
benjamin at codespeak.net
Fri Jul 3 01:01:02 CEST 2009
Author: benjamin
Date: Fri Jul 3 01:01:00 2009
New Revision: 66096
Added:
pypy/branch/parser-compiler/pypy/interpreter/astcompiler/symtable.py (contents, props changed)
pypy/branch/parser-compiler/pypy/interpreter/astcompiler/test/test_symtable.py (contents, props changed)
Log:
implement symbol table builder
Added: pypy/branch/parser-compiler/pypy/interpreter/astcompiler/symtable.py
==============================================================================
--- (empty file)
+++ pypy/branch/parser-compiler/pypy/interpreter/astcompiler/symtable.py Fri Jul 3 01:01:00 2009
@@ -0,0 +1,408 @@
+"""
+Symbol tabling building.
+"""
+
+from pypy.interpreter.astcompiler import ast2 as ast, misc
+from pypy.interpreter.pyparser.error import SyntaxError
+
+# These are for internal use only:
+SYM_BLANK = 0
+SYM_GLOBAL = 1
+SYM_ASSIGNED = 2 # Or deleted actually.
+SYM_PARAM = 2 << 1
+SYM_USED = 2 << 2
+SYM_BOUND = (SYM_PARAM | SYM_ASSIGNED)
+
+# codegen.py actually deals with these:
+SCOPE_UNKNOWN = 0
+SCOPE_GLOBAL_IMPLICIT = 1
+SCOPE_GLOBAL_EXPLICIT = 2
+SCOPE_LOCAL = 3
+SCOPE_FREE = 4
+SCOPE_CELL = 5
+
+
+class Scope(object):
+
+ def __init__(self, node, name, optimized):
+ self.node = node
+ self.parent = None
+ self.name = name
+ self.optimized = optimized
+ self.symbols = None
+ self.roles = {}
+ self.varnames = []
+ self.children = []
+ self.has_free = False
+ self.child_has_free = False
+ self.nested = False
+
+ def lookup(self, name):
+ return self.symbols.get(name, SCOPE_UNKNOWN)
+
+ def note_symbol(self, identifier, role):
+ mangled = self.mangle(identifier)
+ new_role = role
+ if identifier in self.roles:
+ old_role = self.roles[identifier]
+ if old_role & SYM_PARAM and role & SYM_PARAM:
+ err = "duplicate argument '%s' in function definition" % \
+ (identifier,)
+ raise SyntaxError(err, self.node.lineno, self.node.col_offset)
+ new_role |= old_role
+ self.roles[mangled] = new_role
+ if role & SYM_PARAM:
+ self.varnames.append(mangled)
+ return mangled
+
+ def note_yield(self, yield_node):
+ raise SyntaxError("yield outside function", yield_node.lineno,
+ yield_node.col_offset)
+
+ def note_return(self, ret):
+ raise SyntaxError("return outside function", ret.lineno,
+ ret.col_offset)
+
+ def note_bare_exec(self, exc):
+ pass
+
+ def note_import_star(self, imp):
+ pass
+
+ def mangle(self, name):
+ if self.parent:
+ return self.parent.mangle(name)
+ else:
+ return name
+
+ def add_child(self, child_scope):
+ child_scope.parent = self
+ self.children.append(child_scope)
+
+ def _finalize_name(self, name, flags, local, bound, free, globs):
+ if flags & SYM_GLOBAL:
+ if flags & SYM_PARAM:
+ raise SyntaxError("name %r is both local and global" % (name,),
+ self.node.lineno, self.node.col_offset)
+ self.symbols[name] = SCOPE_GLOBAL_EXPLICIT
+ globs[name] = None
+ if bound:
+ try:
+ del bound[name]
+ except KeyError:
+ pass
+ elif flags & SYM_BOUND:
+ self.symbols[name] = SCOPE_LOCAL
+ local[name] = None
+ try:
+ del globs[name]
+ except KeyError:
+ pass
+ elif bound and name in bound:
+ self.symbols[name] = SCOPE_FREE
+ free[name] = None
+ self.has_free = True
+ elif name in globs:
+ self.symbols[name] = SCOPE_GLOBAL_IMPLICIT
+ else:
+ if self.nested:
+ self.has_free = True
+ self.symbols[name] = SCOPE_GLOBAL_IMPLICIT
+
+ def _pass_on_bindings(self, local, bound, globs, new_bound, new_globs):
+ new_globs.update(globs)
+ if bound:
+ new_bound.update(bound)
+
+ def _finalize_cells(self, free):
+ pass
+
+ def _check_optimization(self):
+ pass
+
+ _hide_bound_from_nested_scopes = False
+
+ def finalize(self, bound, free, globs):
+ self.symbols = {}
+ local = {}
+ new_globs = {}
+ new_bound = {}
+ new_free = {}
+ if self._hide_bound_from_nested_scopes:
+ self._pass_on_bindings(local, bound, globs, new_bound, new_globs)
+ for name, flags in self.roles.iteritems():
+ self._finalize_name(name, flags, local, bound, free, globs)
+ if not self._hide_bound_from_nested_scopes:
+ self._pass_on_bindings(local, bound, globs, new_bound, new_globs)
+ child_frees = {}
+ for child in self.children:
+ child_free = new_free.copy()
+ child.finalize(new_bound.copy(), child_free, new_globs.copy())
+ child_frees.update(child_free)
+ if child.has_free or child.child_has_free:
+ self.child_has_free = True
+ new_free.update(child_frees)
+ self._finalize_cells(new_free)
+ for name in new_free:
+ try:
+ role_here = self.roles[name]
+ except KeyError:
+ if name in bound:
+ self.symbols[name] = SCOPE_FREE
+ else:
+ if role_here & (SYM_ASSIGNED | SYM_GLOBAL) and \
+ self._hide_bound_from_nested_scopes:
+ self.symbols[name] = SCOPE_FREE
+ self._check_optimization()
+ free.update(new_free)
+
+
+class ModuleScope(Scope):
+
+ def __init__(self, module):
+ Scope.__init__(self, module, "top", False)
+
+
+class FunctionScope(Scope):
+
+ def __init__(self, func, name):
+ Scope.__init__(self, func, name, True)
+ self.has_variable_arg = False
+ self.has_keywords_arg = False
+ self.is_generator = False
+ self.return_with_value = False
+ self.import_star = None
+ self.bare_exec = None
+
+ def note_yield(self, yield_node):
+ if self.return_with_value:
+ raise SyntaxError("return with value in generator",
+ yield_node.lineno, yield_node.col_offset)
+ self.is_generator = True
+
+ def note_return(self, ret):
+ if self.is_generator and ret.value:
+ raise SyntaxError("return with value in generator", ret.lineno,
+ ret.col_offset)
+ self.return_with_value = True
+
+ def note_exec(self, exc):
+ self.has_exec = True
+ if not exc.globals:
+ self.optimized = False
+ self.bare_exec = exc
+
+ def note_import_star(self, imp):
+ self.optimized = False
+ self.import_star = imp
+
+ def note_variable_arg(self, vararg):
+ self.has_variable_arg = True
+
+ def note_keywords_arg(self, kwarg):
+ self.has_keywords_arg = True
+
+ def add_child(self, child_scope):
+ Scope.add_child(self, child_scope)
+ child_scope.nested = True
+
+ def _pass_on_bindings(self, local, bound, globs, new_bound, new_globs):
+ new_bound.update(local)
+ Scope._pass_on_bindings(self, local, bound, globs, new_bound, new_globs)
+
+ def _finalize_cells(self, free):
+ for name, role in self.symbols.iteritems():
+ if role == SCOPE_LOCAL and name in free:
+ self.symbols[name] = SCOPE_CELL
+ del free[name]
+
+ def _check_optimization(self):
+ if (self.has_free or self.child_has_free) and not self.optimized:
+ err = None
+ if self.import_star:
+ node = self.import_star
+ if self.bare_exec:
+ err = "function %r uses import * and bare exec, " \
+ "which are illegal because it %s"
+ else:
+ err = "import * is not allowed in function %r because it %s"
+ elif self.bare_exec:
+ node = self.bare_exec
+ err = "unqualified exec is not allowed in function %r " \
+ "because it %s"
+ else:
+ raise AssertionError("unkown reason for unoptimization")
+ if self.child_has_free:
+ trailer = "contains a nested function with free variables"
+ else:
+ trailer = "is a nested function"
+ raise SyntaxError(err % (self.name, trailer), node.lineno,
+ node.col_offset)
+
+
+class ClassScope(Scope):
+
+ _hide_bound_from_nested_scopes = True
+
+ def __init__(self, clsdef):
+ Scope.__init__(self, clsdef, clsdef.name, False)
+
+ def mangle(self, name):
+ return misc.mangle(name, self.name)
+
+
+class SymtableBuilder(ast.GenericASTVisitor):
+
+ def __init__(self, space, module):
+ self.space = space
+ self.module = module
+ self.scopes = {}
+ self.scope = None
+ self.stack = []
+ top = ModuleScope(module)
+ self.globs = top.roles
+ self.push_scope(top)
+ module.walkabout(self)
+ top.finalize(None, {}, {})
+ self.pop_scope()
+ assert not self.stack
+
+ def push_scope(self, scope):
+ if self.stack:
+ self.stack[-1].add_child(scope)
+ self.stack.append(scope)
+ self.scopes[scope.node] = scope
+ # Convenience
+ self.scope = scope
+
+ def pop_scope(self):
+ self.stack.pop()
+ if self.stack:
+ self.scope = self.stack[-1]
+ else:
+ self.scope = None
+
+ def find_scope(self, scope_node):
+ return self.scopes[scope_node]
+
+ def implicit_arg(self, pos):
+ name = ".%i" % (pos,)
+ self.note_symbol(name, SYM_PARAM)
+
+ def note_symbol(self, identifier, role):
+ mangled = self.scope.note_symbol(identifier, role)
+ if role & SYM_GLOBAL:
+ if identifier in self.globs:
+ role |= self.globs[mangled]
+ self.globs[mangled] = role
+
+ def visit_FunctionDef(self, func):
+ self.note_symbol(func.name, SYM_ASSIGNED)
+ if func.args.defaults:
+ self.visit_sequence(func.args.defaults)
+ if func.decorators:
+ self.visit_sequence(func.decorators)
+ self.push_scope(FunctionScope(func, func.name))
+ func.args.walkabout(self)
+ self.visit_sequence(func.body)
+ self.pop_scope()
+
+ def visit_Return(self, ret):
+ self.scope.note_return(ret)
+ ast.GenericASTVisitor.visit_Return(self, ret)
+
+ def visit_ClassDef(self, clsdef):
+ self.note_symbol(clsdef.name, SYM_ASSIGNED)
+ if clsdef.bases:
+ self.visit_sequence(clsdef.bases)
+ self.push_scope(ClassScope(clsdef))
+ self.visit_sequence(clsdef.body)
+ self.pop_scope()
+
+ def visit_ImportFrom(self, imp):
+ for alias in imp.names:
+ if self.visit_alias(alias):
+ self.scope.note_import_star(imp)
+
+ def visit_alias(self, alias):
+ if alias.asname:
+ store_name = alias.asname
+ else:
+ store_name = alias.name
+ if store_name == "*":
+ return True
+ dot = store_name.find(".")
+ if dot != -1:
+ store_name = store_name[:dot]
+ self.note_symbol(store_name, SYM_ASSIGNED)
+ return False
+
+ def visit_Exec(self, exc):
+ self.scope.note_exec(exc)
+ ast.GenericASTVisitor.visit_Exec(self, exc)
+
+ def visit_Yield(self, yie):
+ self.scope.note_yield(yie)
+ ast.GenericASTVisitor.visit_Yield(self, yie)
+
+ def visit_Global(self, glob):
+ for name in glob.names:
+ self.note_symbol(name, SYM_GLOBAL)
+
+ def visit_Lambda(self, lamb):
+ if lamb.args.defaults:
+ self.visit_sequence(lamb.defaults)
+ self.push_scope(FunctionScope(lamb, "lambda"))
+ lamb.args.walkabout(self)
+ lamb.body.walkabout(self)
+ self.pop_scope()
+
+ def visit_GeneratorExp(self, genexp):
+ outer = genexp.generators[0]
+ outer.iter.walkabout(self)
+ self.push_scope(FunctionScope(genexp, "genexp"))
+ self.implicit_arg(0)
+ outer.target.walkabout(self)
+ if outer.ifs:
+ self.visit_sequence(outer.ifs)
+ self.visit_sequence(genexp.generators[1:])
+ genexp.elt.walkabout(self)
+ self.pop_scope()
+
+ def visit_arguments(self, arguments):
+ assert isinstance(self.scope, FunctionScope) # Annotator hint.
+ if arguments.args:
+ self._handle_params(arguments.args, True)
+ if arguments.vararg:
+ self.note_symbol(arguments.vararg, SYM_PARAM)
+ self.scope.note_variable_arg(arguments.vararg)
+ if arguments.kwarg:
+ self.note_symbol(arguments.kwarg, SYM_PARAM)
+ self.scope.note_keywords_arg(arguments.kwarg)
+ if arguments.args:
+ self._handle_nested_params(arguments.args)
+
+ def _handle_params(self, params, is_toplevel):
+ for i in range(len(params)):
+ arg = params[i]
+ if isinstance(arg, ast.Name):
+ self.note_symbol(arg.id, SYM_PARAM)
+ elif isinstance(arg, ast.Tuple):
+ if is_toplevel:
+ self.implicit_arg(i)
+ else:
+ raise AssertionError("unkown parameter type")
+ if not is_toplevel:
+ self._handle_nested_params(params)
+
+ def _handle_nested_params(self, params):
+ for param in params:
+ if isinstance(param, ast.Tuple):
+ self._handle_params(param.elts, False)
+
+ def visit_Name(self, name):
+ if name.ctx is ast.Load:
+ role = SYM_USED
+ else:
+ role = SYM_ASSIGNED
+ self.note_symbol(name.id, role)
Added: pypy/branch/parser-compiler/pypy/interpreter/astcompiler/test/test_symtable.py
==============================================================================
--- (empty file)
+++ pypy/branch/parser-compiler/pypy/interpreter/astcompiler/test/test_symtable.py Fri Jul 3 01:01:00 2009
@@ -0,0 +1,290 @@
+import string
+import py
+from pypy.interpreter.astcompiler import ast2 as ast, astbuilder, symtable
+from pypy.interpreter.pyparser import pyparse
+from pypy.interpreter.pyparser.error import SyntaxError
+
+
+class TestSymbolTable:
+
+ def setup_class(cls):
+ cls.parser = pyparse.PythonParser(cls.space)
+
+ def mod_scope(self, source, mode="exec"):
+ tree = self.parser.parse_source(source)
+ module = astbuilder.ast_from_node(self.space, tree)
+ builder = symtable.SymtableBuilder(self.space, module)
+ scope = builder.find_scope(module)
+ assert isinstance(scope, symtable.ModuleScope)
+ return scope
+
+ def func_scope(self, func_code):
+ mod_scope = self.mod_scope(func_code)
+ assert len(mod_scope.children) == 1
+ func_name = mod_scope.lookup("f")
+ assert func_name == symtable.SCOPE_LOCAL
+ func_scope = mod_scope.children[0]
+ assert isinstance(func_scope, symtable.FunctionScope)
+ return func_scope
+
+ def class_scope(self, class_code):
+ mod_scope = self.mod_scope(class_code)
+ assert len(mod_scope.children) == 1
+ class_name = mod_scope.lookup("x")
+ assert class_name == symtable.SCOPE_LOCAL
+ class_scope = mod_scope.children[0]
+ assert isinstance(class_scope, symtable.ClassScope)
+ return class_scope
+
+ def gen_scope(self, gen_code):
+ mod_scope = self.mod_scope(gen_code)
+ assert len(mod_scope.children) == 1
+ gen_scope = mod_scope.children[0]
+ assert isinstance(gen_scope, symtable.FunctionScope)
+ assert not gen_scope.children
+ assert gen_scope.name == "genexp"
+ return mod_scope, gen_scope
+
+ def check_unknown(self, scp, *names):
+ for name in names:
+ assert scp.lookup(name) == symtable.SCOPE_UNKNOWN
+
+ def test_toplevel(self):
+ scp = self.mod_scope("x = 4")
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ assert not scp.optimized
+ scp = self.mod_scope("x = 4", "single")
+ assert not scp.optimized
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.mod_scope("x*4*6", "eval")
+ assert not scp.optimized
+ assert scp.lookup("x") == symtable.SCOPE_GLOBAL_IMPLICIT
+
+ def test_duplicate_argument(self):
+ input = "def f(x, x): pass"
+ exc = py.test.raises(SyntaxError, self.mod_scope, input).value
+ assert exc.msg == "duplicate argument 'x' in function definition"
+
+ def test_function_defaults(self):
+ scp = self.mod_scope("y = 4\ndef f(x=y): return x")
+ self.check_unknown(scp, "x")
+ assert scp.lookup("y") == symtable.SCOPE_LOCAL
+ scp = scp.children[0]
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ self.check_unknown(scp, "y")
+
+ def test_genexp(self):
+ scp, gscp = self.gen_scope("(y[1] for y in z)")
+ assert scp.lookup("z") == symtable.SCOPE_GLOBAL_IMPLICIT
+ self.check_unknown(scp, "y", "x")
+ self.check_unknown(gscp, "z")
+ assert gscp.lookup("y") == symtable.SCOPE_LOCAL
+ scp, gscp = self.gen_scope("(x for x in z if x)")
+ self.check_unknown(scp, "x")
+ assert gscp.lookup("x") == symtable.SCOPE_LOCAL
+ scp, gscp = self.gen_scope("(x for y in g for f in n if f[h])")
+ self.check_unknown(scp, "f")
+ assert gscp.lookup("f") == symtable.SCOPE_LOCAL
+
+ def test_arguments(self):
+ scp = self.func_scope("def f(): pass")
+ assert not scp.children
+ self.check_unknown(scp, "x", "y")
+ assert not scp.symbols
+ assert not scp.roles
+ scp = self.func_scope("def f(x): pass")
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.func_scope("def f(*x): pass")
+ assert scp.has_variable_arg
+ assert not scp.has_keywords_arg
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.func_scope("def f(**x): pass")
+ assert scp.has_keywords_arg
+ assert not scp.has_variable_arg
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.func_scope("def f((x, y), a): pass")
+ for name in ("x", "y", "a"):
+ assert scp.lookup(name) == symtable.SCOPE_LOCAL
+ scp = self.func_scope("def f(((a, b), c)): pass")
+ for name in ("a", "b", "c"):
+ assert scp.lookup(name) == symtable.SCOPE_LOCAL
+
+ def test_function(self):
+ scp = self.func_scope("def f(): x = 4")
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.func_scope("def f(): x")
+ assert scp.lookup("x") == symtable.SCOPE_GLOBAL_IMPLICIT
+
+ def test_nested_scopes(self):
+ def nested_scope(*bodies):
+ names = enumerate("f" + string.ascii_letters)
+ lines = []
+ for body, (level, name) in zip(bodies, names):
+ lines.append(" " * level + "def %s():\n" % (name,))
+ if body:
+ if isinstance(body, str):
+ body = [body]
+ lines.extend(" " * (level + 1) + line + "\n"
+ for line in body)
+ return self.func_scope("".join(lines))
+ scp = nested_scope("x = 1", "return x")
+ assert not scp.has_free
+ assert scp.child_has_free
+ assert scp.lookup("x") == symtable.SCOPE_CELL
+ child = scp.children[0]
+ assert child.has_free
+ assert child.lookup("x") == symtable.SCOPE_FREE
+ scp = nested_scope("x = 1", None, "return x")
+ assert not scp.has_free
+ assert scp.child_has_free
+ assert scp.lookup("x") == symtable.SCOPE_CELL
+ child = scp.children[0]
+ assert not child.has_free
+ assert child.child_has_free
+ assert child.lookup("x") == symtable.SCOPE_FREE
+ child = child.children[0]
+ assert child.has_free
+ assert not child.child_has_free
+ assert child.lookup("x") == symtable.SCOPE_FREE
+ scp = nested_scope("x = 1", "x = 3", "return x")
+ assert scp.child_has_free
+ assert not scp.has_free
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ child = scp.children[0]
+ assert child.child_has_free
+ assert not child.has_free
+ assert child.lookup("x") == symtable.SCOPE_CELL
+ child = child.children[0]
+ assert child.has_free
+ assert child.lookup("x") == symtable.SCOPE_FREE
+
+ def test_class(self):
+ scp = self.mod_scope("class x(A, B): pass")
+ cscp = scp.children[0]
+ for name in ("A", "B"):
+ assert scp.lookup(name) == symtable.SCOPE_GLOBAL_IMPLICIT
+ self.check_unknown(cscp, name)
+ scp = self.func_scope("""def f(x):
+ class X:
+ def n():
+ return x
+ a = x
+ return X()""")
+ self.check_unknown(scp, "a")
+ assert scp.lookup("x") == symtable.SCOPE_CELL
+ assert scp.lookup("X") == symtable.SCOPE_LOCAL
+ cscp = scp.children[0]
+ assert cscp.lookup("a") == symtable.SCOPE_LOCAL
+ assert cscp.lookup("x") == symtable.SCOPE_FREE
+ fscp = cscp.children[0]
+ assert fscp.lookup("x") == symtable.SCOPE_FREE
+ self.check_unknown(fscp, "a")
+
+ def test_lambda(self):
+ scp = self.mod_scope("lambda x: y")
+ self.check_unknown(scp, "x", "y")
+ assert len(scp.children) == 1
+ lscp = scp.children[0]
+ assert isinstance(lscp, symtable.FunctionScope)
+ assert lscp.name == "lambda"
+ assert lscp.lookup("x") == symtable.SCOPE_LOCAL
+ assert lscp.lookup("y") == symtable.SCOPE_GLOBAL_IMPLICIT
+
+ def test_import(self):
+ scp = self.mod_scope("import x")
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ scp = self.mod_scope("import x as y")
+ assert scp.lookup("y") == symtable.SCOPE_LOCAL
+ self.check_unknown(scp, "x")
+ scp = self.mod_scope("import x.y")
+ assert scp.lookup("x") == symtable.SCOPE_LOCAL
+ self.check_unknown(scp, "y")
+
+ def test_from_import(self):
+ scp = self.mod_scope("from x import y")
+ self.check_unknown("x")
+ assert scp.lookup("y") == symtable.SCOPE_LOCAL
+ scp = self.mod_scope("from a import b as y")
+ assert scp.lookup("y") == symtable.SCOPE_LOCAL
+ self.check_unknown(scp, "a", "b")
+ scp = self.mod_scope("from x import *")
+ self.check_unknown("x")
+ scp = self.func_scope("def f(): from x import *")
+ self.check_unknown(scp, "x")
+ assert not scp.optimized
+ assert scp.import_star
+
+ def test_global(self):
+ scp = self.func_scope("def f():\n global x\n x = 4")
+ assert scp.lookup("x") == symtable.SCOPE_GLOBAL_EXPLICIT
+ input = "def f(x):\n global x"
+ scp = self.func_scope("""def f():
+ y = 3
+ def x():
+ global y
+ y = 4
+ def z():
+ return y""")
+ assert scp.lookup("y") == symtable.SCOPE_CELL
+ xscp, zscp = scp.children
+ assert xscp.lookup("y") == symtable.SCOPE_GLOBAL_EXPLICIT
+ assert zscp.lookup("y") == symtable.SCOPE_FREE
+ exc = py.test.raises(SyntaxError, self.func_scope, input).value
+ assert exc.msg == "name 'x' is both local and global"
+
+ def test_optimization(self):
+ assert not self.mod_scope("").optimized
+ assert not self.class_scope("class x: pass").optimized
+ assert self.func_scope("def f(): pass").optimized
+
+ def test_unoptimization_with_nested_scopes(self):
+ table = (
+ ("from x import *; exec 'hi'", "function 'f' uses import * " \
+ "and bare exec, which are illegal because it"),
+ ("from x import *", "import * is not allowed in function 'f' " \
+ "because it"),
+ ("exec 'hi'", "unqualified exec is not allowed in function 'f' " \
+ "because it")
+ )
+ for line, error in table:
+ input = """def n():
+ x = 4
+ def f():
+ %s
+ return x""" % (line,)
+ exc = py.test.raises(SyntaxError, self.mod_scope, input).value
+ assert exc.msg == error + " is a nested function"
+ input = """def f():
+ %s
+ x = 4
+ def n():
+ return x""" % (line,)
+ exc = py.test.raises(SyntaxError, self.mod_scope, input).value
+ assert exc.msg == error + " contains a nested function with free variables"
+
+ def test_exec(self):
+ scp = self.func_scope("def f(): exec 'hi'")
+ assert not scp.optimized
+ assert isinstance(scp.bare_exec, ast.Exec)
+ assert scp.has_exec
+ for line in ("exec 'hi' in g", "exec 'hi' in g, h"):
+ scp = self.func_scope("def f(): " + line)
+ assert scp.optimized
+ assert scp.bare_exec is None
+ assert scp.has_exec
+
+ def test_yield(self):
+ scp = self.func_scope("def f(): yield x")
+ assert scp.is_generator
+ for input in ("yield x", "class y: yield x"):
+ exc = py.test.raises(SyntaxError, self.mod_scope, "yield x").value
+ assert exc.msg == "yield outside function"
+ for input in ("yield\n return x", "return x\n yield"):
+ input = "def f():\n " + input
+ exc = py.test.raises(SyntaxError, self.func_scope, input).value
+ assert exc.msg == "return with value in generator"
+
+ def test_return(self):
+ for input in ("class x: return", "return"):
+ exc = py.test.raises(SyntaxError, self.func_scope, input).value
+ assert exc.msg == "return outside function"
More information about the Pypy-commit
mailing list