Mercurial > genshi > mirror
changeset 190:769d945ac030 trunk
Improvements for the `py:with` directive:
* One assignment in the same directive can now refer to an earlier assignment.
* Semicolons used in string literals inside the expression are no longer treated as statement separators, and they don't need to be escaped then either.
* Trailing semicolons are now ignored
Many thanks to Oliver Cope for reporting these problems, and providing unit tests and a patch!
author | cmlenz |
---|---|
date | Wed, 23 Aug 2006 14:33:37 +0000 |
parents | 0be9ba422a33 |
children | 3289055a8c32 |
files | markup/template.py markup/tests/template.py |
diffstat | 2 files changed, 59 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/markup/template.py +++ b/markup/template.py @@ -665,18 +665,30 @@ def __init__(self, value, filename=None, lineno=-1, offset=-1): Directive.__init__(self, None, filename, lineno, offset) self.vars = [] + value = value.strip() try: - for stmt in value.split(';'): - name, value = stmt.split('=', 1) - self.vars.append((name.strip(), - Expression(value.strip(), filename, lineno))) + ast = compiler.parse(value, 'exec').node + for node in ast.nodes: + if isinstance(node, compiler.ast.Discard): + continue + elif not isinstance(node, compiler.ast.Assign): + raise TemplateSyntaxError('only assignment allowed in ' + 'value of the "with" directive', + filename, lineno, offset) + self.vars.append(([n.name for n in node.nodes], + Expression(node.expr, filename, lineno))) except SyntaxError, err: + err.msg += ' in expression "%s" of "%s" directive' % (value, + self.tagname) raise TemplateSyntaxError(err, filename, lineno, offset + (err.offset or 0)) def __call__(self, stream, ctxt, directives): - ctxt.push(dict([(name, expr.evaluate(ctxt, nocall=True)) - for name, expr in self.vars])) + frame = {} + ctxt.push(frame) + for names, expr in self.vars: + value = expr.evaluate(ctxt, nocall=True) + frame.update(dict((name, value) for name in names)) for event in _apply_directives(stream, ctxt, directives): yield event ctxt.pop()
--- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -574,6 +574,47 @@ 84 </div>""", str(tmpl.generate(x=42))) + def test_multiple_vars_same_name(self): + tmpl = Template("""<div xmlns:py="http://markup.edgewall.org/"> + <py:with vars=" + foo = 'bar'; + foo = foo.replace('r', 'z') + "> + $foo + </py:with> + </div>""") + self.assertEqual("""<div> + baz + </div>""", str(tmpl.generate(x=42))) + + def test_multiple_vars_single_assignment(self): + tmpl = Template("""<div xmlns:py="http://markup.edgewall.org/"> + <py:with vars="x = y = z = 1">${x} ${y} ${z}</py:with> + </div>""") + self.assertEqual("""<div> + 1 1 1 + </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> + </div>""") + self.assertEqual("""<div> + 84 42 + </div>""", str(tmpl.generate(x=42))) + + def test_semicolon_escape(self): + tmpl = Template("""<div xmlns:py="http://markup.edgewall.org/"> + <py:with vars="x = 'here is a semicolon: ;'; y = 'here are two semicolons: ;;' ;"> + ${x} + ${y} + </py:with> + </div>""") + self.assertEqual("""<div> + here is a semicolon: ; + here are two semicolons: ;; + </div>""", str(tmpl.generate())) + class TemplateTestCase(unittest.TestCase): """Tests for basic template processing, expression evaluation and error