[Checkins] SVN: z3c.pt/trunk/ Implemented tuple-unpack in tal:define statement.

Malthe Borch mborch at gmail.com
Mon Dec 3 12:35:51 EST 2007


Log message for revision 82102:
  Implemented tuple-unpack in tal:define statement.

Changed:
  U   z3c.pt/trunk/README.txt
  U   z3c.pt/trunk/setup.py
  U   z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
  U   z3c.pt/trunk/z3c/pt/pagetemplate.py
  U   z3c.pt/trunk/z3c/pt/tal.py
  U   z3c.pt/trunk/z3c/pt/tal.txt

-=-
Modified: z3c.pt/trunk/README.txt
===================================================================
--- z3c.pt/trunk/README.txt	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/README.txt	2007-12-03 17:35:50 UTC (rev 82102)
@@ -7,7 +7,29 @@
 In a nutshell:
 
   - Templates are JIT-compiled
-  - Only python-expressions are supported
+  - Only Python-expressions are supported
   - Depends only on lxml
 
 The METAL macro language is not supported; i18n is on the to-do.
+
+Template language
+-----------------
+
+The template language is based loosely on the TAL 1.4 specification
+found here:
+
+  * http://wiki.zope.org/ZPT/TALSpecification14
+  
+1. Only Python-expressions are allowed. Expressions can have
+   try-except fallbacks using the vertical bar syntax:
+
+      tal:content="<expression> | <first fallback> | <second fallback> | ..."
+
+2. Tuples are allowed in the tal:define statement:
+
+      tal:define="(a, b, c) [1, 2, 3]"
+
+3. Generators are allowed in tal:repeat statements. Note that the
+   repeat variable is not available in this case.
+
+      tal:repeat="i <some generator>"

Modified: z3c.pt/trunk/setup.py
===================================================================
--- z3c.pt/trunk/setup.py	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/setup.py	2007-12-03 17:35:50 UTC (rev 82102)
@@ -1,7 +1,7 @@
 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.1'
