[Checkins] SVN: Sandbox/malthe/chameleon.genshi/ Initial import.

Malthe Borch mborch at gmail.com
Sat Sep 20 14:44:44 EDT 2008


Log message for revision 91288:
  Initial import.

Changed:
  A   Sandbox/malthe/chameleon.genshi/
  A   Sandbox/malthe/chameleon.genshi/README.txt
  A   Sandbox/malthe/chameleon.genshi/bootstrap.py
  A   Sandbox/malthe/chameleon.genshi/buildout.cfg
  A   Sandbox/malthe/chameleon.genshi/setup.py
  A   Sandbox/malthe/chameleon.genshi/src/
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/__init__.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/__init__.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/expressions.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.txt
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.txt
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/__init__.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/helloworld.html
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/test_doctests.py
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude1.html
  A   Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude2.html

-=-
Added: Sandbox/malthe/chameleon.genshi/README.txt
===================================================================
--- Sandbox/malthe/chameleon.genshi/README.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/README.txt	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,25 @@
+Overview
+========
+
+This package provides a fast Genshi template implementation based on
+the Chameleon template compiler. It's largely compatible with
+``genshi.template``.
+
+Compiler notes
+--------------
+
+1. Match templates defined in included templates are not applied to
+the calling document.
+
+Development
+-----------
+
+If you want to use the code directly from trunk (recommended only for
+development and testing usage), provide ``chameleon.genshi==dev`` as your
+dependency.
+
+svn://svn.zope.org/repos/main/Sandbox/malthe/chameleon.genshi/trunk#egg=chameleon.genshi-dev
+
+Want to contribute? Join #repoze on Freenode IRC.
+
+

Added: Sandbox/malthe/chameleon.genshi/bootstrap.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/bootstrap.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/bootstrap.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 71627 2006-12-20 16:46:11Z jim $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)

Added: Sandbox/malthe/chameleon.genshi/buildout.cfg
===================================================================
--- Sandbox/malthe/chameleon.genshi/buildout.cfg	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/buildout.cfg	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,14 @@
+[buildout]
+develop = . ../Chameleon
+parts = test
+
+[test]
+recipe = zc.recipe.testrunner
+environment = test-environment
+eggs =
+   Chameleon [lxml]
+   chameleon.genshi
+
+[test-environment]
+CHAMELEON_DEBUG = False
+CHAMELEON_CACHE = False
\ No newline at end of file

