# HG changeset patch # User cmlenz # Date 1186612046 0 # Node ID 587910938507b9c4448ddca23a73aebaa11e6008 # Parent 60a906b93acd0fcceab1942eb4202f4147413e99 Ported [700] to 0.4.x branch. diff --git a/genshi/template/eval.py b/genshi/template/eval.py --- a/genshi/template/eval.py +++ b/genshi/template/eval.py @@ -688,6 +688,8 @@ return ASTTransformer.visitAugAssign(self, node) def visitClass(self, node): + if self.locals: + self.locals[-1].add(node.name) self.locals.append(set()) node = ASTTransformer.visitClass(self, node) self.locals.pop() @@ -700,6 +702,8 @@ return node def visitFunction(self, node): + if self.locals: + self.locals[-1].add(node.name) self.locals.append(set(node.argnames)) node = ASTTransformer.visitFunction(self, node) self.locals.pop() @@ -726,12 +730,11 @@ def visitName(self, node): # If the name refers to a local inside a lambda, list comprehension, or # generator expression, leave it alone - for frame in self.locals: - if node.name in frame: - return node - # Otherwise, translate the name ref into a context lookup - func_args = [ast.Name('data'), ast.Const(node.name)] - return ast.CallFunc(ast.Name('_lookup_name'), func_args) + if node.name not in flatten(self.locals): + # Otherwise, translate the name ref into a context lookup + func_args = [ast.Name('data'), ast.Const(node.name)] + node = ast.CallFunc(ast.Name('_lookup_name'), func_args) + return node class ExpressionASTTransformer(TemplateASTTransformer): diff --git a/genshi/template/tests/eval.py b/genshi/template/tests/eval.py --- a/genshi/template/tests/eval.py +++ b/genshi/template/tests/eval.py @@ -449,7 +449,8 @@ self.assertEqual(None, data['donothing']()) def test_def_with_multiple_statements(self): - suite = Suite("""def donothing(): + suite = Suite(""" +def donothing(): if True: return foo """) @@ -458,6 +459,35 @@ assert 'donothing' in data self.assertEqual('bar', data['donothing']()) + def test_def_using_nonlocal(self): + suite = Suite(""" +values = [] +def add(value): + if value not in values: + values.append(value) +add('foo') +add('bar') +""") + data = {} + suite.execute(data) + self.assertEqual(['foo', 'bar'], data['values']) + + def test_def_nested(self): + suite = Suite(""" +def doit(): + values = [] + def add(value): + if value not in values: + values.append(value) + add('foo') + add('bar') + return values +x = doit() +""") + data = {} + suite.execute(data) + self.assertEqual(['foo', 'bar'], data['x']) + def test_delete(self): suite = Suite("""foo = 42 del foo @@ -472,6 +502,28 @@ suite.execute(data) assert 'plain' in data + def test_class_in_def(self): + suite = Suite(""" +def create(): + class Foobar(object): + def __str__(self): + return 'foobar' + return Foobar() +x = create() +""") + data = {} + suite.execute(data) + self.assertEqual('foobar', str(data['x'])) + + def test_class_with_methods(self): + suite = Suite("""class plain(object): + def donothing(): + pass +""") + data = {} + suite.execute(data) + assert 'plain' in data + def test_import(self): suite = Suite("from itertools import ifilter") data = {}