[Checkins] SVN: z3c.pt/tags/0.2/ Tagging release.

Malthe Borch mborch at gmail.com
Wed Dec 5 11:25:24 EST 2007


Log message for revision 82142:
  Tagging release.

Changed:
  A   z3c.pt/tags/0.2/
  D   z3c.pt/tags/0.2/README.txt
  A   z3c.pt/tags/0.2/README.txt
  D   z3c.pt/tags/0.2/setup.py
  A   z3c.pt/tags/0.2/setup.py
  D   z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt
  A   z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt
  D   z3c.pt/tags/0.2/z3c/pt/README.txt
  A   z3c.pt/tags/0.2/z3c/pt/README.txt
  D   z3c.pt/tags/0.2/z3c/pt/VERSION.txt
  A   z3c.pt/tags/0.2/z3c/pt/VERSION.txt
  A   z3c.pt/tags/0.2/z3c/pt/codegen.py
  A   z3c.pt/tags/0.2/z3c/pt/codegen.txt
  D   z3c.pt/tags/0.2/z3c/pt/io.py
  A   z3c.pt/tags/0.2/z3c/pt/io.py
  D   z3c.pt/tags/0.2/z3c/pt/pagetemplate.py
  A   z3c.pt/tags/0.2/z3c/pt/pagetemplate.py
  D   z3c.pt/tags/0.2/z3c/pt/tal.py
  A   z3c.pt/tags/0.2/z3c/pt/tal.py
  D   z3c.pt/tags/0.2/z3c/pt/tal.txt
  A   z3c.pt/tags/0.2/z3c/pt/tal.txt
  D   z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py
  A   z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py
  A   z3c.pt/tags/0.2/z3c/pt/transformer.py
  D   z3c.pt/tags/0.2/z3c/pt/translate.txt
  A   z3c.pt/tags/0.2/z3c/pt/translation.py
  A   z3c.pt/tags/0.2/z3c/pt/translation.txt

-=-
Copied: z3c.pt/tags/0.2 (from rev 82100, z3c.pt/trunk)

Deleted: z3c.pt/tags/0.2/README.txt
===================================================================
--- z3c.pt/trunk/README.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/README.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,13 +0,0 @@
-Overview
---------
-
-The z3c.pt package provides an alternative implementation of the TAL
-template language.
-
-In a nutshell:
-
-  - Templates are JIT-compiled
-  - Only python-expressions are supported
-  - Depends only on lxml
-
-The METAL macro language is not supported; i18n is on the to-do.

Copied: z3c.pt/tags/0.2/README.txt (from rev 82141, z3c.pt/trunk/README.txt)
===================================================================
--- z3c.pt/tags/0.2/README.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/README.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,42 @@
+Overview
+--------
+
+The z3c.pt package provides an alternative implementation of the TAL
+template language.
+
+In a nutshell:
+
+  - Templates are JIT-compiled
+  - Only Python-expressions are supported
+  - Depends only on lxml
+
+The METAL macro language is not supported; i18n is on the to-do.
+
+Template and expression language
+--------------------------------
+
+The template and expression language is based loosely on the TAL 1.4
+specification*. Some notable changes:
+
+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>"
+
+4. Attribute-access to dictionary entries is allowed, e.g.
+
+      dictionary.key
+
+   can be used instead of ``dictionary['key']``.
+
+*) http://wiki.zope.org/ZPT/TALSpecification14
+  

Deleted: z3c.pt/tags/0.2/setup.py
===================================================================
--- z3c.pt/trunk/setup.py	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/setup.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,32 +0,0 @@
-from setuptools import setup, find_packages
-import sys, os
-
-version = '0.1'
-
-setup(name='z3c.pt',
-      version=version,
-      description="An implementation of the TAL template language.",
-      long_description=open("README.txt").read(),
-      classifiers=[
-        "Programming Language :: Python",
-        "Topic :: Text Processing :: Markup :: HTML",
-        "Topic :: Text Processing :: Markup :: XML",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-        ],
-      keywords='',
-      author='Malthe Borch',
-      author_email='mborch at gmail.com',
-      url='',
-      license='GPL',
-      packages=find_packages(exclude=['ez_setup']),
-      namespace_packages=['z3c'],
-      include_package_data=True,
-      zip_safe=False,
-      install_requires=[
-          'setuptools',
-          # -*- Extra requirements: -*-
-      ],
-      entry_points="""
-      # -*- Entry points: -*-
-      """,
-      )

Copied: z3c.pt/tags/0.2/setup.py (from rev 82141, z3c.pt/trunk/setup.py)
===================================================================
--- z3c.pt/tags/0.2/setup.py	                        (rev 0)
+++ z3c.pt/tags/0.2/setup.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,33 @@
+from setuptools import setup, find_packages
+import sys, os
+
+version = '0.2'
+
+setup(name='z3c.pt',
+      version=version,
+      description="An implementation of the TAL template language.",
+      long_description=open("README.txt").read(),
+      classifiers=[
+        "Programming Language :: Python",
+        "Topic :: Text Processing :: Markup :: HTML",
+        "Topic :: Text Processing :: Markup :: XML",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        ],
+      keywords='',
+      author='Malthe Borch',
+      author_email='mborch at gmail.com',
+      url='',
+      license='GPL',
+      packages=find_packages(exclude=['ez_setup']),
+      namespace_packages=['z3c'],
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=[
+          'setuptools',
+          'lxml',
+          # -*- Extra requirements: -*-
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )

Deleted: z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/BENCHMARKS.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,70 +0,0 @@
-Benchmarks
-==========
-
-                  zope.pagetemplate     z3c.pt
-Hello World       4.482                 1     
-1000 x 10 table   4.540                 1
-
-Source
-------
-
-  >>> from z3c.pt import PageTemplate
-  >>> from zope.pagetemplate.pagetemplate import PageTemplate as z3PageTemplate
-
-Hello World:
-
-  >>> template = PageTemplate("""\
-  ... <div xmlns="http://www.w3.org/1999/xhtml">
-  ...   Hello World!
-  ... </div>""")
-
-  >>> # for i in range(300000): a = template()
-
-  >>> template = z3PageTemplate()
-  >>> template.pt_edit("""\
-  ... <div xmlns="http://www.w3.org/1999/xhtml">
-  ...   Hello World!
-  ... </div>""", 'text/xhtml')
-
-  >>> # for i in range(300000): a = template()  
-
-1000 x 10 table:
-  
-  >>> table = [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) \
-  ...          for x in range(1000)]
-
-  >>> template = PageTemplate("""\
-  ... <table xmlns="http://www.w3.org/1999/xhtml"
-  ...        xmlns:tal="http://xml.zope.org/namespaces/tal">
-  ...   <tr tal:repeat="row table">
-  ...      <td tal:repeat="c row.values()"
-  ...          tal:content="c" />
-  ...   </tr>
-  ... </table>""")
-
-  >>> # for i in range(20): a = template(table=table)
-  
-  >>> template = z3PageTemplate()
-  >>> template.pt_edit("""\
-  ... <table xmlns="http://www.w3.org/1999/xhtml"
-  ...        xmlns:tal="http://xml.zope.org/namespaces/tal">
-  ...   <tr tal:repeat="row options/table">
-  ...      <td tal:repeat="c python: row.values()"
-  ...          tal:content="c" />
-  ...   </tr>
-  ... </table>""", 'text/xhtml')
-
-  >>> # for i in range(20): a = template(table=table)
-
-  >>> from StringIO import StringIO
-  >>> def bigtable(table):
-  ...   out = StringIO()
-  ...   for row in table:
-  ...      out.write('<tr>')
-  ...      for c in row.values():
-  ...         out.write('<td>%s</td>' % c)
-  ...      out.write('</tr>')
-  ...   return out.getvalue()
-  
-  >>> # for i in range(20): a = bigtable(table=table)
-

