[Checkins] SVN: z3c.pt/ Initial import.

Malthe Borch mborch at gmail.com
Mon Dec 3 10:00:50 EST 2007


Log message for revision 82096:
  Initial import.

Changed:
  A   z3c.pt/
  A   z3c.pt/branches/
  A   z3c.pt/tags/
  A   z3c.pt/trunk/
  A   z3c.pt/trunk/README.txt
  A   z3c.pt/trunk/setup.py
  A   z3c.pt/trunk/z3c/
  A   z3c.pt/trunk/z3c/__init__.py
  A   z3c.pt/trunk/z3c/pt/
  A   z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
  A   z3c.pt/trunk/z3c/pt/README.txt
  A   z3c.pt/trunk/z3c/pt/VERSION.txt
  A   z3c.pt/trunk/z3c/pt/__init__.py
  A   z3c.pt/trunk/z3c/pt/io.py
  A   z3c.pt/trunk/z3c/pt/pagetemplate.py
  A   z3c.pt/trunk/z3c/pt/tal.py
  A   z3c.pt/trunk/z3c/pt/tal.txt
  A   z3c.pt/trunk/z3c/pt/tests/
  A   z3c.pt/trunk/z3c/pt/tests/__init__.py
  A   z3c.pt/trunk/z3c/pt/tests/helloworld.pt
  A   z3c.pt/trunk/z3c/pt/tests/test_doctests.py
  A   z3c.pt/trunk/z3c/pt/translate.txt

-=-
Added: z3c.pt/trunk/README.txt
===================================================================
--- z3c.pt/trunk/README.txt	                        (rev 0)
+++ z3c.pt/trunk/README.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,13 @@
+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.

Added: z3c.pt/trunk/setup.py
===================================================================
--- z3c.pt/trunk/setup.py	                        (rev 0)
+++ z3c.pt/trunk/setup.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,32 @@
+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: -*-
+      """,
+      )

Added: z3c.pt/trunk/z3c/__init__.py
===================================================================
--- z3c.pt/trunk/z3c/__init__.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/__init__.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,6 @@
+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)

Added: z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/BENCHMARKS.txt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/BENCHMARKS.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,70 @@
+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)
+

Added: z3c.pt/trunk/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/README.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -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">
+  ...   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.

Added: z3c.pt/trunk/z3c/pt/VERSION.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/VERSION.txt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/VERSION.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1 @@
+0.1

Added: z3c.pt/trunk/z3c/pt/__init__.py
===================================================================
--- z3c.pt/trunk/z3c/pt/__init__.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/__init__.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,2 @@
+from pagetemplate import PageTemplate
+from pagetemplate import PageTemplateFile

Added: z3c.pt/trunk/z3c/pt/io.py
===================================================================
--- z3c.pt/trunk/z3c/pt/io.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/io.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -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_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)

Added: z3c.pt/trunk/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/pagetemplate.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,108 @@
+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)

Added: z3c.pt/trunk/z3c/pt/tal.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/tal.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,389 @@
+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)

Added: z3c.pt/trunk/z3c/pt/tal.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/tal.txt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/tal.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,226 @@
+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>'

Added: z3c.pt/trunk/z3c/pt/tests/__init__.py
===================================================================

Added: z3c.pt/trunk/z3c/pt/tests/helloworld.pt
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/helloworld.pt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/tests/helloworld.pt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,3 @@
+<div xmlns="http://www.w3.org/1999/xhtml">
+  Hello World!
+</div>

Added: z3c.pt/trunk/z3c/pt/tests/test_doctests.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/test_doctests.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/tests/test_doctests.py	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,23 @@
+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')

Added: z3c.pt/trunk/z3c/pt/translate.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translate.txt	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/translate.txt	2007-12-03 15:00:50 UTC (rev 82096)
@@ -0,0 +1,60 @@
+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>



More information about the Checkins mailing list