Mercurial > genshi > genshi-test
changeset 220:c62dbea15f36
Fix for #45 and #46: properly support assignment to nested tuples in `py:for` and `py:with` directives.
author | cmlenz |
---|---|
date | Tue, 05 Sep 2006 16:33:13 +0000 |
parents | 0f897d319002 |
children | c448cf114c30 |
files | markup/template.py markup/tests/template.py |
diffstat | 2 files changed, 64 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/markup/template.py +++ b/markup/template.py @@ -113,7 +113,7 @@ self.frames[0][key] = value def _find(self, key, default=None): - """Retrieve a given variable's value and frame it was found in. + """Retrieve a given variable's value and the frame it was found in. Intented for internal use by directives. """ @@ -191,6 +191,23 @@ stream = directives[0](iter(stream), ctxt, directives[1:]) return stream +def _assignment(ast): + """Takes the AST representation of an assignment, and returns a function + that applies the assignment of a given value to a dictionary. + """ + def _names(node): + if isinstance(node, (compiler.ast.AssTuple, compiler.ast.Tuple)): + return tuple([_names(child) for child in node]) + elif isinstance(node, (compiler.ast.AssName, compiler.ast.Name)): + return node.name + def _assign(data, value, names=_names(ast)): + if type(names) is tuple: + for idx in range(len(names)): + _assign(data, value[idx], names[idx]) + else: + data[names] = value + return _assign + class AttrsDirective(Directive): """Implementation of the `py:attrs` template directive. @@ -385,7 +402,7 @@ <li>1</li><li>2</li><li>3</li> </ul> """ - __slots__ = ['targets'] + __slots__ = ['assign'] ATTRIBUTE = 'each' @@ -393,8 +410,9 @@ if ' in ' not in value: raise TemplateSyntaxError('"in" keyword missing in "for" directive', filename, lineno, offset) - targets, value = value.split(' in ', 1) - self.targets = [str(name.strip()) for name in targets.split(',')] + assign, value = value.split(' in ', 1) + ast = compiler.parse(assign, 'exec') + self.assign = _assignment(ast.node.nodes[0].expr) Directive.__init__(self, value.strip(), filename, lineno, offset) def __call__(self, stream, ctxt, directives): @@ -402,16 +420,11 @@ if iterable is None: return + assign = self.assign scope = {} stream = list(stream) - targets = self.targets - single = len(targets) == 1 for item in iter(iterable): - if single: - scope[targets[0]] = item - else: - for idx, name in enumerate(targets): - scope[name] = item[idx] + assign(scope, item) ctxt.push(scope) for event in _apply_directives(stream, ctxt, directives): yield event @@ -691,7 +704,7 @@ raise TemplateSyntaxError('only assignment allowed in ' 'value of the "with" directive', filename, lineno, offset) - self.vars.append(([n.name for n in node.nodes], + self.vars.append(([_assignment(n) for n in node.nodes], Expression(node.expr, filename, lineno))) except SyntaxError, err: err.msg += ' in expression "%s" of "%s" directive' % (value, @@ -702,9 +715,10 @@ def __call__(self, stream, ctxt, directives): frame = {} ctxt.push(frame) - for names, expr in self.vars: + for targets, expr in self.vars: value = expr.evaluate(ctxt, nocall=True) - frame.update(dict([(name, value) for name in names])) + for assign in targets: + assign(frame, value) for event in _apply_directives(stream, ctxt, directives): yield event ctxt.pop()
--- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -381,6 +381,34 @@ <b>5</b> </doc>""", str(tmpl.generate(items=range(1, 6)))) + def test_multi_assignment(self): + """ + Verify that assignment to tuples works correctly. + """ + tmpl = Template("""<doc xmlns:py="http://markup.edgewall.org/"> + <py:for each="k, v in items"> + <p>key=$k, value=$v</p> + </py:for> + </doc>""") + self.assertEqual("""<doc> + <p>key=a, value=1</p> + <p>key=b, value=2</p> + </doc>""", str(tmpl.generate(items=dict(a=1, b=2).items()))) + + def test_nested_assignment(self): + """ + Verify that assignment to nested tuples works correctly. + """ + tmpl = Template("""<doc xmlns:py="http://markup.edgewall.org/"> + <py:for each="idx, (k, v) in items"> + <p>$idx: key=$k, value=$v</p> + </py:for> + </doc>""") + self.assertEqual("""<doc> + <p>0: key=a, value=1</p> + <p>1: key=b, value=2</p> + </doc>""", str(tmpl.generate(items=enumerate(dict(a=1, b=2).items())))) + class IfDirectiveTestCase(unittest.TestCase): """Tests for the `py:if` template directive.""" @@ -710,6 +738,14 @@ 1 1 1 </div>""", str(tmpl.generate(x=42))) + def test_nested_vars_single_assignment(self): + tmpl = Template("""<div xmlns:py="http://markup.edgewall.org/"> + <py:with vars="x, (y, z) = (1, (2, 3))">${x} ${y} ${z}</py:with> + </div>""") + self.assertEqual("""<div> + 1 2 3 + </div>""", str(tmpl.generate(x=42))) + def test_multiple_vars_trailing_semicolon(self): tmpl = Template("""<div xmlns:py="http://markup.edgewall.org/"> <py:with vars="x = x * 2; y = x / 2;">${x} ${y}</py:with>