Copied: z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt (from rev 82102, z3c.pt/trunk/z3c/pt/BENCHMARKS.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/BENCHMARKS.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,70 @@
+Benchmarks
+==========
+
+                  zope.pagetemplate     z3c.pt
+Hello World       4.482                 1     
+1000 x 10 table   4.513                 1
+
+Source
+------
+
+  >>> from z3c.pt import PageTemplate
+  >>> from zope.pagetemplate.pagetemplate import PageTemplate as z3PageTemplate
+
+Hello World:
+
+  >>> template = PageTemplate("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   Hello World!
+  ... </div>""")
+
+  >>> # for i in range(300000): a = template()
+
+  >>> template = z3PageTemplate()
+  >>> template.pt_edit("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   Hello World!
+  ... </div>""", 'text/xhtml')
+
+  >>> # for i in range(300000): a = template()  
+
+1000 x 10 table:
+  
+  >>> table = [dict(a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10) \
+  ...          for x in range(1000)]
+
+  >>> template = PageTemplate("""\
+  ... <table xmlns="http://www.w3.org/1999/xhtml"
+  ...        xmlns:tal="http://xml.zope.org/namespaces/tal">
+  ...   <tr tal:repeat="row table">
+  ...      <td tal:repeat="c row.values()"
+  ...          tal:content="c" />
+  ...   </tr>
+  ... </table>""")
+
+  >>> for i in range(20): a = template(table=table)
+  
+  >>> template = z3PageTemplate()
+  >>> template.pt_edit("""\
+  ... <table xmlns="http://www.w3.org/1999/xhtml"
+  ...        xmlns:tal="http://xml.zope.org/namespaces/tal">
+  ...   <tr tal:repeat="row options/table">
+  ...      <td tal:repeat="c python: row.values()"
+  ...          tal:content="c" />
+  ...   </tr>
+  ... </table>""", 'text/xhtml')
+
+  >>> # for i in range(20): a = template(table=table)
+
+  >>> from StringIO import StringIO
+  >>> def bigtable(table):
+  ...   out = StringIO()
+  ...   for row in table:
+  ...      out.write('<tr>')
+  ...      for c in row.values():
+  ...         out.write('<td>%s</td>' % c)
+  ...      out.write('</tr>')
+  ...   return out.getvalue()
+  
+  >>> # for i in range(20): a = bigtable(table=table)
+

Deleted: z3c.pt/tags/0.2/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/README.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,34 +0,0 @@
-z3c.pt
-======
-
-Usage
------
-
-From a string:
-
-  >>> from z3c.pt import PageTemplate
-  >>> template = PageTemplate("""\
-  ... <div xmlns="http://www.w3.org/1999/xhtml"
-  ...      xmlns:tal="http://xml.zope.org/namespaces/tal">
-  ...   Hello World!
-  ... </div>
-  ... """)
-
-  >>> print template()
-  <div>
-     Hello World!
-  </div>
-
-From a file:
-
-  >>> from z3c.pt import PageTemplateFile
-  >>> from z3c.pt import tests
-  >>> filename = tests.__path__[0]+'/helloworld.pt'
-  
-  >>> template = PageTemplateFile(filename)
-  >>> print template()
-  <div>
-     Hello World!
-  </div>
-
-Keyword-parameters are passed on to the template namespace as-is.

Copied: z3c.pt/tags/0.2/z3c/pt/README.txt (from rev 82141, z3c.pt/trunk/z3c/pt/README.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/README.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/README.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,34 @@
+z3c.pt
+======
+
+Usage
+-----
+
+From a string:
+
+  >>> from z3c.pt import PageTemplate
+  >>> template = PageTemplate("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:tal="http://xml.zope.org/namespaces/tal/python">
+  ...   Hello World!
+  ... </div>
+  ... """)
+
+  >>> print template()
+  <div>
+     Hello World!
+  </div>
+
+From a file:
+
+  >>> from z3c.pt import PageTemplateFile
+  >>> from z3c.pt import tests
+  >>> filename = tests.__path__[0]+'/helloworld.pt'
+  
+  >>> template = PageTemplateFile(filename)
+  >>> print template()
+  <div>
+     Hello World!
+  </div>
+
+Keyword-parameters are passed on to the template namespace as-is.

Deleted: z3c.pt/tags/0.2/z3c/pt/VERSION.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/VERSION.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/VERSION.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1 +0,0 @@
-0.1

Copied: z3c.pt/tags/0.2/z3c/pt/VERSION.txt (from rev 82141, z3c.pt/trunk/z3c/pt/VERSION.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/VERSION.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/VERSION.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1 @@
+0.2

Copied: z3c.pt/tags/0.2/z3c/pt/codegen.py (from rev 82141, z3c.pt/trunk/z3c/pt/codegen.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/codegen.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/codegen.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,63 @@
+from compiler import ast, parse
+from compiler.pycodegen import ModuleCodeGenerator
+
+from transformer import ASTTransformer
+
+marker = object()
+
+CONSTANTS = frozenset(['False', 'True', 'None', 'NotImplemented', 'Ellipsis'])
+
+class TemplateASTTransformer(ASTTransformer):
+    def __init__(self):
+        self.locals = [CONSTANTS]
+        
+    def visitGetattr(self, node):
+        """
+        Allow fallback to dictionary lookup if attribute does not exist.
+
+        Variables starting with an underscore are exempt.
+
+        """
+        
+        if hasattr(node.expr, 'name') and node.expr.name.startswith('_'):
+            return ast.Getattr(node.expr, node.attrname)
+
+        expr = self.visit(node.expr)
+        name = ast.Const(node.attrname)
+
+        # hasattr(obj, key) and getattr(obj, key) or not
+        # hasattr(obj, key) and obj[key]
+        return ast.Or([ast.And(
+            [ast.CallFunc(ast.Name('hasattr'), [expr, name], None, None),
+             ast.CallFunc(ast.Name('getattr'), [expr, name], None, None)]),
+                    ast.And([
+            ast.Not(ast.CallFunc(ast.Name('hasattr'), [expr, name], None, None)),
+            ast.Subscript(expr, 'OP_APPLY', [name])])])
+    
+class Suite(object):
+    __slots__ = ['code']
+
+    xform = TemplateASTTransformer
+    mode = 'exec'
+    
+    def __init__(self, source):
+        """Create the code object from a string."""
+        
+        node = parse(source, self.mode)
+
+        # build tree
+        transform = self.xform()
+        tree = transform.visit(node)
+        filename = tree.filename = '<script>'
+
+        # generate code
+        gen = ModuleCodeGenerator(tree)
+        gen.optimized = True
+
+        self.code = gen.getCode()
+        
+    def __hash__(self):
+        return hash(self.code)
+
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, self.source)

Copied: z3c.pt/tags/0.2/z3c/pt/codegen.txt (from rev 82141, z3c.pt/trunk/z3c/pt/codegen.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/codegen.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/codegen.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,31 @@
+Codegen
+=======
+
+The ``Codegen`` module is responsible for the low-level compilation
+of the page template.
+
+Suite
+-----
+
+The ``Suite`` class compiles a source code suite and makes a code
+object available.
+
+  >>> from z3c.pt.codegen import Suite
+  >>> suite = Suite("""\
+  ... print 'Hello World!'
+  ... """)
+  >>> exec suite.code
+  Hello World!
+
+AST transformations
+-------------------
+  
+We allow attribute access to dictionary entries to minimize verbosity
+in templates. It works by wrapping the get attribute nodes in a method
+that tries a dictionary lookup if attribute lookup failed.
+
+  >>> suite = Suite("""\
+  ... a = {'b': 1}
+  ... assert a['b'] == a.b
+  ... """)
+  >>> exec suite.code

Deleted: z3c.pt/tags/0.2/z3c/pt/io.py
===================================================================
--- z3c.pt/trunk/z3c/pt/io.py	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/io.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,58 +0,0 @@
-from StringIO import StringIO
-
-class CodeIO(StringIO):
-    """
-    A high-level I/O class to write Python code to a stream.
-    Indentation is managed using ``indent`` and ``outdent``.
-
-    Convenience methods for keeping track of temporary
-    variables.
-    
-    """
-
-    variable_prefix = '_saved'
-    
-    def __init__(self, indentation_string="\t"):
-        StringIO.__init__(self)
-        self.indentation = 0
-        self.indentation_string = indentation_string
-        self.counter = 0
-        self.queue = u''
-    
-    def save(self, variable=None):
-        self.counter += 1
-        if variable:
-            self.write("%s%d = %s" % (self.variable_prefix, self.counter, variable))
-        else:
-            return "%s%d" % (self.variable_prefix, self.counter)
-        
-    def restore(self, variable=None):
-        if variable:
-            self.write("%s = %s%d" % (variable, self.variable_prefix, self.counter))
-        else:
-            return "%s%d" % (self.variable_prefix, self.counter)
-        
-        self.counter -= 1
-        
-    def indent(self, amount=1):
-        self.indentation += amount
-
-    def outdent(self, amount=1):
-        self.indentation -= amount
-
-    def out(self, string):
-        self.queue += string
-
-    def cook(self):
-        if self.queue:
-            queue = self.queue
-            self.queue = ''
-            self.write("_out.write('%s')" % queue)
-        
-    def write(self, string):
-        self.cook()
-        StringIO.write(self, self.indentation_string * self.indentation + string + '\n')
-
-    def getvalue(self):
-        self.cook()
-        return StringIO.getvalue(self)

Copied: z3c.pt/tags/0.2/z3c/pt/io.py (from rev 82141, z3c.pt/trunk/z3c/pt/io.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/io.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/io.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,58 @@
+from StringIO import StringIO
+
+class CodeIO(StringIO):
+    """
+    A high-level I/O class to write Python code to a stream.
+    Indentation is managed using ``indent`` and ``outdent``.
+
+    Convenience methods for keeping track of temporary
+    variables.
+    
+    """
+
+    variable_prefix = '_saved'
+    
+    def __init__(self, indentation=0, indentation_string="\t"):
+        StringIO.__init__(self)
+        self.indentation = indentation
+        self.indentation_string = indentation_string
+        self.counter = 0
+        self.queue = u''
+    
+    def save(self, variable=None):
+        self.counter += 1
+        if variable:
+            self.write("%s%d = %s" % (self.variable_prefix, self.counter, variable))
+        else:
+            return "%s%d" % (self.variable_prefix, self.counter)
+        
+    def restore(self, variable=None):
+        if variable:
+            self.write("%s = %s%d" % (variable, self.variable_prefix, self.counter))
+        else:
+            return "%s%d" % (self.variable_prefix, self.counter)
+        
+        self.counter -= 1
+        
+    def indent(self, amount=1):
+        self.indentation += amount
+
+    def outdent(self, amount=1):
+        self.indentation -= amount
+
+    def out(self, string):
+        self.queue += string
+
+    def cook(self):
+        if self.queue:
+            queue = self.queue
+            self.queue = ''
+            self.write("_out.write('%s')" % queue)
+        
+    def write(self, string):
+        self.cook()
+        StringIO.write(self, self.indentation_string * self.indentation + string + '\n')
+
+    def getvalue(self):
+        self.cook()
+        return StringIO.getvalue(self)

Deleted: z3c.pt/tags/0.2/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/pagetemplate.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,108 +0,0 @@
-import lxml.etree
-import StringIO
-
-import tal
-import io
-
-ns_lookup_table = [(f.__ns__, f) for f in (tal.define,
-                                           tal.condition,
-                                           tal.omit,
-                                           tal.repeat,
-                                           tal.attribute,
-                                           tal.replace,
-                                           tal.tag,
-                                           tal.content)]
-                                                 
-class PageTemplate(object):
-    def __init__(self, body):
-        self.body = body
-        self.render = None
-        
-    def translate(self):
-        """Translates page template body to Python-code."""
-
-        tree = lxml.etree.parse(StringIO.StringIO(self.body))
-        root = tree.getroot()
-
-        stream = io.CodeIO(indentation_string='  ')
-
-        # imports and symbols
-        stream.write("from cgi import escape as _escape")
-        stream.write("from z3c.pt.tal import repeatdict as _repeatdict")
-        stream.write("from StringIO import StringIO as _StringIO")
-
-        stream.write("def render(**_kwargs):")
-        stream.indent()
-        stream.write("global _StringIO, _repeatdict, _escape")
-        stream.write("repeat = _repeatdict()")
-        stream.write("_attrs = {}")
-        stream.write("_scope = _kwargs.keys()")
-        
-        # output
-        stream.write("_out = _StringIO()")
-        
-        # set up keyword args
-        stream.write("for _variable, _value in _kwargs.items():")
-        stream.indent()
-        stream.write("globals()[_variable] = _value")
-        stream.outdent()
-
-        # translate tree
-        translate(root, stream)
-        
-        # output
-        stream.write("return _out.getvalue()")
-        stream.outdent()
-        
-        return stream.getvalue()
-
-    def cook(self):
-        exec self.translate()
-        self.render = render
-        
-    @property
-    def template(self):
-        if self.render is None:
-            self.cook()
-
-        return self.render
-        
-    def __call__(self, **kwargs):
-        return self.template(**kwargs)
-
-class PageTemplateFile(PageTemplate):
-    def __init__(self, filename):
-        self.filename = filename
-        self.render = None
-
-    @property
-    def body(self):
-        return open(self.filename, 'r').read()
-
-def translate(node, stream):
-    keys = node.keys() + [None]
-    
-    handlers = [handler(node) for key, handler in ns_lookup_table \
-                if key in keys]
-
-    # remove namespace attributes
-    for key, handler in ns_lookup_table:
-        if key is not None and key in node.attrib:
-            del node.attrib[key]
-
-    # update node
-    for handler in handlers:
-        node = handler.update(node)
-
-    # begin tag
-    for handler in handlers:
-        handler.begin(stream)
-
-    # process children
-    if node:
-        for element in node:
-            translate(element, stream)
-
-    # end tag
-    for handler in reversed(handlers):
-        handler.end(stream)

Copied: z3c.pt/tags/0.2/z3c/pt/pagetemplate.py (from rev 82141, z3c.pt/trunk/z3c/pt/pagetemplate.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/pagetemplate.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/pagetemplate.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,36 @@
+import translation
+import codegen
+                                                 
+class PageTemplate(object):
+    def __init__(self, body):
+        self.body = body
+        self.render = None
+        
+    def cook(self):
+        source, _globals = translation.translate(self.body)
+        suite = codegen.Suite(source)
+
+        _locals = {}
+
+        exec suite.code in _globals, _locals
+
+        self.render = _locals['render']
+
+    @property
+    def template(self):
+        if self.render is None:
+            self.cook()
+
+        return self.render
+        
+    def __call__(self, **kwargs):
+        return self.template(**kwargs)
+
+class PageTemplateFile(PageTemplate):
+    def __init__(self, filename):
+        self.filename = filename
+        self.render = None
+
+    @property
+    def body(self):
+        return open(self.filename, 'r').read()

Deleted: z3c.pt/tags/0.2/z3c/pt/tal.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.py	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/tal.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,389 +0,0 @@
-import parser
-import cgi
-import xml.sax.saxutils
-
-def expression(string):
-    """
-    Python-expressions in try-except construct.
-    
-    Specification:
-
-    expression :: = python_expression [ |* expression ]
-    python_expresion ::= a python expression string
-
-    *) Using | as logical or is not supported.
-
-    """
-
-    string = string.replace('\n', '')
-
-    expression = []
-
-    i = j = 0
-    while i < len(string):
-        j = string.find('|', j + 1)
-        if j == -1:
-            j = len(string)
-
-        expr = string[i:j].lstrip()
-
-        try:
-            # we use the ``parser`` module to determine if
-            # an expression is a valid python expression
-            parser.expr(expr)
-        except SyntaxError, e:
-            if j < len(string):
-                continue
-
-            raise e
-
-        expression.append(expr)
-        i = j + 1
-
-    return expression
-
-def definition(string):
-    """
-    TAL define-expression:
-
-    Specification:
-
-    argument ::= define_var [';' define_var]
-    define_var ::= Name python_expression
-
-    """
-
-    string = string.replace('\n', '').strip()
-
-    defines = []
-
-    i = 0
-    while i < len(string):
-        while string[i] == ' ':
-            i += 1
-
-        # get variable name
-        j = string.find(' ', i + 1)
-        if j == -1:
-            raise ValueError, "Invalid define clause (%s)." % string
-
-        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):
-            j = string.find(';', j+1)
-            if j == -1:
-                j = len(string)
-
-            try:
-                expr = expression(string[i:j])
-            except SyntaxError, e:
-                continue
-            break
-        else:
-            raise e
-
-        defines.append((variable, expr))
-
-        i = j + 1
-
-    return defines
-
-class repeatitem(object):
-    def __init__(self, iterator, length):
-        self.length = length
-        self.iterator = iterator
-        
-    @property
-    def index(self):
-        return self.length - len(self.iterator) - 1
-        
-    @property
-    def start(self):
-        return self.index == 0
-
-    @property
-    def end(self):
-        return self.index == self.length - 1
-
-    def number(self):
-        return self.index + 1
-
-    def odd(self):
-        return bool(self.index % 2)
-
-    def even(self):
-        return not self.odd()
-
-class repeatdict(dict):
-    def __setitem__(self, key, iterator):
-        try:
-            length = len(iterator)
-        except TypeError:
-            length = None
-            
-        dict.__setitem__(self, key, (iterator, length))
-        
-    def __getitem__(self, key):
-        value, length = dict.__getitem__(self, key)
-
-        if not isinstance(value, repeatitem):
-            value = repeatitem(value, length)
-            self.__setitem__(key, value)
-
-        return value
-    
-class Assign(object):
-    def __init__(self, expression):
-        self.expressions = expression
-            
-    def begin(self, stream, variable):
-        """First n - 1 expressions must be try-except wrapped."""
-
-        for expression in self.expressions[:-1]:
-            stream.write("try:")
-            stream.indent()
-            stream.write("%s = %s" % (variable, expression))
-            stream.outdent()
-            stream.write("except Exception, e:")
-            stream.indent()
-
-        expression = self.expressions[-1]
-        stream.write("%s = %s" % (variable, expression))
-
-    def end(self, stream):
-        stream.outdent(len(self.expressions)-1)
-
-class Define(object):    
-    def __init__(self, value):
-        self.defines = [(v, Assign(e)) for v, e in definition(value)]
-
-    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:
-            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)
-            assign.end(stream)
-        
-    def end(self, stream):
-        restore = stream.restore()
-
-        for variable, expression in reversed(self.defines):
-            # restore local variables previously in scope
-            stream.write("if '%s' in %s:" % (variable, restore))
-            stream.indent()
-            stream.write("%s = %s['%s']" % (variable, restore, variable))
-            stream.outdent()
-            stream.write("else:")
-            stream.indent()
-            stream.write("del %s" % variable)
-            stream.write("_scope.remove('%s')" % variable)
-            stream.outdent()
-            
-class Condition(object):
-    def __init__(self, value):
-        self.assign = Assign(expression(value))
-
-    def update(self, node):
-        return node
-
-    def begin(self, stream):
-        variable = '_condition'
-
-        self.assign.begin(stream, variable)
-        stream.write("if %s:" % variable)
-        stream.indent()
-
-    def end(self, stream):
-        self.assign.end(stream)
-        stream.outdent()
-
-class Repeat(object):
-    def __init__(self, value):
-        string = value.lstrip().replace('\n', '')
-
-        space = string.find(' ')
-        if space == -1:
-            raise ValueError, "Invalid repeat clause (%s)." % value
-
-        self.variable = string[:space]
-        self.assign = Assign(expression(string[space:]))
-        
-    def update(self, node):
-        return node
-
-    def begin(self, stream):
-        variable = self.variable
-        iterator = stream.save()
-        
-        self.assign.begin(stream, iterator)
-
-        # define variable scope
-        self.define = Define("%s None" % self.variable)
-        self.define.begin(stream)
-
-        # initialize iterator
-        stream.write("repeat['%s'] = %s = %s.__iter__()" % (variable, iterator, iterator))
-
-        # loop
-        stream.write("while %s:" % iterator)
-        stream.indent()
-        stream.write("%s = %s.next()" % (variable, iterator))
-        
-    def end(self, stream):
-        # cook before leaving loop
-        stream.cook()
-        
-        stream.outdent()
-        self.define.end(stream)
-        self.assign.end(stream)
-        stream.restore()
-        
-class Attribute(object):
-    def __init__(self, value):
-        self.attributes = definition(value)
-
-    def update(self, node):
-        for variable, expression in self.attributes:
-            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)
-
-    def end(self, stream):
-        pass
-
-class Content(object):
-    def __init__(self, value):
-        self.assign = Assign(expression(value))
-
-    def update(self, node):
-        node.text = ''
-        for element in node.getchildren():
-            node.remove(element)
-
-        return node
-        
-    def begin(self, stream):
-        self.assign.begin(stream, '_content')
-        stream.write("_out.write(_content)")
-        
-    def end(self, stream):
-        self.assign.end(stream)
-
-class Replace(Content):
-    def update(self, node):
-        return None
-    
-class Tag(object):
-    def __init__(self, node):
-        self.node = node
-        self.tail = node.tail
-                
-    @property
-    def tag(self):
-        tag = self.node.tag
-        if tag.startswith('{'):
-            return tag[tag.find('}')+1:]
-
-        return tag
-
-    def update(self, node):
-        self.node = node
-        return node
-    
-    def begin(self, stream):
-        if self.node is None:
-            return
-        stream.out('<%s' % self.tag)
-        stream.write("for _name, _value in _attrs.items():")
-        stream.indent()
-        stream.write("""_out.write(' %s=\"%s\"' % (_name, _escape(_value, '\"')))""")
-        stream.outdent()
-        stream.write("_attrs.clear()")
-        for name, value in self.node.attrib.items():
-            stream.out(' %s=%s' % (name, xml.sax.saxutils.quoteattr(value)))
-
-        if self.node.text is None:
-            stream.out(' />')
-        else:
-            stream.out('>')
-        
-        text = self.node.text
-        if text is not None:
-            text = cgi.escape(text.replace('\n', '\\n'), '"')
-            stream.out(text)
-
-    def end(self, stream):
-        if self.node is not None and self.node.text is not None:
-            stream.out('</%s>' % self.tag)
-
-        if self.tail is not None:
-            tail = cgi.escape(self.tail.replace('\n', '\\n'), "'")
-            stream.out(tail)
-
-def handler(key=None):
-    def decorate(f):
-        def g(node):
-            if key is None:
-                return f(node, None)
-            return f(node, node.get(key))
-        g.__ns__ = key
-        return g
-    return decorate
-
- at handler("{http://xml.zope.org/namespaces/tal}define")
-def define(node, value):
-    return Define(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}condition")
-def condition(node, value):
-    return Condition(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}repeat")
-def repeat(node, value):
-    return Repeat(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}attributes")
-def attribute(node, value):
-    return Attribute(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}content")
-def content(node, value):
-    return Content(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}replace")
-def replace(node, value):
-    return Replace(value)
-
- at handler("{http://xml.zope.org/namespaces/tal}omit-tag")
-def omit(node, value):
-    raise NotImplemented, "The tal:omit-tag statement is not supported yet."
-
- at handler()
-def tag(node, value):
-    return Tag(node)

Copied: z3c.pt/tags/0.2/z3c/pt/tal.py (from rev 82141, z3c.pt/trunk/z3c/pt/tal.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/tal.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/tal.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,406 @@
+import parser
+import cgi
+import xml.sax.saxutils
+import itertools
+
+def expression(string):
+    """
+    Python-expressions in try-except construct.
+    
+    Specification:
+
+    expression :: = python_expression [ |* expression ]
+    python_expresion ::= a python expression string
+
+    *) Using | as logical or is not supported.
+
+    """
+
+    string = string.replace('\n', '')
+
+    expression = []
+
+    i = j = 0
+    while i < len(string):
+        j = string.find('|', j + 1)
+        if j == -1:
+            j = len(string)
+
+        expr = string[i:j].lstrip()
+
+        try:
+            # we use the ``parser`` module to determine if
+            # an expression is a valid python expression
+            parser.expr(expr)
+        except SyntaxError, e:
+            if j < len(string):
+                continue
+
+            raise e
+
+        expression.append(expr)
+        i = j + 1
+
+    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:
+
+    Specification:
+
+    argument ::= define_var [';' define_var]
+    define_var ::= Name python_expression
+
+    """
+
+    string = string.replace('\n', '').strip()
+
+    defines = []
+
+    i = 0
+    while i < len(string):
+        while string[i] == ' ':
+            i += 1
+
+        # 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])
+
+        # get expression
+        i = j
+        while j < len(string):
+            j = string.find(';', j+1)
+            if j == -1:
+                j = len(string)
+
+            try:
+                expr = expression(string[i:j])
+            except SyntaxError, e:
+                continue
+            break
+        else:
+            raise e
+
+        defines.append((list(var), expr))
+
+        i = j + 1
+
+    return defines
+
+class repeatitem(object):
+    def __init__(self, iterator, length):
+        self.length = length
+        self.iterator = iterator
+        
+    @property
+    def index(self):
+        return self.length - len(self.iterator) - 1
+        
+    @property
+    def start(self):
+        return self.index == 0
+
+    @property
+    def end(self):
+        return self.index == self.length - 1
+
+    def number(self):
+        return self.index + 1
+
+    def odd(self):
+        return bool(self.index % 2)
+
+    def even(self):
+        return not self.odd()
+
+class repeatdict(dict):
+    def __setitem__(self, key, iterator):
+        try:
+            length = len(iterator)
+        except TypeError:
+            length = None
+            
+        dict.__setitem__(self, key, (iterator, length))
+        
+    def __getitem__(self, key):
+        value, length = dict.__getitem__(self, key)
+
+        if not isinstance(value, repeatitem):
+            value = repeatitem(value, length)
+            self.__setitem__(key, value)
+
+        return value
+    
+class Assign(object):
+    def __init__(self, expression):
+        self.expressions = expression
+            
+    def begin(self, stream, variable):
+        """First n - 1 expressions must be try-except wrapped."""
+
+        for expression in self.expressions[:-1]:
+            stream.write("try:")
+            stream.indent()
+            stream.write("%s = %s" % (variable, expression))
+            stream.outdent()
+            stream.write("except Exception, e:")
+            stream.indent()
+
+        expression = self.expressions[-1]
+        stream.write("%s = %s" % (variable, expression))
+        
+    def end(self, stream):
+        stream.outdent(len(self.expressions)-1)
+
+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]))
+
+    def update(self, node):
+        return node
+            
+    def begin(self, stream):
+        # save local variables already in scope
+        save = stream.save()
+        stream.write("%s = {}" % save)
+
+        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 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 in reversed(self.variables):
+            # restore local variables previously in scope
+            stream.write("if '%s' in %s:" % (variable, restore))
+            stream.indent()
+            stream.write("%s = %s['%s']" % (variable, restore, variable))
+            stream.outdent()
+            stream.write("else:")
+            stream.indent()
+            stream.write("del %s" % variable)
+            stream.write("_scope.remove('%s')" % variable)
+            stream.outdent()
+
+class Condition(object):
+    def __init__(self, value):
+        self.assign = Assign(expression(value))
+
+    def update(self, node):
+        return node
+
+    def begin(self, stream):
+        variable = '_condition'
+
+        self.assign.begin(stream, variable)
+        stream.write("if %s:" % variable)
+        stream.indent()
+
+    def end(self, stream):
+        self.assign.end(stream)
+        stream.outdent()
+
+class Repeat(object):
+    def __init__(self, value):
+        string = value.lstrip().replace('\n', '')
+
+        space = string.find(' ')
+        if space == -1:
+            raise ValueError, "Invalid repeat clause (%s)." % value
+
+        self.variable = string[:space]
+        self.assign = Assign(expression(string[space:]))
+        
+    def update(self, node):
+        return node
+
+    def begin(self, stream):
+        variable = self.variable
+        iterator = stream.save()
+        
+        self.assign.begin(stream, iterator)
+
+        # define variable scope
+        self.define = Define("%s None" % self.variable)
+        self.define.begin(stream)
+
+        # initialize iterator
+        stream.write("repeat['%s'] = %s = %s.__iter__()" % (variable, iterator, iterator))
+
+        # loop
+        stream.write("while %s:" % iterator)
+        stream.indent()
+        stream.write("%s = %s.next()" % (variable, iterator))
+        
+    def end(self, stream):
+        # cook before leaving loop
+        stream.cook()
+        
+        stream.outdent()
+        self.define.end(stream)
+        self.assign.end(stream)
+        stream.restore()
+        
+class Attribute(object):
+    def __init__(self, value):
+        self.attributes = [(v, Assign(e)) for v, e in definition(value)]
+
+    def update(self, node):
+        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 variables, assign in self.attributes:
+            for variable in variables:
+                assign.begin(stream, "_attrs['%s']" % variable)
+                assign.end(stream)
+
+    def end(self, stream):
+        pass
+
+class Content(object):
+    def __init__(self, value):
+        self.assign = Assign(expression(value))
+
+    def update(self, node):
+        node.text = ''
+        for element in node.getchildren():
+            node.remove(element)
+
+        return node
+        
+    def begin(self, stream):
+        self.assign.begin(stream, '_content')
+        stream.write("_out.write(_content)")
+        
+    def end(self, stream):
+        self.assign.end(stream)
+
+class Replace(Content):
+    def update(self, node):
+        return None
+    
+class Tag(object):
+    def __init__(self, node):
+        self.node = node
+        self.tail = node.tail
+                
+    @property
+    def tag(self):
+        tag = self.node.tag
+        if tag.startswith('{'):
+            return tag[tag.find('}')+1:]
+
+        return tag
+
+    def update(self, node):
+        self.node = node
+        return node
+    
+    def begin(self, stream):
+        if self.node is None:
+            return
+        stream.out('<%s' % self.tag)
+        stream.write("for _name, _value in _attrs.items():")
+        stream.indent()
+        stream.write("""_out.write(' %s=\"%s\"' % (_name, _escape(_value, '\"')))""")
+        stream.outdent()
+        stream.write("_attrs.clear()")
+        for name, value in self.node.attrib.items():
+            stream.out(' %s=%s' % (name, xml.sax.saxutils.quoteattr(value)))
+
+        if self.node.text is None:
+            stream.out(' />')
+        else:
+            stream.out('>')
+        
+        text = self.node.text
+        if text is not None:
+            text = cgi.escape(text.replace('\n', '\\n'), '"')
+            stream.out(text)
+
+    def end(self, stream):
+        if self.node is not None and self.node.text is not None:
+            stream.out('</%s>' % self.tag)
+
+        if self.tail is not None:
+            tail = cgi.escape(self.tail.replace('\n', '\\n'), "'")
+            stream.out(tail)
+
+def handler(key=None):
+    def decorate(f):
+        def g(node):
+            if key is None:
+                return f(node, None)
+            return f(node, node.get(key))
+        g.__ns__ = key
+        return g
+    return decorate
+
+ at handler("{http://xml.zope.org/namespaces/tal}define")
+def define(node, value):
+    return Define(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}condition")
+def condition(node, value):
+    return Condition(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}repeat")
+def repeat(node, value):
+    return Repeat(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}attributes")
+def attribute(node, value):
+    return Attribute(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}content")
+def content(node, value):
+    return Content(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}replace")
+def replace(node, value):
+    return Replace(value)
+
+ at handler("{http://xml.zope.org/namespaces/tal}omit-tag")
+def omit(node, value):
+    raise NotImplemented, "The tal:omit-tag statement is not supported yet."
+
+ at handler()
+def tag(node, value):
+    return Tag(node)

