# HG changeset patch # User cmlenz # Date 1202300282 0 # Node ID 0653f6c1ffdfb4456349326c3fc1b69a1de276bd # Parent 3e7cd32c941176162a72edee5e4de07b2212f53c Assigning to a variable named `data` in a Python code block no longer breaks context lookup. We now use the name `__data__` for internal data, hoping that that name is not as commonly used in templates. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -44,6 +44,8 @@ so that it is still possible to use the Genshi `escape` function even with text templates. The old behavior is available via the `strip_markup` option of the serializer (ticket #146). + * Assigning to a variable named `data` in a Python code block no longer + breaks context lookup. Version 0.4.4 diff --git a/genshi/template/eval.py b/genshi/template/eval.py --- a/genshi/template/eval.py +++ b/genshi/template/eval.py @@ -140,8 +140,8 @@ """ __traceback_hide__ = 'before_and_this' _globals = self._globals() - _globals['data'] = data - return eval(self.code, _globals, {'data': data}) + _globals['__data__'] = data + return eval(self.code, _globals, {'__data__': data}) class Suite(Code): @@ -162,7 +162,7 @@ """ __traceback_hide__ = 'before_and_this' _globals = self._globals() - _globals['data'] = data + _globals['__data__'] = data exec self.code in _globals, data @@ -678,11 +678,11 @@ if isinstance(node.node, ast.Name) \ and node.node.name not in flatten(self.locals): name = node.node.name - node.node = ast.Subscript(ast.Name('data'), 'OP_APPLY', + node.node = ast.Subscript(ast.Name('__data__'), 'OP_APPLY', [ast.Const(name)]) node.expr = self.visit(node.expr) return ast.If([ - (ast.Compare(ast.Const(name), [('in', ast.Name('data'))]), + (ast.Compare(ast.Const(name), [('in', ast.Name('__data__'))]), ast.Stmt([node]))], ast.Stmt([ast.Raise(ast.CallFunc(ast.Name('UndefinedError'), [ast.Const(name)]), @@ -741,7 +741,7 @@ # generator expression, leave it alone 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)] + func_args = [ast.Name('__data__'), ast.Const(node.name)] node = ast.CallFunc(ast.Name('_lookup_name'), func_args) return node @@ -753,12 +753,12 @@ def visitGetattr(self, node): return ast.CallFunc(ast.Name('_lookup_attr'), [ - ast.Name('data'), self.visit(node.expr), + ast.Name('__data__'), self.visit(node.expr), ast.Const(node.attrname) ]) def visitSubscript(self, node): return ast.CallFunc(ast.Name('_lookup_item'), [ - ast.Name('data'), self.visit(node.expr), + ast.Name('__data__'), self.visit(node.expr), ast.Tuple([self.visit(sub) for sub in node.subs]) ]) 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 @@ -438,6 +438,19 @@ class SuiteTestCase(unittest.TestCase): + def test_internal_shadowing(self): + # The context itself is stored in the global execution scope of a suite + # It used to get stored under the name 'data', which meant the + # following test would fail, as the user defined 'data' variable + # shadowed the Genshi one. We now use the name '__data__' to avoid + # conflicts + suite = Suite("""data = [] +bar = foo +""") + data = {'foo': 42} + suite.execute(data) + self.assertEqual(42, data['bar']) + def test_assign(self): suite = Suite("foo = 42") data = {}