[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ñ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