Deleted: z3c.pt/tags/0.2/z3c/pt/tal.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/tal.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,226 +0,0 @@
-TAL
-===
-
-Tests for the TAL language implementation.
-
-  >>> from z3c.pt import tal
-
-expression
-----------
-
-Simple expression:
-
-  >>> tal.expression("4 + 5")
-  ['4 + 5']
-
-Complex expressions:
-
-  >>> tal.expression("a.non_defined_method() | 1")
-  ['a.non_defined_method() ', '1']
-
-Expression with non-semantic horizontal bar.
-
-  >>> tal.expression("'|'")
-  ["'|'"]
-    
-Expression with non-semantic horizontal bar and semantic bar.
-
-  >>> tal.expression("a.non_defined_method() | '|'")
-  ['a.non_defined_method() ', "'|'"]
-    
-Define
-------
-
-  >>> from z3c.pt.io import CodeIO
-  >>> stream = CodeIO()
-  >>> _scope = []
-  
-Variable scope:
-
-  >>> define = tal.Define("a b")
-  >>> b = object()
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a is b
-  True
-  >>> del a
-  >>> _scope.remove('a')
-  >>> define.end(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  Traceback (most recent call last):
-      ...
-  NameError: name 'a' is not defined
-  >>> b is not None
-  True
-  
-Multiple defines:
-
-  >>> define = tal.Define("a b; c d")
-  >>> d = object()
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a is b and c is d
-  True
-  >>> define.end(stream)
-  >>> del a; del c
-  >>> _scope.remove('a'); _scope.remove('c')
-  >>> exec stream.getvalue()
-  >>> a
-  Traceback (most recent call last):
-      ...
-  NameError: name 'a' is not defined
-  >>> c
-  Traceback (most recent call last):
-      ...
-  NameError: name 'c' is not defined
-  >>> b is not None and d is not None
-  True
-
-Using semicolons in expressions within a define:
-
-  >>> define = tal.Define("a ';'")
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  ';'
-  >>> define.end(stream)
-
-Ending an define clause with a semicolon.
-
-  >>> define = tal.Define("a 4 + 5;")
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  9
-  >>> define.end(stream)
-
-Scope:
-
-  >>> a = 1
-  >>> define = tal.Define("a 2")
-  >>> define.begin(stream)
-  >>> define.end(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  1
-  
-Conditions
-----------
-
-  >>> stream = CodeIO()
-  
-True:
-
-  >>> a = 0
-  >>> condition = tal.Condition("True")
-  >>> define = tal.Define("a 1")
-  >>> condition.begin(stream)
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  1
-  >>> define.end(stream)
-  >>> condition.end(stream)
-
-False:
-
-  >>> a = 0
-  >>> condition = tal.Condition("False")
-  >>> define = tal.Define("a 1")
-  >>> condition.begin(stream)
-  >>> define.begin(stream)
-  >>> exec stream.getvalue()
-  >>> a
-  0
-  >>> define.end(stream)
-  >>> condition.end(stream)
-
-Repeat
-------
-
-  >>> from z3c.pt.tal import repeatdict as _repeat
-  >>> repeat = _repeat()
-  
-  >>> stream = CodeIO()
-  
-  >>> _repeat = tal.Repeat("i range(5)")
-  >>> _repeat.begin(stream)
-  >>> stream.write("r = repeat['i']")
-  >>> stream.write("print (i, r.index, r.start, r.end, r.number(), r.odd(), r.even())")
-  >>> exec stream.getvalue()
-  (0, 0, True, False, 1, False, True)
-  (1, 1, False, False, 2, True, False)
-  (2, 2, False, False, 3, False, True)
-  (3, 3, False, False, 4, True, False)
-  (4, 4, False, True, 5, False, True)
-  >>> _repeat.end(stream)
-
-Attribute
----------
-
-  >>> from z3c.pt.tal import Attribute
-  >>> stream = CodeIO()
-  
-  >>> attribute = Attribute("class 'plain'")
-  >>> attribute.begin(stream)
-  >>> attribute.end(stream)
-  >>> exec stream.getvalue()
-  >>> _attrs
-  {'class': 'plain'}
-  >>> del _attrs
-  
-Attributes that are bound to expressions will be removed on ``update``:
-
-  >>> from lxml.etree import Element
-  >>> img = Element('img')
-  >>> img.set('src', u'logo.gif')
-  >>> img.set('class', u'stx')
-  >>> img = attribute.update(img)
-  >>> img.attrib
-  {'src': 'logo.gif'}
-
-Content
--------
-
-  >>> from z3c.pt.tal import Content
-  >>> stream = CodeIO()
-
-  >>> from StringIO import StringIO
-  >>> _out = StringIO()
-  
-  >>> content = tal.Content(u"'Hello World!'")
-  >>> content.begin(stream)
-  >>> exec stream.getvalue()
-  >>> _out.getvalue()
-  'Hello World!'
-  >>> content.end(stream)
-
-Tag
----
-
-Define required global symbols.
-
-  >>> from cgi import escape as _escape
-  >>> _attrs = {}
-  
-  >>> stream = CodeIO()
-  >>> br = Element('br')  
-  >>> tag = tal.Tag(br)
-  >>> tag.begin(stream)
-  >>> tag.end(stream)
-  >>> _out = StringIO()
-  >>> exec stream.getvalue()
-  >>> _out.getvalue()
-  '<br />'
-
-  >>> stream = CodeIO()
-  >>> div = Element('div')
-  >>> div.text = ''
-  >>> tag = tal.Tag(div)
-  >>> tag.begin(stream)
-  >>> tag.end(stream)
-  >>> _out = StringIO()
-  >>> exec stream.getvalue()
-  >>> _out.getvalue()
-  '<div></div>'

Copied: z3c.pt/tags/0.2/z3c/pt/tal.txt (from rev 82141, z3c.pt/trunk/z3c/pt/tal.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/tal.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/tal.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,267 @@
+TAL
+===
+
+Tests for the TAL language implementation.
+
+  >>> from z3c.pt import tal
+
+expression
+----------
+
+Simple expression:
+
+  >>> tal.expression("4 + 5")
+  ['4 + 5']
+
+Complex expressions:
+
+  >>> tal.expression("a.non_defined_method() | 1")
+  ['a.non_defined_method() ', '1']
+
+Expression with non-semantic horizontal bar.
+
+  >>> tal.expression("'|'")
+  ["'|'"]
+    
+Expression with non-semantic horizontal bar and semantic bar.
+
+  >>> tal.expression("a.non_defined_method() | '|'")
+  ['a.non_defined_method() ', "'|'"]
+
+Assign
+------
+
+Simple assignment.
+
+  >>> from z3c.pt.io import CodeIO
+  >>> stream = CodeIO()
+  >>> _scope = []
+
+  >>> assign = tal.Assign(tal.expression("1"))
+  >>> assign.begin(stream, 'a')
+  >>> exec stream.getvalue()
+  >>> a == 1
+  True
+  >>> del a
+  >>> assign.end(stream)
+  >>> exec stream.getvalue()
+  
+Define
+------
+
+Variable scope:
+
+  >>> stream = CodeIO()
+  >>> define = tal.Define("a b")
+  >>> b = object()
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a is b
+  True
+  >>> del a
+  >>> _scope.remove('a')
+  >>> define.end(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  Traceback (most recent call last):
+      ...
+  NameError: name 'a' is not defined
+  >>> b is not None
+  True
+  
+Multiple defines:
+
+  >>> stream = CodeIO()
+  >>> define = tal.Define("a b; c d")
+  >>> d = object()
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a is b and c is d
+  True
+  >>> define.end(stream)
+  >>> del a; del c
+  >>> _scope.remove('a'); _scope.remove('c')
+  >>> exec stream.getvalue()
+  >>> a
+  Traceback (most recent call last):
+      ...
+  NameError: name 'a' is not defined
+  >>> c
+  Traceback (most recent call last):
+      ...
+  NameError: name 'c' is not defined
+  >>> b is not None and d is not None
+  True
+
+Tuple assignments:
+
+  >>> stream = CodeIO()
+  >>> 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:
+
+  >>> stream = CodeIO()
+  >>> define = tal.Define("a ';'")
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  ';'
+  >>> define.end(stream)
+
+Ending an define clause with a semicolon.
+
+  >>> stream = CodeIO()
+  >>> define = tal.Define("a 4 + 5;")
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  9
+  >>> define.end(stream)
+  >>> del a
+  >>> _scope.remove('a')
+  >>> exec stream.getvalue()
+  
+Scope:
+
+  >>> stream = CodeIO()
+  >>> a = 1
+  >>> _scope.append('a')
+  >>> define = tal.Define("a 2")
+  >>> define.begin(stream)
+  >>> define.end(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  1
+  
+Conditions
+----------
+
+  >>> stream = CodeIO()
+  
+True:
+
+  >>> a = 0
+  >>> condition = tal.Condition("True")
+  >>> define = tal.Define("a 1")
+  >>> condition.begin(stream)
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  1
+  >>> define.end(stream)
+  >>> condition.end(stream)
+
+False:
+
+  >>> a = 0
+  >>> condition = tal.Condition("False")
+  >>> define = tal.Define("a 1")
+  >>> condition.begin(stream)
+  >>> define.begin(stream)
+  >>> exec stream.getvalue()
+  >>> a
+  0
+  >>> define.end(stream)
+  >>> condition.end(stream)
+
+Repeat
+------
+
+  >>> from z3c.pt.tal import repeatdict as _repeat
+  >>> repeat = _repeat()
+  
+  >>> stream = CodeIO()
+  
+  >>> _repeat = tal.Repeat("i range(5)")
+  >>> _repeat.begin(stream)
+  >>> stream.write("r = repeat['i']")
+  >>> stream.write("print (i, r.index, r.start, r.end, r.number(), r.odd(), r.even())")
+  >>> exec stream.getvalue()
+  (0, 0, True, False, 1, False, True)
+  (1, 1, False, False, 2, True, False)
+  (2, 2, False, False, 3, False, True)
+  (3, 3, False, False, 4, True, False)
+  (4, 4, False, True, 5, False, True)
+  >>> _repeat.end(stream)
+
+Attribute
+---------
+
+  >>> from z3c.pt.tal import Attribute
+  >>> stream = CodeIO()
+  
+  >>> attribute = Attribute("class 'plain'")
+  >>> attribute.begin(stream)
+  >>> attribute.end(stream)
+  >>> exec stream.getvalue()
+  >>> _attrs
+  {'class': 'plain'}
+  >>> del _attrs
+  
+Attributes that are bound to expressions will be removed on ``update``:
+
+  >>> from lxml.etree import Element
+  >>> img = Element('img')
+  >>> img.set('src', u'logo.gif')
+  >>> img.set('class', u'stx')
+  >>> img = attribute.update(img)
+  >>> img.attrib
+  {'src': 'logo.gif'}
+
+Content
+-------
+
+  >>> from z3c.pt.tal import Content
+  >>> stream = CodeIO()
+
+  >>> from StringIO import StringIO
+  >>> _out = StringIO()
+  
+  >>> content = tal.Content(u"'Hello World!'")
+  >>> content.begin(stream)
+  >>> exec stream.getvalue()
+  >>> _out.getvalue()
+  'Hello World!'
+  >>> content.end(stream)
+
+Tag
+---
+
+Define required global symbols.
+
+  >>> from cgi import escape as _escape
+  >>> _attrs = {}
+  
+  >>> stream = CodeIO()
+  >>> br = Element('br')  
+  >>> tag = tal.Tag(br)
+  >>> tag.begin(stream)
+  >>> tag.end(stream)
+  >>> _out = StringIO()
+  >>> exec stream.getvalue()
+  >>> _out.getvalue()
+  '<br />'
+
+  >>> stream = CodeIO()
+  >>> div = Element('div')
+  >>> div.text = ''
+  >>> tag = tal.Tag(div)
+  >>> tag.begin(stream)
+  >>> tag.end(stream)
+  >>> _out = StringIO()
+  >>> exec stream.getvalue()
+  >>> _out.getvalue()
+  '<div></div>'

Deleted: z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/test_doctests.py	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,23 +0,0 @@
-import zope.testing
-import unittest
-
-OPTIONFLAGS = (zope.testing.doctest.REPORT_ONLY_FIRST_FAILURE |
-               zope.testing.doctest.ELLIPSIS |
-#               zope.testing.doctest.REPORT_NDIFF | 
-               zope.testing.doctest.NORMALIZE_WHITESPACE)
-
-import zope.component.testing
-
-def test_suite():
-    doctests = ['README.txt', 'BENCHMARKS.txt', 'tal.txt', 'translate.txt']
-    
-    return unittest.TestSuite((
-        zope.testing.doctest.DocFileSuite(doctest,
-                                          optionflags=OPTIONFLAGS,
-                                          setUp=zope.component.testing.setUp,
-                                          tearDown=zope.component.testing.tearDown,
-                                          package="z3c.pt") for doctest in doctests
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')

Copied: z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py (from rev 82141, z3c.pt/trunk/z3c/pt/tests/test_doctests.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/tests/test_doctests.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,22 @@
+import zope.testing
+import unittest
+
+OPTIONFLAGS = (zope.testing.doctest.REPORT_ONLY_FIRST_FAILURE |
+               zope.testing.doctest.ELLIPSIS |
+               zope.testing.doctest.NORMALIZE_WHITESPACE)
+
+import zope.component.testing
+
+def test_suite():
+    filesuites = ['README.txt', 'BENCHMARKS.txt', 'tal.txt', 'translation.txt', 'codegen.txt']
+        
+    return unittest.TestSuite(
+        [zope.testing.doctest.DocFileSuite(doctest,
+                                          optionflags=OPTIONFLAGS,
+                                          setUp=zope.component.testing.setUp,
+                                          tearDown=zope.component.testing.tearDown,
+                                          package="z3c.pt") for doctest in filesuites]
+        )
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Copied: z3c.pt/tags/0.2/z3c/pt/transformer.py (from rev 82141, z3c.pt/trunk/z3c/pt/transformer.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/transformer.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/transformer.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,235 @@
+from compiler import ast, parse
+
+class ASTTransformer(object):
+    """General purpose base class for AST transformations.
+    
+    Every visitor method can be overridden to return an AST node that has been
+    altered or replaced in some way.
+    """
+
+    def visit(self, node):
+        if node is None:
+            return None
+        if type(node) is tuple:
+            return tuple([self.visit(n) for n in node])
+        visitor = getattr(self, 'visit%s' % node.__class__.__name__,
+                          self._visitDefault)
+        return visitor(node)
+
+    def _clone(self, node, *args):
+        lineno = getattr(node, 'lineno', None)
+        node = node.__class__(*args)
+        if lineno is not None:
+            node.lineno = lineno
+        if isinstance(node, (ast.Class, ast.Function, ast.GenExpr, ast.Lambda)):
+            node.filename = '<string>' # workaround for bug in pycodegen
+        return node
+
+    def _visitDefault(self, node):
+        return node
+
+    def visitExpression(self, node):
+        return self._clone(node, self.visit(node.node))
+
+    def visitModule(self, node):
+        return self._clone(node, node.doc, self.visit(node.node))
+
+    def visitStmt(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes])
+
+    # Classes, Functions & Accessors
+
+    def visitCallFunc(self, node):
+        return self._clone(node, self.visit(node.node),
+            [self.visit(x) for x in node.args],
+            node.star_args and self.visit(node.star_args) or None,
+            node.dstar_args and self.visit(node.dstar_args) or None
+        )
+
+    def visitClass(self, node):
+        return self._clone(node, node.name, [self.visit(x) for x in node.bases],
+            node.doc, self.visit(node.code)
+        )
+
+    def visitFunction(self, node):
+        args = []
+        if hasattr(node, 'decorators'):
+            args.append(self.visit(node.decorators))
+        return self._clone(node, *args + [
+            node.name,
+            node.argnames,
+            [self.visit(x) for x in node.defaults],
+            node.flags,
+            node.doc,
+            self.visit(node.code)
+        ])
+
+    def visitGetattr(self, node):
+        return self._clone(node, self.visit(node.expr), node.attrname)
+
+    def visitLambda(self, node):
+        node = self._clone(node, node.argnames,
+            [self.visit(x) for x in node.defaults], node.flags,
+            self.visit(node.code)
+        )
+        return node
+
+    def visitSubscript(self, node):
+        return self._clone(node, self.visit(node.expr), node.flags,
+            [self.visit(x) for x in node.subs]
+        )
+
+    # Statements
+
+    def visitAssert(self, node):
+        return self._clone(node, self.visit(node.test), self.visit(node.fail))
+
+    def visitAssign(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes],
+            self.visit(node.expr)
+        )
+
+    def visitAssAttr(self, node):
+        return self._clone(node, self.visit(node.expr), node.attrname,
+            node.flags
+        )
+
+    def visitAugAssign(self, node):
+        return self._clone(node, self.visit(node.node), node.op,
+            self.visit(node.expr)
+        )
+
+    def visitDecorators(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes])
+
+    def visitExec(self, node):
+        return self._clone(node, self.visit(node.expr), self.visit(node.locals),
+            self.visit(node.globals)
+        )
+
+    def visitFor(self, node):
+        return self._clone(node, self.visit(node.assign), self.visit(node.list),
+            self.visit(node.body), self.visit(node.else_)
+        )
+
+    def visitIf(self, node):
+        return self._clone(node, [self.visit(x) for x in node.tests],
+            self.visit(node.else_)
+        )
+
+    def _visitPrint(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes],
+            self.visit(node.dest)
+        )
+    visitPrint = visitPrintnl = _visitPrint
+
+    def visitRaise(self, node):
+        return self._clone(node, self.visit(node.expr1), self.visit(node.expr2),
+            self.visit(node.expr3)
+        )
+
+    def visitReturn(self, node):
+        return self._clone(node, self.visit(node.value))
+
+    def visitTryExcept(self, node):
+        return self._clone(node, self.visit(node.body), self.visit(node.handlers),
+            self.visit(node.else_)
+        )
+
+    def visitTryFinally(self, node):
+        return self._clone(node, self.visit(node.body), self.visit(node.final))
+
+    def visitWhile(self, node):
+        return self._clone(node, self.visit(node.test), self.visit(node.body),
+            self.visit(node.else_)
+        )
+
+    def visitWith(self, node):
+        return self._clone(node, self.visit(node.expr),
+            [self.visit(x) for x in node.vars], self.visit(node.body)
+        )
+
+    def visitYield(self, node):
+        return self._clone(node, self.visit(node.value))
+
+    # Operators
+
+    def _visitBoolOp(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes])
+    visitAnd = visitOr = visitBitand = visitBitor = visitBitxor = _visitBoolOp
+    visitAssTuple = visitAssList = _visitBoolOp
+
+    def _visitBinOp(self, node):
+        return self._clone(node,
+            (self.visit(node.left), self.visit(node.right))
+        )
+    visitAdd = visitSub = _visitBinOp
+    visitDiv = visitFloorDiv = visitMod = visitMul = visitPower = _visitBinOp
+    visitLeftShift = visitRightShift = _visitBinOp
+
+    def visitCompare(self, node):
+        return self._clone(node, self.visit(node.expr),
+            [(op, self.visit(n)) for op, n in  node.ops]
+        )
+
+    def _visitUnaryOp(self, node):
+        return self._clone(node, self.visit(node.expr))
+    visitUnaryAdd = visitUnarySub = visitNot = visitInvert = _visitUnaryOp
+    visitBackquote = visitDiscard = _visitUnaryOp
+
+    def visitIfExp(self, node):
+        return self._clone(node, self.visit(node.test), self.visit(node.then),
+            self.visit(node.else_)
+        )
+
+    # Identifiers, Literals and Comprehensions
+
+    def visitDict(self, node):
+        return self._clone(node, 
+            [(self.visit(k), self.visit(v)) for k, v in node.items]
+        )
+
+    def visitGenExpr(self, node):
+        return self._clone(node, self.visit(node.code))
+
+    def visitGenExprFor(self, node):
+        return self._clone(node, self.visit(node.assign), self.visit(node.iter),
+            [self.visit(x) for x in node.ifs]
+        )
+
+    def visitGenExprIf(self, node):
+        return self._clone(node, self.visit(node.test))
+
+    def visitGenExprInner(self, node):
+        quals = [self.visit(x) for x in node.quals]
+        return self._clone(node, self.visit(node.expr), quals)
+
+    def visitKeyword(self, node):
+        return self._clone(node, node.name, self.visit(node.expr))
+
+    def visitList(self, node):
+        return self._clone(node, [self.visit(n) for n in node.nodes])
+
+    def visitListComp(self, node):
+        quals = [self.visit(x) for x in node.quals]
+        return self._clone(node, self.visit(node.expr), quals)
+
+    def visitListCompFor(self, node):
+        return self._clone(node, self.visit(node.assign), self.visit(node.list),
+            [self.visit(x) for x in node.ifs]
+        )
+
+    def visitListCompIf(self, node):
+        return self._clone(node, self.visit(node.test))
+
+    def visitSlice(self, node):
+        return self._clone(node, self.visit(node.expr), node.flags,
+            node.lower and self.visit(node.lower) or None,
+            node.upper and self.visit(node.upper) or None
+        )
+
+    def visitSliceobj(self, node):
+        return self._clone(node, [self.visit(x) for x in node.nodes])
+
+    def visitTuple(self, node):
+        return self._clone(node, [self.visit(n) for n in node.nodes])

Deleted: z3c.pt/tags/0.2/z3c/pt/translate.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translate.txt	2007-12-03 15:44:56 UTC (rev 82100)
+++ z3c.pt/tags/0.2/z3c/pt/translate.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -1,60 +0,0 @@
-Translation
------------
-
-  >>> from z3c.pt.pagetemplate import translate
-  >>> from z3c.pt import io
-
-A few imports.
-  
-  >>> from lxml import etree
-  >>> from StringIO import StringIO
-
-The following symbols are expected to be in the local symbol table.
-
-  >>> from cgi import escape as _escape
-  >>> from z3c.pt.tal import repeatdict as _repeat
-  >>> repeat = _repeat()
-  >>> _attrs = {}
-  
-Example:
-
-  >>> tree = etree.fromstring("""\
-  ... <div xmlns="http://www.w3.org/1999/xhtml"
-  ...      xmlns:tal="http://xml.zope.org/namespaces/tal">
-  ...   <span tal:define="a 'abc'"
-  ...         tal:attributes="class 'def' + a"
-  ...         tal:content="a + 'ghi'" />
-  ...   <ul>
-  ...     <li tal:repeat="i range(5)">
-  ...       <span tal:replace="'Item ' + str(i) + ')'" />
-  ...     </li>
-  ...   </ul>
-  ... </div>
-  ... """)
-
-  >>> stream = io.CodeIO(indentation_string="  ")
-  >>> translate(tree, stream)
-  >>> _out = StringIO()
-  >>> _scope = []
-  >>> exec stream.getvalue()
-  >>> print _out.getvalue()
-  <div>
-    <span class="defabc">abcghi</span>
-    <ul>
-       <li>
-          Item 0)
-       </li>
-       <li>
-          Item 1)
-       </li>
-       <li>
-          Item 2)
-       </li>
-       <li>
-          Item 3)
-       </li>
-       <li>
-          Item 4)
-       </li>
-     </ul>
-  </div>