Added: Sandbox/malthe/chameleon.genshi/setup.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/setup.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/setup.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,35 @@
+from setuptools import setup, find_packages
+import sys
+
+version = '1.0dev'
+
+install_requires = [
+    'setuptools',
+    'zope.interface',
+    'zope.component',
+    'zope.i18n >= 3.5',
+    'Chameleon',
+    ]
+
+setup(name='chameleon.genshi',
+      version=version,
+      description="Genshi template engine based on Chameleon",
+      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 and the Zope Community',
+      author_email='zope-dev at zope.org',
+      url='',
+      license='BSD',
+      namespace_packages=['chameleon'],
+      packages = find_packages('src'),
+      package_dir = {'':'src'},
+      include_package_data=True,
+      zip_safe=False,
+      install_requires=install_requires,
+      )

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/__init__.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/__init__.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/__init__.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -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: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/__init__.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/__init__.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/__init__.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1 @@
+#

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/expressions.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/expressions.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/expressions.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,440 @@
+import re
+import parser
+
+from zope import interface
+from zope import component
+
+from chameleon.core import types
+
+class ExpressionTranslator(object):
+    """Genshi expression translation."""
+
+    re_method = re.compile(r'^(?P<name>[A-Za-z0-9_]+)'
+                           '(\((?P<args>[A-Za-z0-9_]+\s*(,\s*[A-Za-z0-9_]+)*)\))?')
+
+    re_interpolation = re.compile(r'(?P<prefix>[^\\]\$|^\$)({((?P<expression>.*)})?|'
+                                  '(?P<variable>[A-Za-z][A-Za-z0-9_]*))')
+
+    def declaration(self, string):
+        """Variable declaration.
+        
+        >>> declaration = ExpressionTranslator().declaration
+
+        Single variable:
+
+        >>> declaration("variable")
+        declaration('variable')
+
+        Multiple variables:
+
+        >>> declaration("variable1, variable2")
+        declaration('variable1', 'variable2')
+
+        Repeat not allowed:
+
+        >>> declaration('repeat')
+        Traceback (most recent call last):
+         ...
+        ValueError: Invalid variable name 'repeat' (reserved).
+
+        >>> declaration('_disallowed')
+        Traceback (most recent call last):
+         ...
+        ValueError: Invalid variable name '_disallowed' (starts with an underscore).
+        """
+
+        variables = []
+        for var in string.split(', '):
+            var = var.strip()
+
+            if var in ('repeat',):
+                raise ValueError, "Invalid variable name '%s' (reserved)." % var
+
+            if var.startswith('_') and not var.startswith('_tmp'):
+                raise ValueError(
+                    "Invalid variable name '%s' (starts with an underscore)." % var)
+
+            variables.append(var)
+
+        return types.declaration(variables)
+
+    def mapping(self, string):
+        """Semicolon-separated mapping.
+        
+        >>> mapping = ExpressionTranslator().mapping
+
+        >>> mapping("abc def")
+        mapping(('abc', 'def'),)
+
+        >>> mapping("abc def;")
+        mapping(('abc', 'def'),)
+
+        >>> mapping("abc")
+        mapping(('abc', None),)
+
+        >>> mapping("abc;")
+        mapping(('abc', None),)
+
+        >>> mapping("abc; def ghi")
+        mapping(('abc', None), ('def', 'ghi'))
+        """
+
+        defs = string.split(';')
+        mappings = []
+        for d in defs:
+            d = d.strip()
+            if d == '':
+                continue
+
+            while '  ' in d:
+                d = d.replace('  ', ' ')
+
+            parts = d.split(' ')
+            if len(parts) == 1:
+                mappings.append((d, None))
+            elif len(parts) == 2:
+                mappings.append((parts[0], parts[1]))
+            else:
+                raise ValueError, "Invalid mapping (%s)." % string
+
+        return types.mapping(mappings)
+
+    def definitions(self, string):
+        """Semi-colon separated variable definitions.
+        
+        >>> class MockExpressionTranslator(ExpressionTranslator):
+        ...     def validate(self, string):
+        ...         if string == '' or ';' in string:
+        ...             raise SyntaxError()
+        ...
+        ...     def translate(self, string):
+        ...         return types.value(string.strip())
+        
+        >>> definitions = MockExpressionTranslator().definitions
+        
+        Single define:
+        
+        >>> definitions("variable expression")
+        definitions((declaration('variable'), value('expression')),)
+        
+        Multiple defines:
+        
+        >>> definitions("variable1 expression1; variable2 expression2")
+        definitions((declaration('variable1'), value('expression1')),
+                    (declaration('variable2'), value('expression2')))
+        
+        Tuple define:
+        
+        >>> definitions("(variable1, variable2) (expression1, expression2)")
+        definitions((declaration('variable1', 'variable2'),
+                    value('(expression1, expression2)')),)
+
+        Space, the 'in' operator and '=' may be used to separate
+        variable from expression.
+
+        >>> definitions("variable in expression")
+        definitions((declaration('variable'), value('expression')),)        
+        
+        >>> definitions("variable1 = expression1; variable2 = expression2")
+        definitions((declaration('variable1'), value('expression1')),
+                    (declaration('variable2'), value('expression2')))
+
+        >>> definitions("variable1=expression1; variable2=expression2")
+        definitions((declaration('variable1'), value('expression1')),
+                    (declaration('variable2'), value('expression2')))
+        
+        A define clause that ends in a semicolon:
+        
+        >>> definitions("variable expression;")
+        definitions((declaration('variable'), value('expression')),)
+        
+        A define clause with a trivial expression (we do allow this):
+        
+        >>> definitions("variable")
+        definitions((declaration('variable'), None),)
+        
+        A proper define clause following one with a trivial expression:
+        
+        >>> definitions("variable1 expression; variable2")
+        definitions((declaration('variable1'), value('expression')),
+                    (declaration('variable2'), None))
+        """
+
+        string = string.replace('\n', '').strip()
+
+        defines = []
+        i = 0
+        global_scope = False
+        
+        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 = self.declaration(string[i+1:j])
+                j += 1
+            else:
+                j = string.find('=', i + 1)
+                k = string.find(' ', i + 1)
+                if k < j and k > -1 or j < 0:
+                    j = k
+                
+                if j < 0:
+                    var = self.declaration(string[i:])
+                    j = len(string)
+                else:
+                    var = self.declaration(string[i:j])
+
+            var.global_scope = global_scope
+            
+            # get expression
+            i = j + len(string) - j - len(string[j:].lstrip())
+
+            token = string[i:]
+            if token.startswith('=='):
+                raise ValueError("Invalid variable definition (%s)." % string)
+            elif token.startswith('='):
+                i += 1
+            elif token.startswith('in '):
+                i += 3
+
+            try:
+                expr = self.expression(string[i:])
+                j = -1
+            except SyntaxError, e:
+                expr = None
+                j = len(string)
+            
+            while j > i:
+                j = string.rfind(';', i, j)
+                if j < 0:
+                    raise e
+
+                try:
+                    expr = self.expression(string[i:j])
+                except SyntaxError, e:
+                    if string.rfind(';', i, j) > 0:
+                        continue
+                    raise e
+                
+                break
+                
+            defines.append((var, expr))
+
+            if j < 0:
+                break
+            
+            i = j + 1
+
+        return types.definitions(defines)
+
+    def definition(self, string):
+        defs = self.definitions(string)
+        if len(defs) != 1:
+            raise ValueError, "Multiple definitions not allowed."
+
+        return defs[0]
+
+    def output(self, string):
+        """String output; supports 'structure' keyword.
+        
+        >>> class MockExpressionTranslator(ExpressionTranslator):
+        ...     def validate(self, string):
+        ...         return True
+        ...
+        ...     def translate(self, string):
+        ...         return types.value(string)
+
+        >>> output = MockExpressionTranslator().output
+
+        >>> output("context/title")
+        escape(value('context/title'),)
+
+        >>> output("structure context/title")
+        value('context/title')        
+        """
+        
+        if string.startswith('structure '):
+            return self.expression(string[len('structure')+1:])
+        
+        expression = self.expression(string)
+
+        if isinstance(expression, types.parts):
+            return types.escape(expression)
+
+        return types.escape((expression,))
+            
+    def expression(self, string):
+        self.validate(string)
+        return self.translate(string)
+
+    def method(self, string):
+        """Parse a method definition.
+
+        >>> method = ExpressionTranslator().method
+
+        >>> method('name')
+        name()
+
+        >>> method('name(a, b, c)')
+        name(a, b, c)
+        
+        """
+
+        m = self.re_method.match(string)
+        if m is None:
+            raise ValueError("Not a valid method definition (%s)." % string)
+
+        name = m.group('name')
+        args = [arg.strip() for arg in (m.group('args') or "").split(',') if arg]
+
+        return types.method(name, args)
+
+    def validate(self, string):
+        """We use the ``parser`` module to determine if
+        an expression is a valid python expression."""
+
+        if isinstance(string, unicode):
+            string = string.encode('utf-8')
+
+        if string != "":
+            parser.expr(string.strip())
+
+    def translate(self, string):
+        if isinstance(string, str):
+            string = string.decode('utf-8')
+
+        return types.value(string.strip())
+
+    def split(self, string):
+        """Split up an interpolation string expression into parts that
+        are either unicode strings or ``value``-tuples.
+
+        >>> class MockTranslator(ExpressionTranslator):
+        ...     def expression(self, string):
+        ...         return types.value(string)
+        
+        >>> split = MockTranslator().split
+
+        >>> split("${abc}")
+        (value('abc'),)
+
+        >>> split(" ${abc}")
+        (' ', value('abc'))
+
+        >>> split("abc${def}")
+        ('abc', value('def'))
+
+        >>> split("${def}abc")
+        (value('def'), 'abc')
+
+        >>> split("abc${def}ghi")
+        ('abc', value('def'), 'ghi')
+
+        >>> split("abc${def}ghi${jkl}")
+        ('abc', value('def'), 'ghi', value('jkl'))
+
+        >>> split("abc${def}")
+        ('abc', value('def'))
+
+        >>> print split(u"abc${ghi}")
+        (u'abc', value('ghi'))
+        
+        """
+
+        m = self.interpolate(string)
+        if m is None:
+            return (string,)
+
+        prefix = m.group('prefix')
+        parts = []
+        
+        start = m.start() + len(prefix) - 1
+        if start > 0:
+            text = string[:start]
+            parts.append(text)
+
+        expression = m.group('expression')
+        variable = m.group('variable')
+
+        if expression:
+            parts.append(self.expression(expression))
+        elif variable:
+            parts.append(self.expression(variable))
+                
+        rest = string[m.end():]
+        if len(rest):
+            parts.extend(self.split(rest))
+
+        return tuple(parts)
+
+    def interpolate(self, string):
+        """Search for an interpolation and return a match.
+
+        >>> class MockTranslator(ExpressionTranslator):
+        ...     def expression(self, string):
+        ...         return types.parts((types.value(string),))
+
+        >>> interpolate = MockTranslator().interpolate
+        
+        >>> interpolate('${abc}').group('expression')
+        'abc'
+
+        >>> interpolate(' ${abc}').group('expression')
+        'abc'
+
+        >>> interpolate('abc${def}').group('expression')
+        'def'
+
+        >>> interpolate('abc${def}ghi${jkl}').group('expression')
+        'def'
+
+        >>> interpolate('$abc').group('variable')
+        'abc'
+
+        >>> interpolate('${abc')
+        Traceback (most recent call last):
+          ...
+        SyntaxError: Interpolation expressions must be of the form ${<expression>} (${abc)
+        
+        """
+
+        m = self.re_interpolation.search(string)
+        if m is None:
+            return None
+
+        expression = m.group('expression')
+        variable = m.group('variable')
+        
+        if expression:
+            left = m.start()+len(m.group('prefix'))+1
+            right = string.find('}')
+
+            while right != -1:
+                match = string[left:right]
+                try:
+                    exp = self.expression(match)
+                    break
+                except SyntaxError:
+                    right = string.find('}', right)
+            else:
+                raise
+
+            string = string[:right+1]
+            return self.re_interpolation.search(string)
+
+        if m is None or (expression is None and variable is None):
+            raise SyntaxError(
+                "Interpolation expressions must be of the "
+                "form ${<expression>} (%s)" % string)
+
+        if expression and not m.group('expression'):
+            raise SyntaxError(expression)
+
+        return m
+
+translator = ExpressionTranslator()

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,269 @@
+from chameleon.core import translation
+from chameleon.core import config
+from chameleon.core import etree
+from chameleon.core import utils
+from chameleon.core import types
+
+import expressions
+
+class GenshiElement(translation.Element):
+    """Genshi language element."""
+
+    translator = expressions.translator
+    
+    class node(translation.Node):
+        @property
+        def omit(self):
+            if self.element.py_strip is not None:
+                return self.element.py_strip or True
+            if self.element.meta_omit is not None:
+                return self.element.meta_omit or True
+            if self.element.py_replace or self.element.meta_replace:
+                return True
+            if self.element.xi_href:
+                return True
+            
+        @property
+        def define(self):
+            return self.element.py_with
+
+        @property
+        def condition(self):
+            return self.element.py_if
+
+        @property
+        def repeat(self):
+            return self.element.py_for
+
+        @property
+        def content(self):
+            return self.element.py_content or self.element.py_replace or \
+                   self.element.meta_replace
+
+        @property
+        def skip(self):
+            return bool(self.content)
+        
+        @property
+        def dict_attributes(self):
+            return self.element.py_attrs
+
+        @property
+        def dynamic_attributes(self):
+            attributes = []
+
+            xhtml_attributes = utils.get_attributes_from_namespace(
+                self.element, config.XHTML_NS)
+            
+            for name, value in xhtml_attributes.items():
+                parts = self.element.translator.split(value)
+                for part in parts:
+                    if isinstance(part, types.expression):
+                        attributes.append(
+                            (types.declaration((name,)), types.join(parts)))
+                        break
+                    
+            if self.element.meta_attributes is not None:
+                attributes.extend(self.element.meta_attributes)
+
+            if len(attributes) > 0:
+                return attributes
+            
+        @property
+        def static_attributes(self):
+            return utils.get_attributes_from_namespace(
+                self.element, config.XHTML_NS)
+
+        @property
+        def macro(self):
+            return self.element.py_def
+
+        @property
+        def cdata(self):
+            return self.element.meta_cdata
+
+        @property
+        def include(self):
+            href = self.element.xi_href
+            if href is not None:
+                return types.join(self.element.translator.split(href))
+
+        @property
+        def format(self):
+            return self.element.xi_parse
+
+        @property
+        def text(self):
+            if self.element.text is not None:
+                return self.element.translator.split(self.element.text)
+            return ()
+
+        @property
+        def tail(self):
+            if self.element.tail is not None:
+                return self.element.translator.split(self.element.tail)
+            return ()
+
+    node = property(node)
+
+    def update(self):
+        # Step 1: Convert py:choose, py:when, py:otherwise into
+        # tal:define, tal:condition
+        stream = self.stream
+        choose_expression = self._pull_attribute(utils.py_attr('choose'))
+        if choose_expression is not None:
+            choose_variable = stream.save()
+            
+            if choose_expression:
+                self._add_define(choose_variable, choose_expression)
+                
+            # select all elements that have the "py:when" controller,
+            # unless a "py:choose" expression sits in-between
+            variables = []
+            for element in self.xpath(
+                './*[@py:when]|.//*[not(@py:choose)]/*[@py:when]',
+                namespaces={'py': config.PY_NS}):
+
+                expression = element._pull_attribute(utils.py_attr('when'))
+                variable = stream.save()
+                variables.append(variable)
+
+                # add definition to ancestor
+                self._add_define(variable, expression)
+                
+                # add condition to element
+                if choose_expression:
+                    expression = "%s == %s" % (
+                        choose_variable, variable)
+                else:
+                    expression = "%s" % variable
+                    
+                element.attrib[utils.py_attr('if')] = expression
+
+            # process any "py:otherwise"-controllers
+            for element in self.xpath(
+                './*[@py:otherwise]|.//*[not(@py:choose)]/*[@py:otherwise]',
+                namespaces={'py': config.PY_NS}):
+                if choose_expression:
+                    expression = "%s not in %s" % (
+                        choose_variable, repr(tuple(variables)))
+                else:
+                    expression = "not(%s)" % " or ".join(variables)
+                    
+                element.attrib[utils.py_attr('if')] = expression
+
+        # Step 2: Process "py:match" macros
+        for element in self:
+            if getattr(element, 'py_match', None) is None:
+                continue
+            
+            nsmap = element.nsmap.copy()
+
+            # default namespace is not allowed in XPath
+            nsmap['xmlns'] = nsmap[None]
+            del nsmap[None]
+
+            # define macro
+            name = stream.save()
+            element.attrib[utils.py_attr('def')] = "%s(select)" % name
+
+            matches = self.getroottree().getroot().xpath(
+                element.py_match, namespaces=nsmap)
+            for match in matches:
+                # save reference to bound xpath-function
+                select = stream.save()
+                stream.selectors[select] = match.xpath
+
+                # replace matched element with macro
+                expression = "%s(%s)" % (name, select)
+                match.attrib[utils.py_attr('replace')] = expression
+
+                # save select-variable as element attribute
+                match.attrib[utils.meta_attr('select')] = select
+
+        # Step 3: Variable interpolation
+        
+
+    def _add_define(self, variable, expression):
+        name = utils.py_attr('with')
+        define = "%s=%s; " % (variable, expression)
+
+        if name in self.attrib:
+            self.attrib[name] += define
+        else:
+            self.attrib[name] = define
+
+    def _pull_attribute(self, name, default=None):
+        attrib = self.attrib
+        if name in attrib:
+            value = attrib[name]
+            del attrib[name]
+            return value
+        return default    
+
+class XHTMLElement(GenshiElement):
+    """XHTML namespace element."""
+
+    py_if = utils.attribute(
+        utils.py_attr('if'), lambda p: p.expression)
+    py_for = utils.attribute(
+        utils.py_attr('for'), lambda p: p.definition)
+    py_with = utils.attribute(utils.py_attr('with'),
+        lambda p: expressions.translator.definitions)
+    py_choose = utils.attribute(
+        utils.py_attr('choose'), lambda p: p.expression)
+    py_when = utils.attribute(
+        utils.py_attr('when'), lambda p: p.expression)
+    py_match = utils.attribute(
+        utils.py_attr('match'))
+    py_def = utils.attribute(
+        utils.py_attr('def'), lambda p: p.method)
+    py_attrs = utils.attribute(
+        utils.py_attr('attrs'), lambda p: p.expression)
+    py_content = utils.attribute(
+        utils.py_attr('content'), lambda p: p.output)
+    py_replace = utils.attribute(
+        utils.py_attr('replace'), lambda p: p.output)
+    py_strip = utils.attribute(
+        utils.py_attr('strip'), lambda p: p.expression)
+    xi_href = None
+    xi_parse = None
+
+class MetaElement(XHTMLElement, translation.MetaElement):
+    pass
+
+class PyElement(XHTMLElement):
+    py_strip = utils.attribute("strip", lambda p: p.expression, u"")
+    
+class PyIfElement(PyElement):
+    py_if = utils.attribute("test", lambda p: p.expression)
+
+class PyForElement(PyElement):
+    py_for = utils.attribute("each", lambda p: p.definition)
+
+class PyWithElement(PyElement):
+    py_with = utils.attribute(
+        "vars", lambda p: expressions.translator.definitions)
+
+class PyDefElement(PyElement):
+    py_def = utils.attribute("function", lambda p: p.method)
+    
+class PyMatchElement(PyElement):
+    py_match = utils.attribute("path")
+
+class XiIncludeElement(XHTMLElement):
+    xi_href = utils.attribute("href")
+    xi_parse = utils.attribute("parse", default="xml")
+
+class Parser(etree.Parser):
+    """The parser implementation for Genshi templates."""
+    
+    element_mapping = {
+        config.XHTML_NS: {None: XHTMLElement},
+        config.META_NS: {None: MetaElement},
+        config.XI_NS: {'include': XiIncludeElement},
+        config.PY_NS: {'if': PyIfElement,
+                       'for': PyForElement,
+                       'def': PyDefElement,
+                       'with': PyWithElement,
+                       'match': PyMatchElement}}

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.txt
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/language.txt	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,438 @@
+Genshi templates
+================
+
+This test demonstrates the compilation of the Genshi language.
+
+py:if
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <div py:if="False">
+  ...     <p>Bar</p>
+  ...   </div>
+  ...   <div py:if="True">
+  ...     <p>Foo</p>
+  ...   </div>
+  ...   <py:if test="False">
+  ...     <b>Bar</b>
+  ...   </py:if>
+  ...   <py:if test="True">
+  ...     <b>Foo</b>
+  ...   </py:if>
+  ... </div>""")
+  <div>
+    <div>
+      <p>Foo</p>
+    </div>
+  <BLANKLINE>
+      <b>Foo</b>
+  <BLANKLINE>
+  </div>
+
+py:choose, py:when, py:otherwise
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <div py:choose="">
+  ...     <span py:when="0 == 1">0</span>
+  ...     <span py:when="1 == 1">1</span>
+  ...     <div>
+  ...       <span py:when="2 == 2">2</span>
+  ...       <span py:when="2 == 3">3</span>
+  ...       <div py:choose="1">
+  ...          <b py:when="1">3</b>
+  ...          <b py:when="2">4</b>
+  ...       </div>
+  ...     </div>
+  ...     <span py:otherwise="">3</span>
+  ...     <div py:choose="1">
+  ...       <span py:when="0">1</span>
+  ...       <span py:otherwise="">1</span>
+  ...     </div>
+  ...     <div py:choose="">
+  ...       <span py:when="0 == 1">1</span>
+  ...       <span py:otherwise="">2</span>
+  ...     </div>
+  ...   </div>
+  ... </div>""")
+  <div>
+    <div>
+      <span>1</span>
+      <div>
+        <span>2</span>
+        <div>
+           <b>3</b>
+           </div>
+      </div>
+      <div>
+        <span>1</span>
+      </div>
+      <div>
+        <span>2</span>
+      </div>
+    </div>
+  </div>
+
+py:for
+
+  >>> print render("""\
+  ... <ul xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <li py:for="item in range(3)">${item}</li>
+  ...  <py:for each="item in range(3, 5)">
+  ...    <li>${item}</li>
+  ...  </py:for>
+  ... </ul>""")
+  <ul>
+    <li>0</li>
+  <li>1</li>
+  <li>2</li>
+  <li>3</li>
+  <li>4</li>
+  </ul>
+
+py:def
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...  <p py:def="greeting(name)" class="greeting">
+  ...    Hello, ${name}!
+  ...  </p>
+  ...  ${greeting('world')}
+  ...  ${greeting('everyone else')}
+  ...  <py:def function="goodbye(name)">
+  ...    <p class="goodbye">Goodbye, ${name}!</p>
+  ...  </py:def>
+  ...  ${goodbye('world')}
+  ...  ${goodbye('everyone')}  
+  ... </div>""")
+  <div>
+   <p class="greeting">
+     Hello, world!
+   </p>
+  <BLANKLINE>
+   <p class="greeting">
+     Hello, everyone else!
+   </p>
+  <BLANKLINE>
+  <BLANKLINE>
+     <p class="goodbye">Goodbye, world!</p>
+  <BLANKLINE>
+  <BLANKLINE>
+  <BLANKLINE>
+     <p class="goodbye">Goodbye, everyone!</p>
+  <BLANKLINE>
+  <BLANKLINE>
+  </div>
+
+py:with
+
+  >>> def quote():
+  ...     return dict(author=u"Some name", quote=u"Some random quote")
+     
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <span py:with="x=2; y=7; z=x+10">${x} ${y} ${z}</span>
+  ...   <py:with vars="x=4; y=3; z=x+5">${x} ${y} ${z}</py:with>
+  ...   <blockquote py:with="q=quote()">
+  ...       "${q["quote"]} <em>${q["author"]}</em>
+  ...   </blockquote>
+  ... </div>""", quote=quote)
+  <div>
+    <span>2 7 12</span>
+    4 3 9
+    <blockquote>
+        "Some random quote <em>Some name</em>
+    </blockquote>
+  </div>
+
+  
+py:attrs
+  
+  >>> print render("""\
+  ... <ul xmlns="http://www.w3.org/1999/xhtml"
+  ...     xmlns:py="http://genshi.edgewall.org/">
+  ...   <li class="expand" py:attrs="{'class': 'collapse'}">Bar</li>
+  ...   <li class="expand" py:attrs="d">Bar</li>
+  ... </ul>""", d=dict({'class': u'\u1234'}))
+  <ul>
+    <li class="collapse">Bar</li>
+    <li class="ሴ">Bar</li>
+  </ul>
+
+py:content, py:replace
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <span py:content="'Hello, world!'" />
+  ...   <span py:replace="'Goodbye, world!'" />
+  ... </div>""")
+  <div>
+    <span>Hello, world!</span>
+    Goodbye, world!
+  </div>
+
+py:strip
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...    <span py:strip="True"><b>foo</b></span>
+  ... </div>""")
+  <div>
+     <b>foo</b>
+  </div>
+
+py:match
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <span py:match=".//xmlns:greeting">
+  ...     Hello, ${select('@name')[0]}!
+  ...   </span>
+  ...   <py:match path=".//xmlns:farewell">
+  ...      <span>Goodbye, ${select('@name')[0]}!</span>
+  ...   </py:match>
+  ...   <greeting name="dude" />
+  ...   <farewell name="dude" />
+  ... </div>""")
+  <div>
+    <span>
+      Hello, dude!
+    </span>
+  <BLANKLINE>
+  <BLANKLINE>
+       <span>Goodbye, dude!</span>
+  <BLANKLINE>
+  <BLANKLINE>
+  </div>
+
+:: Genshi variable interpolation (${<exp>} notation)
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <span>inter${'pol' + 'ati'}on</span>is ${'convenient'}!
+  ...   <span>${'a'}${'b'}${'c'} ${'d'}</span>
+  ...   <span py:with="hello='Hello'" class="${hello} World!" />
+  ...   <span class="my-${'class'} item${'Last'}" />
+  ...   <a href="${ltr.href}" class="${ltr.iscurrent}">${ltr.letter}</a>
+  ...   <span style="position: ${'abs'}olute"
+  ...         class="my-${'class'} item${'Last'}" />
+  ... </div>""", ltr={'letter': 'A', 'href': '?title=A', 'iscurrent': 'current'})
+    <div>
+      <span>interpolation</span>is convenient!
+      <span>abc d</span>
+      <span class="Hello World!" />
+      <span class="my-class itemLast" />
+      <a href="?title=A" class="current">A</a>
+      <span style="position: absolute" class="my-class itemLast" />
+    </div>
+
+:: Simple variable names do not need { .. }. From `py:with specs`_.
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <span py:with="y=7; z=x+10">$x $y $z</span>
+  ... </div>""", x=42)
+  <div>
+    <span>42 7 52</span>
+  </div>
+
+:: Genshi variable interpolation and unicode values
+    
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   <img alt="${'La Peña'}" />
+  ...   <img alt="Hello ${'La Peña'}" />
+  ...   <img alt="La Peña, oh ${'La Peña'}" />
+  ...   ${unicode('La Pe\xc3\xb1a', 'utf-8').encode('utf-8')}
+  ...   <img alt="${unicode('La Pe\xc3\xb1a', 'utf-8').encode('utf-8')}" />
+  ...   <img alt="Hello ${unicode('La Pe\xc3\xb1a', 'utf-8').encode('utf-8')}!" />
+  ... </div>""", encoding='utf-8')
+  <div>
+    <img alt="La Peña" />
+    <img alt="Hello La Peña" />
+    <img alt="La Peña, oh La Peña" />
+    La Peña
+    <img alt="La Peña" />
+    <img alt="Hello La Peña!" />
+  </div>
+
+:: Unicode combined with attribute expansion
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   <img alt="La Peña" />
+  ...   <img alt="${alt}" />
+  ... </div>""", alt=unicode("La Pe\xc3\xb1a", 'utf-8'))
+  <div>
+    <img alt="La Pe&Atilde;&plusmn;a" />
+    <img alt="La Peña" />
+  </div>
+
+:: Variables containing quotes
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   <strong>"${quote}"</strong>
+  ... </div>""", quote="Hello, World!")
+    <div>
+      <strong>"Hello, World!"</strong>
+    </div>
+  
+:: Variables containing markup
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   ${message}
+  ... </div>""", message="Hello, <em>World</em>!")
+    <div>
+      Hello, <em>World</em>!
+    </div>
+
+:: Variable expansion must preserve XML validity
+
+  >>> from chameleon.core import config
+  >>> config.VALIDATION = True
+  
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   ${message}
+  ... </div>""", message="Hello, <em>World!")
+  Traceback (most recent call last):
+   ...
+  ValidationError: Insertion of 'Hello, <em>World!' is not allowed.
+  
+  >>> config.VALIDATION = False
+  
+:: Unless we are in a CDATA block
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ... /* <![CDATA[ */
+  ...   ${message}
+  ... /* ]]> */
+  ... </div>""", message="Hello, <em>World!")
+    <div>
+      /* <![CDATA[ */
+      Hello, <em>World!
+      /* ]]> */
+    </div>
+
+:: HTML comments
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <!-- a comment -->
+  ...   <!-- a multi-
+  ...        line comment -->
+  ...   <!-- a comment with an ${'expression'} -->
+  ... </div>""")
+  <div>
+    <!-- a comment -->
+    <!-- a multi-
+         line comment -->
+    <!-- a comment with an expression -->
+  </div>
+
+:: Chameleon used to forget to output the ]; after the for loop
+
+  >>> print render("""\
+  ... <script xmlns="http://www.w3.org/1999/xhtml"
+  ...         xmlns:py="http://genshi.edgewall.org/"
+  ...         type="text/javascript">
+  ...  var brands = [
+  ...  <py:for each="brand in []">
+  ...    { value :  "${brand['id']}", "title" : "${brand['title']}" },
+  ...  </py:for>
+  ...  ];
+  ... </script>""")
+  <script type="text/javascript">
+  var brands = [
+  ];
+  </script>
+
+:: Strange looping behaviour
+
+  >>> brands=[dict(id=1, title="One"),
+  ...         dict(id=2, title="Two"),
+  ...         dict(id=3, title="Three")]
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:py="http://genshi.edgewall.org/">
+  ... <script type="text/javascript">
+  ... var brands = [
+  ...     <py:for each="brand in brands">
+  ...         { value :  "${brand['id']}", title : "${brand['title']}" },
+  ...     </py:for>
+  ...     ];
+  ...
+  ... $(document.ready(function() {
+  ...   alert("Hello, World");
+  ... });
+  ... </script>
+  ... </div>""", brands=brands)
+  <div>
+  <script type="text/javascript">
+    var brands = [
+      { value : "1", title : "One" },
+      { value : "2", title : "Two" },
+      { value : "3", title : "Three" },
+      ];
+  <BLANKLINE>
+  $(document.ready(function() {
+    alert("Hello, World");
+  });
+  </script>
+  </div>
+
+:: Very basic recursing
+
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...     xmlns:py="http://genshi.edgewall.org/">
+  ...   <ul py:def="rendermenu(menu)" py:if="menu">
+  ...     ${rendermenu([])}
+  ...   </ul>
+  ... </div>""")
+  <div>
+  </div>
+  
+:: Slightly more complex recursive function calls
+ 
+  >>> print render("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...       xmlns:py="http://genshi.edgewall.org/">
+  ...   <ul py:def="rendermenu(menu)">
+  ...     <li py:for="entry in menu"
+  ...         py:attrs="{'class' : entry['current'] and 'current' or None}">
+  ...       <a py:attrs="dict(href=entry['url'])"
+  ...          py:content="entry['title']">Merken</a>
+  ...       ${rendermenu(entry.get('children', []))}
+  ...     </li>
+  ...   </ul>
+  ...   ${rendermenu(menu)}
+  ... </div>""", menu=[dict(title=u"Menu entry", url="/test", current=True)])
+  <div>
+    <ul>
+      <li class="current">
+        <a href="/test">Menu entry</a>
+        <ul>
+  <BLANKLINE>
+    </ul>
+  <BLANKLINE>
+      </li>
+  <BLANKLINE>
+    </ul>
+  <BLANKLINE>
+  </div>
+
+.. _py:with specs: http://genshi.edgewall.org/wiki/Documentation/0.4.x/xml-templates.html#py-with

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,26 @@
+from chameleon.core import template
+from chameleon.core import config
+
+import language
+
+class GenshiTemplate(template.Template):
+    __doc__ = template.Template.__doc__ # for Sphinx autodoc
+
+    default_parser = language.Parser()
+    
+    def __init__(self, body, parser=None, format=None, doctype=None):
+        if parser is None:
+            parser = self.default_parser
+        super(GenshiTemplate, self).__init__(body, parser, format, doctype)
+
+class GenshiTemplateFile(template.TemplateFile):
+    __doc__ = template.TemplateFile.__doc__ # for Sphinx autodoc
+
+    default_parser = language.Parser()
+    
+    def __init__(self, filename, parser=None, format=None,
+                 doctype=None, **kwargs):
+        if parser is None:
+            parser = self.default_parser
+        super(GenshiTemplateFile, self).__init__(
+            filename, parser, format, doctype, **kwargs)

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.txt
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.txt	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/template.txt	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,84 @@
+Template classes
+================
+
+The ``chameleon.genshi`` package provides the ``GenshiTemplate`` and
+``GenshiTemplateFile`` classes which allow easy usage of templates in
+your application.
+
+Usage
+-----
+
+  >>> from chameleon.genshi.template import GenshiTemplate
+  
+  >>> print GenshiTemplate("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml">
+  ...   Hello World!
+  ... </div>""")()
+  <div>
+    Hello World!
+  </div>
+
+  >>> from chameleon.genshi.template import GenshiTemplateFile
+  >>> from chameleon.genshi import tests
+  
+  >>> path = tests.__path__[0]
+  >>> t = GenshiTemplateFile(path+'/helloworld.html')
+  >>> print t()
+  <div>
+    Hello World!
+  </div>
+
+  >>> import os
+  >>> t.filename.startswith(os.sep)
+  True
+
+Compiler integration
+--------------------
+
+Certain constructs require close collaboration between the template
+compiler and the page template classes.
+
+py:match
+
+  >>> print GenshiTemplate("""\
+  ... <div xmlns="http://www.w3.org/1999/xhtml"
+  ...      xmlns:py="http://genshi.edgewall.org/">
+  ...   <py:match path=".//xmlns:greeting">Hello ${select('@name')[0]}!</py:match>
+  ...   <greeting name="World" />
+  ... </div>
+  ... """)()
+  <div>
+    Hello World!
+  </div>
+
+XInclude-support
+----------------
+
+When using XInclude-statements in Genshi, macro-definitions are
+carried over from the included template. This is demonstrated below.
+
+  >>> template1 = GenshiTemplateFile(path+"/xinclude1.html")
+  >>> template2 = GenshiTemplateFile(path+"/xinclude2.html")
+  
+  >>> print template1()
+  <div>
+    <p class="greeting">
+      Hello, world!
+    </p>
+  </div>
+
+Before we proceed, we reduce and restore the underlying byte-code
+template.
+  
+  >>> from cPickle import dumps, loads
+  >>> for registry in (template1.registry, template2.registry):
+  ...     for key, bct in registry.items():
+  ...         registry[key] = loads(dumps(bct))
+
+  >>> print template1()
+  <div>
+    <p class="greeting">
+      Hello, world!
+    </p>
+  </div>
+  

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/__init__.py
===================================================================

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/helloworld.html
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/helloworld.html	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/helloworld.html	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,3 @@
+<div xmlns="http://www.w3.org/1999/xhtml">
+  Hello World!
+</div>

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/test_doctests.py
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/test_doctests.py	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/test_doctests.py	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,45 @@
+import zope.testing
+import unittest
+
+OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
+               zope.testing.doctest.NORMALIZE_WHITESPACE)
+
+import zope.component.testing
+import zope.configuration.xmlconfig
+
+import chameleon.core.config
+import chameleon.core.testing
+
+import chameleon.genshi
+import chameleon.genshi.language
+
+def render_template(body, **kwargs):
+    parser = chameleon.genshi.language.Parser()
+    return chameleon.core.testing.compile_template(parser, body, **kwargs)
+
+def setUp(suite):
+    zope.component.testing.setUp(suite)
+
+def test_suite():
+    filesuites = 'language.txt', 'template.txt'
+    testsuites = 'language', 'expressions'
+
+    globs = dict(render=render_template)
+    
+    chameleon.core.config.DISK_CACHE = False
+    
+    return unittest.TestSuite(
+        [zope.testing.doctest.DocTestSuite(
+        "chameleon.genshi."+doctest, optionflags=OPTIONFLAGS,
+        setUp=setUp, tearDown=zope.component.testing.tearDown) \
+         for doctest in testsuites] + 
+        
+        [zope.testing.doctest.DocFileSuite(
+        doctest, optionflags=OPTIONFLAGS,
+        globs=globs,
+        setUp=setUp, tearDown=zope.component.testing.tearDown,
+        package="chameleon.genshi") for doctest in filesuites]
+        )
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude1.html
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude1.html	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude1.html	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,6 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:py="http://genshi.edgewall.org/"
+     xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="xinclude${2}.html" />
+  ${greeting('world')}
+</div>

Added: Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude2.html
===================================================================
--- Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude2.html	                        (rev 0)
+++ Sandbox/malthe/chameleon.genshi/src/chameleon/genshi/tests/xinclude2.html	2008-09-20 18:44:40 UTC (rev 91288)
@@ -0,0 +1,7 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:py="http://genshi.edgewall.org/"
+     py:strip="">
+  <p py:def="greeting(name)" class="greeting">
+    Hello, ${name}!
+  </p>
+</div>



More information about the Checkins mailing list