+version = '0.2'
 
 setup(name='z3c.pt',
       version=version,

Modified: z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/BENCHMARKS.txt	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/z3c/pt/BENCHMARKS.txt	2007-12-03 17:35:50 UTC (rev 82102)
@@ -3,7 +3,7 @@
 
                   zope.pagetemplate     z3c.pt
 Hello World       4.482                 1     
-1000 x 10 table   4.540                 1
+1000 x 10 table   4.513                 1
 
 Source
 ------
@@ -42,7 +42,7 @@
   ...   </tr>
   ... </table>""")
 
-  >>> # for i in range(20): a = template(table=table)
+  >>> for i in range(20): a = template(table=table)
   
   >>> template = z3PageTemplate()
   >>> template.pt_edit("""\

Modified: z3c.pt/trunk/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/z3c/pt/pagetemplate.py	2007-12-03 17:35:50 UTC (rev 82102)
@@ -33,6 +33,8 @@
 
         stream.write("def render(**_kwargs):")
         stream.indent()
+
+        # globalize imports and set up process variables
         stream.write("global _StringIO, _repeatdict, _escape")
         stream.write("repeat = _repeatdict()")
         stream.write("_attrs = {}")

Modified: z3c.pt/trunk/z3c/pt/tal.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.py	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/z3c/pt/tal.py	2007-12-03 17:35:50 UTC (rev 82102)
@@ -1,6 +1,7 @@
 import parser
 import cgi
 import xml.sax.saxutils
+import itertools
 
 def expression(string):
     """
@@ -42,6 +43,18 @@
 
     return expression
 
+def variable(string):
+    for var in string.split(', '):
+        var = var.strip()
+
+        if var in ('repeat',):
+            raise ValueError, "Invalid variable name '%s' (reserved)." % variable
+
+        if var.startswith('_'):
+            raise ValueError, \
+                  "Invalid variable name '%s' (starts with an underscore)." % variable            
+        yield var
+    
 def definition(string):
     """
     TAL define-expression:
@@ -62,19 +75,19 @@
         while string[i] == ' ':
             i += 1
 
-        # get variable name
-        j = string.find(' ', i + 1)
-        if j == -1:
-            raise ValueError, "Invalid define clause (%s)." % string
+        # get variable definition
+        if string[i] == '(':
+            j = string.find(')', i+1)
+            if j == -1:
+                raise ValueError, "Invalid variable tuple definition (%s)." % string
+            var = variable(string[i+1:j])
+            j += 1
+        else:
+            j = string.find(' ', i + 1)
+            if j == -1:
+                raise ValueError, "Invalid define clause (%s)." % string
+            var = variable(string[i:j])
 
-        variable = string[i:j]
-
-        if variable in ('repeat',):
-            raise ValueError, "Invalid variable name '%s' (reserved)." % variable
-
-        if variable.startswith('_'):
-            raise ValueError, \
-                  "Invalid variable name '%s' (starts with an underscore)." % variable            
         # get expression
         i = j
         while j < len(string):
@@ -90,7 +103,7 @@
         else:
             raise e
 
-        defines.append((variable, expr))
+        defines.append((list(var), expr))
 
         i = j + 1
 
@@ -164,28 +177,34 @@
 class Define(object):    
     def __init__(self, value):
         self.defines = [(v, Assign(e)) for v, e in definition(value)]
+        self.variables = list(itertools.chain(*[v for (v, e) in self.defines]))
 
+        if '' in self.variables:
+            import pdb; pdb.set_trace()
+            
     def update(self, node):
         return node
             
     def begin(self, stream):
-        variables = [v for (v, e) in self.defines]
-
         # save local variables already in scope
         save = stream.save()
         stream.write("%s = {}" % save)
-        for var in variables:
+
+        for var in self.variables:
             stream.write("if '%s' in _scope: %s['%s'] = %s" % (var, save, var, var))
             stream.write("else: _scope.append('%s')" % var)
         
-        for variable, assign in self.defines:
-            assign.begin(stream, variable)
+        for variables, assign in self.defines:
+            if len(variables) == 1:
+                assign.begin(stream, variables[0])
+            else:
+                assign.begin(stream, u"(%s,)" % ", ".join(variables))
             assign.end(stream)
         
     def end(self, stream):
         restore = stream.restore()
 
-        for variable, expression in reversed(self.defines):
+        for variable in reversed(self.variables):
             # restore local variables previously in scope
             stream.write("if '%s' in %s:" % (variable, restore))
             stream.indent()
@@ -258,21 +277,22 @@
         
 class Attribute(object):
     def __init__(self, value):
-        self.attributes = definition(value)
+        self.attributes = [(v, Assign(e)) for v, e in definition(value)]
 
     def update(self, node):
-        for variable, expression in self.attributes:
-            if variable in node.attrib:
-                del node.attrib[variable]        
+        for variables, expression in self.attributes:
+            for variable in variables:
+                if variable in node.attrib:
+                    del node.attrib[variable]
 
         return node
     
     def begin(self, stream):
         stream.write("_attrs = {}")
-        for variable, expression in self.attributes:
-            assign = Assign(expression)
-            assign.begin(stream, "_attrs['%s']" % variable)
-            assign.end(stream)
+        for variables, assign in self.attributes:
+            for variable in variables:
+                assign.begin(stream, "_attrs['%s']" % variable)
+                assign.end(stream)
 
     def end(self, stream):
         pass

Modified: z3c.pt/trunk/z3c/pt/tal.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.txt	2007-12-03 17:09:37 UTC (rev 82101)
+++ z3c.pt/trunk/z3c/pt/tal.txt	2007-12-03 17:35:50 UTC (rev 82102)
@@ -77,6 +77,23 @@
   >>> b is not None and d is not None
   True
 
+Tuple assignments:
+
+  >>> define = tal.Define("(e, f) [1, 2]")
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> e == 1 and f == 2
+  True
+  >>> define.end(stream)
+
+Verify scope is preserved on tuple assignment:
+
+  >>> e = None; f = None
+  >>> _scope.append('e'); _scope.append('f')
+  >>> exec stream.getvalue()
+  >>> e is None and f is None
+  True
+  
 Using semicolons in expressions within a define:
 
   >>> define = tal.Define("a ';'")



More information about the Checkins mailing list