Copied: z3c.pt/tags/0.2/z3c/pt/translation.py (from rev 82141, z3c.pt/trunk/z3c/pt/translation.py)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/translation.py	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/translation.py	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,76 @@
+from StringIO import StringIO
+import lxml.etree
+
+import cgi
+import tal
+import io
+
+ns_lookup_table = [(f.__ns__, f) for f in \
+    (tal.define,
+     tal.condition,
+     tal.omit,
+     tal.repeat,
+     tal.attribute,
+     tal.replace,
+     tal.tag,
+     tal.content)]
+
+wrapper = """\
+def render(**_kwargs):
+\trepeat = _repeatdict()
+\t_attrs = {}
+\t_scope = _kwargs.keys()
+\t_out = _StringIO()
+\t_globals = globals()
+\tfor _variable, _value in _kwargs.items():
+\t\t_globals[_variable] = _value
+%s
+\treturn _out.getvalue()
+"""
+
+def translate(body):
+    tree = lxml.etree.parse(StringIO(body))
+    root = tree.getroot()
+
+    stream = io.CodeIO(indentation=1, indentation_string="\t")
+    visit(root, stream)
+
+    source = wrapper % stream.getvalue()
+
+    _globals = dict(_StringIO=StringIO,
+                    _repeatdict=tal.repeatdict,
+                    _escape=cgi.escape)
+
+    return source, _globals
+
+def visit(node, stream):
+    """Translates a node and outputs to a code stream."""
+    
+    keys = node.keys() + [None]
+    
+    handlers = [handler(node) for key, handler in ns_lookup_table \
+                if key in keys]
+
+    # remove namespace attributes
+    for key, handler in ns_lookup_table:
+        if key is not None and key in node.attrib:
+            del node.attrib[key]
+
+    # update node
+    for handler in handlers:
+        node = handler.update(node)
+
+    # begin tag
+    for handler in handlers:
+        handler.begin(stream)
+
+    # process children
+    if node:
+        for element in node:
+            visit(element, stream)
+
+    # end tag
+    for handler in reversed(handlers):
+        handler.end(stream)
+
+    return stream

