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
Copyright (C) 2012-2017 Edgewall Software