Copied: z3c.pt/tags/0.2/z3c/pt/translation.txt (from rev 82141, z3c.pt/trunk/z3c/pt/translation.txt)
===================================================================
--- z3c.pt/tags/0.2/z3c/pt/translation.txt	                        (rev 0)
+++ z3c.pt/tags/0.2/z3c/pt/translation.txt	2007-12-05 16:25:23 UTC (rev 82142)
@@ -0,0 +1,45 @@
+Translation
+-----------
+
+  >>> from z3c.pt.translation import translate
+
+Example:
+
+  >>> body = """\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:tal="http://xml.zope.org/namespaces/tal">
+  ...   <span tal:define="a 'abc'"
+  ...         tal:attributes="class 'def' + a"
+  ...         tal:content="a + 'ghi'" />
+  ...   <ul>
+  ...     <li tal:repeat="i range(5)">
+  ...       <span tal:replace="'Item ' + str(i) + ')'" />
+  ...     </li>
+  ...   </ul>
+  ... </div>
+  ... """
+
+  >>> source, _globals = translate(body)
+  >>> _locals = {}
+  >>> exec source in _globals, _locals
+  >>> print _locals['render']()
+  <div>
+    <span class="defabc">abcghi</span>
+    <ul>
+       <li>
+          Item 0)
+       </li>
+       <li>
+          Item 1)
+       </li>
+       <li>
+          Item 2)
+       </li>
+       <li>
+          Item 3)
+       </li>
+       <li>
+          Item 4)
+       </li>
+     </ul>
+  </div>



More information about the Checkins mailing list