[Checkins] SVN: z3c.pt/trunk/ Added Genshi-support.
Malthe Borch
mborch at gmail.com
Wed Aug 6 21:09:03 EDT 2008
Log message for revision 89478:
Added Genshi-support.
Changed:
U z3c.pt/trunk/CHANGES.txt
U z3c.pt/trunk/README.txt
U z3c.pt/trunk/setup.py
U z3c.pt/trunk/src/z3c/pt/README.txt
U z3c.pt/trunk/src/z3c/pt/clauses.py
U z3c.pt/trunk/src/z3c/pt/config.py
U z3c.pt/trunk/src/z3c/pt/configure.zcml
U z3c.pt/trunk/src/z3c/pt/expressions.py
U z3c.pt/trunk/src/z3c/pt/generation.py
U z3c.pt/trunk/src/z3c/pt/template.py
U z3c.pt/trunk/src/z3c/pt/testing.py
U z3c.pt/trunk/src/z3c/pt/translation.py
U z3c.pt/trunk/src/z3c/pt/translation.txt
U z3c.pt/trunk/src/z3c/pt/types.py
U z3c.pt/trunk/src/z3c/pt/utils.py
-=-
Modified: z3c.pt/trunk/CHANGES.txt
===================================================================
--- z3c.pt/trunk/CHANGES.txt 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/CHANGES.txt 2008-08-07 01:09:02 UTC (rev 89478)
@@ -1,9 +1,18 @@
Changelog
---------
-Version 0.8.x
+Version 0.9.x
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Version 0.9 - August 7, 2008
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Added support for Genshi-templates.
+ [malthe]
+
+- Cleanup and refactoring of translation module.
+ [malthe]
+
- If the template source contains a DOCTYPE declaration, output it
during rendering. [chrism]
Modified: z3c.pt/trunk/README.txt
===================================================================
--- z3c.pt/trunk/README.txt 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/README.txt 2008-08-07 01:09:02 UTC (rev 89478)
@@ -1,21 +1,25 @@
Overview
--------
-The z3c.pt package provides an alternative implementation of the TAL
-template language including i18n. It also provides a simple text
-template class that allows expression interpolation.
+The z3c.pt package provides a fast template engine that supports the
+following dialects of the attribute template language:
-Casual benchmarks pegs it 12x more performant than ``zope.pagetemplate``.
+* Zope TAL
+* Zope i18n
+* Genshi
+Non-structural documents are supported through Genshi's variable
+interpolation syntax which is also available for XML templates.
+
+Casual benchmarks pegs it 16x more performant than the reference
+implementations for Zope TAL and Genshi.
+
In a nutshell:
-* Templates are parsed and compiled into Python bytecode
-* While rendering only Python code is executed and no parsing happens
+* Templates are serialized and compiled into Python bytecode
* Pluggable expression implementation
-* Support for expression interpolation using the ${<expression>}-format
-* Non-XML friendly
-Note: The METAL macro language is not supported.
+Note: Zope's METAL macro language is not supported.
Usage
@@ -26,14 +30,17 @@
file (configure.zcml).
-Template and expression language
---------------------------------
+Compiler notes
+--------------
-The template and expression language is based loosely on the TAL 1.4
-specification*. Some notable changes:
+The compiler is largely compatible with the targeted dialects. The TAL
+implementation is based on the 1.4 language specification* while the
+Genshi implementation is based on the documents for the 0.5 release**.
-1. Tuples are allowed in the tal:define statement:
+Some notable changes:
+1. Tuple unpacking is allowed when defining variables:
+
tal:define="(a, b, c) [1, 2, 3]"
2. Generators are allowed in tal:repeat statements. Note that the
@@ -48,15 +55,15 @@
can be used instead of ``dictionary['key']``.
-4. Expression interpolation is allowed in attributes and HTML content.
+4. Default expression type can be set using ``tal:default-expression``.
+ This is an alternative to providing the expression type before each
+ expression.
- <a href="mailto:${context.email}">${context.email}</a>
+5. The XPath select function provided to py:match-elements uses lxml
+ and requires the use of the default namespace prefix "xmlns".
-5. Default expression type can be set using ``tal:default-expression``.
- This is an alternative to providing the expression type before each
- expression.
-
.. _TAL: http://wiki.zope.org/ZPT/TALSpecification14
+.. _Genshi: http://genshi.edgewall.org/wiki/Documentation/xml-templates.html
Development
@@ -68,3 +75,5 @@
http://svn.zope.org/z3c.pt/trunk#egg=z3c.pt-dev
+Want to contribute? Join #zope3-dev on Freenode IRC.
+
Modified: z3c.pt/trunk/setup.py
===================================================================
--- z3c.pt/trunk/setup.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/setup.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -1,6 +1,6 @@
from setuptools import setup, find_packages
-version = '0.8.8dev'
+version = '0.9dev'
setup(name='z3c.pt',
version=version,
Modified: z3c.pt/trunk/src/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/README.txt 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/README.txt 2008-08-07 01:09:02 UTC (rev 89478)
@@ -39,14 +39,17 @@
>>> from z3c.pt import PageTemplate
>>> template = PageTemplate("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... Hello World!
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal"
+ ... xmlns:py="http://genshi.edgewall.org">
+ ... <py:match path="xmlns:greeting">Hello ${select('@name')[0]}!</py:match>
+ ... <greeting name="World" />
... </div>
... """)
>>> print template()
<div>
- Hello World!
+ Hello World!
+ <BLANKLINE>
</div>
Providing the path to a template file:
@@ -57,7 +60,8 @@
>>> template = PageTemplateFile(path+'/helloworld.pt')
>>> print template()
<div>
- Hello World!
+ Hello World!
+ <BLANKLINE>
</div>
Keyword-parameters are passed on to the template namespace as-is.
Modified: z3c.pt/trunk/src/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -423,6 +423,19 @@
def uses_variable(self, var):
return False
+class Visit(object):
+ def __init__(self, element):
+ self.element = element
+
+ def begin(self, stream):
+ self.element.visit(skip_macro=False)
+
+ def end(self, stream):
+ pass
+
+ def uses_variable(self, var):
+ return False
+
class Tag(object):
"""
>>> from z3c.pt.generation import CodeIO
@@ -463,7 +476,7 @@
"""
- def __init__(self, tag, attributes={}, selfclosing=False):
+ def __init__(self, tag, attributes={}, selfclosing=False, expression=None):
i = tag.find('}')
if i != -1:
@@ -473,11 +486,11 @@
self.selfclosing = selfclosing
self.attributes = attributes
+ self.expression = expression
def begin(self, stream):
stream.out('<%s' % self.tag)
- # static attributes
static = filter(
lambda (attribute, value): \
not isinstance(value, types.expression),
@@ -494,7 +507,26 @@
escape(expression, '"')))
temp = stream.save()
+ temp2 = stream.save()
+ if self.expression:
+ stream.write("for %s, %s in (%s).items():" % (temp, temp2, self.expression))
+ stream.indent()
+ if unicode_required_flag:
+ stream.write("if isinstance(%s, unicode):" % temp2)
+ stream.indent()
+ stream.escape(temp2)
+ stream.write("_write(' %%s=\"%%s\"' %% (%s, %s))" % (temp, temp2))
+ stream.outdent()
+ stream.write("elif %s is not None:" % temp2)
+ else:
+ stream.write("if %s is not None:" % temp2)
+ stream.indent()
+ stream.write("%s = str(%s)" % (temp2, temp2))
+ stream.escape(temp2)
+ stream.write("_write(' %%s=\"%%s\"' %% (%s, %s))" % (temp, temp2))
+ stream.outdent()
+
for attribute, value in dynamic:
assign = Assign(value)
assign.begin(stream, temp)
@@ -503,24 +535,8 @@
stream.write("if isinstance(%s, unicode):" % temp)
stream.indent()
stream.write("_write(' %s=\"')" % attribute)
- # Inlined escape function
stream.write("_esc = %s" % temp)
- stream.write("if '&' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('&', '&')")
- stream.outdent()
- stream.write("if '<' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('<', '<')")
- stream.outdent()
- stream.write("if '>' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('>', '>')")
- stream.outdent()
- stream.write("if '\"' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('\"', '"')")
- stream.outdent()
+ stream.escape("_esc")
stream.write("_write(_esc)")
stream.write("_write('\"')")
stream.outdent()
@@ -529,31 +545,16 @@
stream.write("if %s is not None:" % temp)
stream.indent()
stream.write("_write(' %s=\"')" % attribute)
- # Inlined escape function
stream.write("_esc = str(%s)" % temp)
- stream.write("if '&' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('&', '&')")
- stream.outdent()
- stream.write("if '<' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('<', '<')")
- stream.outdent()
- stream.write("if '>' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('>', '>')")
- stream.outdent()
- stream.write("if '\"' in _esc:")
- stream.indent()
- stream.write("_esc = _esc.replace('\"', '"')")
- stream.outdent()
+ stream.escape("_esc")
stream.write("_write(_esc)")
stream.write("_write('\"')")
stream.outdent()
assign.end(stream)
stream.restore()
-
+ stream.restore()
+
if self.selfclosing:
stream.out(" />")
else:
@@ -860,3 +861,33 @@
def uses_variable(self, var):
return False
+
+class Method(object):
+ """
+ >>> from z3c.pt.generation import CodeIO
+ >>> from z3c.pt.testing import pyexp
+ >>> from StringIO import StringIO
+
+ >>> _out = StringIO(); _write = _out.write; stream = CodeIO()
+ >>> method = Method('test', ('a', 'b', 'c'))
+ >>> method.begin(stream)
+ >>> stream.write('print a, b, c')
+ >>> method.end(stream)
+ >>> exec stream.getvalue()
+ >>> test(1, 2, 3)
+ 1 2 3
+
+ """
+
+ def __init__(self, name, args):
+ self.name = name
+ self.args = args
+
+ def begin(self, stream):
+ stream.write('def %s(%s):' % (self.name, ", ".join(self.args)))
+ stream.indent()
+
+ def end(self, stream):
+ stream.outdent()
+
+
Modified: z3c.pt/trunk/src/z3c/pt/config.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/config.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/config.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -15,3 +15,11 @@
DISABLE_I18N_KEY = 'Z3C_PT_DISABLE_I18N'
DISABLE_I18N = os.environ.get(DISABLE_I18N_KEY, 'false')
DISABLE_I18N = DISABLE_I18N.lower() in ('yes', 'true', 'on')
+
+XML_NS = "http://www.w3.org/1999/xhtml"
+TAL_NS = "http://xml.zope.org/namespaces/tal"
+I18N_NS = "http://xml.zope.org/namespaces/i18n"
+PY_NS = "http://genshi.edgewall.org"
+NS_MAP = dict(py=PY_NS, tal=PY_NS)
+
+
Modified: z3c.pt/trunk/src/z3c/pt/configure.zcml
===================================================================
--- z3c.pt/trunk/src/z3c/pt/configure.zcml 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/configure.zcml 2008-08-07 01:09:02 UTC (rev 89478)
@@ -4,11 +4,11 @@
<utility
name="python"
- factory=".expressions.PythonTranslation" />
+ component=".expressions.PythonTranslation" />
<utility
name="path"
- factory=".expressions.PathTranslation" />
+ component=".expressions.PathTranslation" />
<adapter
name="string"
Modified: z3c.pt/trunk/src/z3c/pt/expressions.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/expressions.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/expressions.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -19,6 +19,8 @@
re_pragma = re.compile(r'^\s*(?P<pragma>[a-z]+):\s*')
re_interpolation = re.compile(r'(?P<prefix>[^\\]\$|^\$){((?P<expression>.*)})?')
+ re_method = re.compile(r'^(?P<name>[A-Za-z0-9_]+)'
+ '\((?P<args>[A-Za-z0-9_]+\s*(,\s*[A-Za-z0-9_]+)*)\)')
def name(self, string):
return string
@@ -173,7 +175,21 @@
>>> 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;")
@@ -209,16 +225,28 @@
var = self.declaration(string[i+1:j])
j += 1
else:
- j = string.find(' ', i + 1)
- if j == -1:
+ 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])
# get expression
- i = j
+ i = j + len(string) - j - len(string[j:].lstrip())
while j < len(string):
+ token = string[i:]
+ if token.startswith('=='):
+ raise ValueError("Invalid variable definition (%s)." % string)
+ elif token.startswith('='):
+ i += 1
+ elif token.startswith('in'):
+ i += 2
+
j = string.find(';', j+1)
if j == -1:
j = len(string)
@@ -402,6 +430,25 @@
return m
+ def method(self, string):
+ """Parse a method definition.
+
+ >>> method = ExpressionTranslation().method
+
+ >>> method('name(a, b, c)')
+ name(a, b, c)
+
+ """
+
+ m = self.re_method.match(string)
+ if m is None:
+ return None
+
+ name = m.group('name')
+ args = [arg.strip() for arg in m.group('args').split(',')]
+
+ return types.method(name, args)
+
class PythonTranslation(ExpressionTranslation):
def validate(self, string):
"""We use the ``parser`` module to determine if
@@ -414,7 +461,9 @@
string = string.encode('utf-8')
return types.value(string)
-
+
+PythonTranslation = PythonTranslation()
+
class StringTranslation(ExpressionTranslation):
zope.component.adapts(interfaces.IExpressionTranslation)
@@ -576,3 +625,5 @@
value = types.value('not(%s)' % value)
return value
+
+PathTranslation = PathTranslation()
Modified: z3c.pt/trunk/src/z3c/pt/generation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -72,15 +72,20 @@
def __call__(self):
# prepare template arguments
args = self.params
- # We need to ensure we have _context for the i18n handling in the
- # arguments. The default template implementations pass this in
- # explicitly.
+
+ # we need to ensure we have _context for the i18n handling in
+ # the arguments. the default template implementations pass
+ # this in explicitly.
if '_context' not in args:
args = args + ('_context=None', )
args = ', '.join(args)
if args:
args += ', '
+ # pass selectors
+ for selector in self.stream.selectors:
+ args += '%s=None, ' % selector
+
code = self.stream.getvalue()
return wrapper % (args, code), {'generation': z3c.pt.generation}
@@ -112,6 +117,7 @@
self.indentation_string = indentation_string
self.queue = ''
self.scope = [set()]
+ self.selectors = {}
self.annotations = {}
self._variables = {}
@@ -164,6 +170,24 @@
self.cook()
return BufferIO.getvalue(self)
+ def escape(self, variable):
+ self.write("if '&' in %s:" % variable)
+ self.indent()
+ self.write("%s = %s.replace('&', '&')" % (variable, variable))
+ self.outdent()
+ self.write("if '<' in %s:" % variable)
+ self.indent()
+ self.write("%s = %s.replace('<', '<')" % (variable, variable))
+ self.outdent()
+ self.write("if '>' in %s:" % variable)
+ self.indent()
+ self.write("%s = %s.replace('>', '>')" % (variable, variable))
+ self.outdent()
+ self.write("if '\"' in %s:" % variable)
+ self.indent()
+ self.write("%s = %s.replace('\"', '"')" % (variable, variable))
+ self.outdent()
+
def begin(self, clauses):
if isinstance(clauses, (list, tuple)):
for clause in clauses:
Modified: z3c.pt/trunk/src/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/template.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -38,6 +38,7 @@
self.source = source
self.source_write()
+ self.selectors = generator.stream.selectors
self.annotations = generator.stream.annotations
_globals.update(suite._globals)
@@ -48,13 +49,16 @@
return _locals['render']
def render(self, **kwargs):
- # A ''.join of a dict uses only the keys
+ # a ''.join of a dict uses only the keys
signature = self.signature + hash(''.join(kwargs))
template = self.registry.get(signature, None)
if template is None:
self.registry[signature] = template = self.cook(kwargs.keys())
+ # pass in selectors
+ kwargs.update(self.selectors)
+
if PROD_MODE:
return template(**kwargs)
Modified: z3c.pt/trunk/src/z3c/pt/testing.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -1,5 +1,4 @@
import expressions
def pyexp(string):
- translator = expressions.PythonTranslation()
- return translator.expression(string)
+ return expressions.PythonTranslation.expression(string)
Modified: z3c.pt/trunk/src/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -3,46 +3,156 @@
from StringIO import StringIO
import lxml.etree
+import itertools
import generation
import clauses
import interfaces
+import expressions
import types
import utils
+import config
class Element(lxml.etree.ElementBase):
- def begin(self, stream):
- stream.scope.append(set())
- stream.begin(self._clauses())
+ """Base compiler element class.
+
+ This class represents a node in the template tree. To start
+ compilation at this node, use the ``start`` method, providing a
+ code stream object.
+ """
+
+ _stream = None
+
+ def start(self, stream):
+ self._stream = stream
+ self.visit()
+
+ def update(self):
+ self._preprocess_genshi()
+
+ def begin(self):
+ self.stream.scope.append(set())
+ self.stream.begin(self._serialize())
- def end(self, stream):
- stream.end(self._clauses())
- stream.scope.pop()
+ def end(self):
+ self.stream.end(self._serialize())
+ self.stream.scope.pop()
- def body(self, stream):
- skip = self.tal_replace or self.tal_content or self.i18n_translate is not None
+ def body(self):
+ skip = self._replace or self._content or \
+ self.i18n_translate is not None
+
if not skip:
- for element in [e for e in self
- if isinstance(e, lxml.etree._Comment)]:
+ for element in self:
+ element.update()
+
+ for element in self:
+ element.visit()
+
+ def visit(self, skip_macro=True):
+ assert self.stream is not None, "Must use ``start`` method."
+
+ if skip_macro and self.py_def is not None:
+ return
+
+ for element in self:
+ if isinstance(element, lxml.etree._Comment):
self._wrap_comment(element)
- seen = set()
- while len(seen) < len(self):
- element = set(self).difference(seen).pop()
- element.interpolate(stream)
- seen.add(element)
+ self.update()
+ self.begin()
+ self.body()
+ self.end()
+
+ @property
+ def stream(self):
+ root = self.getroottree().getroot()
+ return root._stream
+
+ @property
+ def translator(self):
+ while self.tal_default_expression is None:
+ self = self.getparent()
+ if self is None:
+ raise ValueError("Default expression not set.")
+
+ return component.getUtility(
+ interfaces.IExpressionTranslation, name=self.tal_default_expression)
+
+ def _preprocess_genshi(self):
+ """Genshi preprocessing."""
+
+ stream = self.stream
+
+ # Step 1: Convert py:choose, py:when, py:otherwise into
+ # tal:define, tal:condition
+ choose_expression = self._pull_attribute(utils.py_attr('choose'))
+ if choose_expression is not None:
+ choose_variable = stream.save()
+
+ if choose_expression:
+ self._add_tal_define(choose_variable, choose_expression)
- for element in self:
- element.visit(stream)
+ # 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_tal_define(variable, expression)
+
+ # add condition to element
+ if choose_expression:
+ expression = "python: %s == %s" % (
+ choose_variable, variable)
+ else:
+ expression = "python: %s" % variable
- def visit(self, stream):
- self.begin(stream)
- self.body(stream)
- self.end(stream)
+ element.attrib[utils.tal_attr('condition')] = expression
- def interpolate(self, stream):
- """The current interpolation strategy is to translate the
- interpolation statements into TAL."""
-
+ # 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 = "python: %s not in %s" % (
+ choose_variable, repr(tuple(variables)))
+ else:
+ expression = "python: not(%s)" % " or ".join(variables)
+
+ element.attrib[utils.tal_attr('condition')] = expression
+
+ # Step 2: Process "py:match" macros
+ for element in self:
+ if element.py_match 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().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.tal_attr('replace')] = expression
+
+ # Step 3: Variable interpolation
translator = self.translator
if self.text is not None:
@@ -51,58 +161,48 @@
if m is None:
break
- t = parser.makeelement(
- '{http://xml.zope.org/namespaces/tal}interpolation')
+ t = parser.makeelement(utils.tal_attr('interpolation'))
t.attrib['replace'] = m.group('expression')
t.tail = self.text[m.end():]
self.insert(0, t)
+ t.update()
if m.start() == 0:
self.text = self.text[1:m.start()+1]
else:
self.text = self.text[:m.start()+1]
- # interpolate tail
if self.tail is not None:
while self.tail:
m = translator.interpolate(self.tail)
if m is None:
break
- t = parser.makeelement(
- '{http://xml.zope.org/namespaces/tal}interpolation')
+ t = parser.makeelement(utils.tal_attr('interpolation'))
t.attrib['replace'] = m.group('expression')
t.tail = self.tail[m.end():]
parent = self.getparent()
parent.insert(parent.index(self)+1, t)
-
+ t.update()
+
self.tail = self.tail[:m.start()+len(m.group('prefix'))-1]
- # interpolate attributes
- for name in self._static_attributes():
+ for name in self._get_static_attributes():
value = self.attrib[name]
if translator.interpolate(value):
del self.attrib[name]
- attributes = '{http://xml.zope.org/namespaces/tal}attributes'
+ attributes = utils.tal_attr('attributes')
expr = '%s string: %s' % (name, value)
if attributes in self.attrib:
self.attrib[attributes] += '; %s' % expr
else:
self.attrib[attributes] = expr
- @property
- def translator(self):
- while self.tal_default_expression is None:
- self = self.getparent()
- if self is None:
- raise ValueError("Default expression not set.")
-
- return component.getUtility(
- interfaces.IExpressionTranslation, name=self.tal_default_expression)
-
- def _clauses(self):
+ def _serialize(self):
+ """Serialize element into clause-statements."""
+
_ = []
# i18n domain
@@ -111,17 +211,34 @@
"_domain", types.value(repr(self.i18n_domain))))
# defines
- if self.tal_define is not None:
- for variables, expression in self.tal_define:
+ if self._define is not None:
+ for variables, expression in self._define:
_.append(clauses.Define(variables, expression))
+ # macro
+ for element in tuple(self):
+ if not isinstance(element, Element):
+ continue
+
+ py_def = element.py_def
+ if py_def is not None:
+ # define macro
+ subclauses = []
+ subclauses.append(clauses.Method("_macro", py_def.args))
+ subclauses.append(clauses.Visit(element))
+ _.append(clauses.Group(subclauses))
+
+ # assign to variable
+ _.append(clauses.Define(
+ py_def.name, types.parts((types.value("_macro"),))))
+
# condition
- if self.tal_condition is not None:
- _.append(clauses.Condition(self.tal_condition))
+ if self._condition is not None:
+ _.append(clauses.Condition(self._condition))
# repeat
- if self.tal_repeat is not None:
- variables, expression = self.tal_repeat
+ if self._repeat is not None:
+ variables, expression = self._repeat
if len(variables) != 1:
raise ValueError(
"Cannot unpack more than one variable in a "
@@ -133,20 +250,20 @@
_.append(clauses.Out(self.tail.encode('utf-8'), defer=True))
# compute dynamic flag
- dynamic = (self.tal_replace or
- self.tal_content or
+ dynamic = (self._replace or
+ self._content or
self.i18n_translate is not None)
# tag
- if self.tal_replace is None:
+ if self._replace is None:
selfclosing = self.text is None and not dynamic and len(self) == 0
- tag = clauses.Tag(self.tag, self._attributes(),
- selfclosing=selfclosing)
+ tag = clauses.Tag(self.tag, self._get_attributes(),
+ expression=self.py_attrs, selfclosing=selfclosing)
- if self.tal_omit:
- _.append(clauses.Condition(_not(self.tal_omit), [tag],
+ if self._omit:
+ _.append(clauses.Condition(_not(self._omit), [tag],
finalize=False))
- elif self.tal_omit is not None:
+ elif self._omit is not None:
pass
else:
_.append(tag)
@@ -156,8 +273,8 @@
_.append(clauses.Out(self.text.encode('utf-8')))
# dynamic content and content translation
- replace = self.tal_replace
- content = self.tal_content
+ replace = self._replace
+ content = self._content
if replace and content:
raise ValueError("Can't use replace clause together with "
@@ -180,7 +297,6 @@
# for each named block, create a new output stream
# and use the value in the translation mapping dict
-
elements = [e for e in self if e.i18n_name]
if elements:
@@ -196,7 +312,7 @@
subclauses.append(clauses.Define(
('_out', '_write'),
types.value('generation.initialize_stream()')))
- subclauses.append(clauses.Group(element._clauses()))
+ subclauses.append(clauses.Visit(element))
subclauses.append(clauses.Assign(
types.value('_out.getvalue()'),
"%s['%s']" % (mapping, name)))
@@ -234,9 +350,7 @@
def _wrap_comment(self, element):
index = self.index(element)
- t = parser.makeelement(
- '{http://xml.zope.org/namespaces/tal}comment')
-
+ t = parser.makeelement(utils.tal_attr('comment'))
t.attrib['omit-tag'] = ''
t.tail = element.tail
t.text = '<!--' + element.text + '-->'
@@ -246,6 +360,7 @@
self.remove(element)
self.insert(index, t)
+ t.update()
def _msgid(self):
"""Create an i18n msgid from the tag contents."""
@@ -264,7 +379,7 @@
return msgid
- def _static_attributes(self):
+ def _get_static_attributes(self):
attributes = {}
for key in self.keys():
@@ -273,11 +388,11 @@
return attributes
- def _attributes(self):
+ def _get_attributes(self):
"""Aggregate static, dynamic and translatable attributes."""
# static attributes are at the bottom of the food chain
- attributes = self._static_attributes()
+ attributes = self._get_static_attributes()
# dynamic attributes
attrs = self.tal_attributes
@@ -323,32 +438,94 @@
return attributes
+ def _pull_attribute(self, name, default=None):
+ if name in self.attrib:
+ value = self.attrib[name]
+ del self.attrib[name]
+ return value
+ return default
+
+ def _add_tal_define(self, variable, expression):
+ name = utils.tal_attr('define')
+ define = "%s %s; " % (variable, expression)
+
+ if name in self.attrib:
+ self.attrib[name] += define
+ else:
+ self.attrib[name] = define
+
+ @property
+ def _define(self):
+ return self.tal_define or self.py_with
+
+ @property
+ def _condition(self):
+ return self.tal_condition or self.py_if
+
+ @property
+ def _repeat(self):
+ return self.tal_repeat or self.py_for
+
+ @property
+ def _replace(self):
+ return self.tal_replace or self.py_replace
+
+ @property
+ def _content(self):
+ return self.tal_content or self.py_content
+
+ @property
+ def _omit(self):
+ if self.tal_omit is not None:
+ return self.tal_omit
+ return self.py_strip
+
tal_define = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}define", lambda p: p.definitions)
+ utils.tal_attr('define'), lambda p: p.definitions)
tal_condition = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}condition",
- lambda p: p.expression)
+ utils.tal_attr('condition'), lambda p: p.expression)
tal_repeat = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}repeat", lambda p: p.definition)
+ utils.tal_attr('repeat'), lambda p: p.definition)
tal_attributes = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}attributes",
- lambda p: p.definitions)
+ utils.tal_attr('attributes'), lambda p: p.definitions)
tal_content = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}content", lambda p: p.output)
+ utils.tal_attr('content'), lambda p: p.output)
tal_replace = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}replace", lambda p: p.output)
+ utils.tal_attr('replace'), lambda p: p.output)
tal_omit = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}omit-tag", lambda p: p.expression)
+ utils.tal_attr('omit-tag'), lambda p: p.expression)
tal_default_expression = utils.attribute(
- "{http://xml.zope.org/namespaces/tal}default-expression")
+ utils.tal_attr('default-expression'))
i18n_translate = utils.attribute(
- "{http://xml.zope.org/namespaces/i18n}translate")
+ utils.i18n_attr('translate'))
i18n_attributes = utils.attribute(
- "{http://xml.zope.org/namespaces/i18n}attributes", lambda p: p.mapping)
+ utils.i18n_attr('attributes'), lambda p: p.mapping)
i18n_domain = utils.attribute(
- "{http://xml.zope.org/namespaces/i18n}domain")
+ utils.i18n_attr('domain'))
i18n_name = utils.attribute(
- "{http://xml.zope.org/namespaces/i18n}name")
+ utils.i18n_attr('name'))
+ 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.PythonTranslation.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)
class TALElement(Element):
tal_define = utils.attribute("define", lambda p: p.definitions)
@@ -360,7 +537,7 @@
tal_omit = utils.attribute("omit-tag", lambda p: p.expression, u"")
tal_default_expression = utils.attribute("default-expression", lambda p: p.name)
- def _static_attributes(self):
+ def _get_static_attributes(self):
attributes = {}
for key in self.keys():
@@ -377,19 +554,42 @@
return attributes
+class PyElement(Element):
+ tal_omit = utils.attribute("omit-tag", 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.PythonTranslation.definitions)
+
+class PyDefElement(PyElement):
+ py_def = utils.attribute("function", lambda p: p.method)
+
+class PyMatchElement(PyElement):
+ py_match = utils.attribute("path")
+
# set up namespaces for XML parsing
lookup = lxml.etree.ElementNamespaceClassLookup()
parser = lxml.etree.XMLParser()
parser.setElementClassLookup(lookup)
try:
- lookup.get_namespace('http://www.w3.org/1999/xhtml')[None] = Element
- lookup.get_namespace('http://xml.zope.org/namespaces/tal'
- )[None] = TALElement
+ ns_lookup = lookup.get_namespace
except AttributeError:
- lxml.etree.Namespace('http://www.w3.org/1999/xhtml')[None] = Element
- lxml.etree.Namespace('http://xml.zope.org/namespaces/tal'
- )[None] = TALElement
+ ns_lookup = lxml.etree.Namespace
+
+ns_lookup(config.XML_NS)[None] = Element
+ns_lookup(config.TAL_NS)[None] = TALElement
+ns_lookup(config.PY_NS)["if"] = PyIfElement
+ns_lookup(config.PY_NS)["for"] = PyForElement
+ns_lookup(config.PY_NS)["def"] = PyDefElement
+ns_lookup(config.PY_NS)["with"] = PyWithElement
+ns_lookup(config.PY_NS)["match"] = PyMatchElement
def translate_xml(body, *args, **kwargs):
tree = lxml.etree.parse(StringIO(body), parser)
@@ -401,7 +601,7 @@
raise ValueError, "Must set default namespace."
# set default expression name
- key = '{http://xml.zope.org/namespaces/tal}default-expression'
+ key = utils.tal_attr('default-expression')
if key not in root.attrib:
root.attrib[key] = default_expression
@@ -409,9 +609,6 @@
generator = generation.Generator(params)
stream = generator.stream
- # visit root
- root.interpolate(stream)
-
# output doctype if any
tree = root.getroottree()
if tree.docinfo.doctype:
@@ -422,16 +619,15 @@
stream.end([doctype])
stream.scope.pop()
- root.visit(stream)
+ root.start(stream)
return generator
def translate_text(body, *args, **kwargs):
xml = parser.makeelement(
- '{http://www.w3.org/1999/xhtml}text',
- nsmap={None: 'http://www.w3.org/1999/xhtml'})
+ utils.xml_attr('text'), nsmap={None: config.XML_NS})
xml.text = body
- xml.attrib['{http://xml.zope.org/namespaces/tal}omit-tag'] = ''
+ xml.attrib[utils.tal_attr('omit-tag')] = ''
return translate_etree(xml, *args, **kwargs)
def _translate(value, mapping=None, default=None):
Modified: z3c.pt/trunk/src/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.txt 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/translation.txt 2008-08-07 01:09:02 UTC (rev 89478)
@@ -11,41 +11,50 @@
... _locals = {}
... _globals.update(kwargs)
... exec source in _globals, _locals
- ... return _locals['render']()
+ ... return _locals['render'](**generator.stream.selectors)
-TAL
----
+ >>> from z3c.pt.translation import translate_xml
-We use the XML translator.
+XHTML
+-----
- >>> from z3c.pt.translation import translate_xml
+:: Plain HTML document
-Basic HTML:
-
>>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <img alt="'Hello World!'" />
+ ... <div xmlns="http://www.w3.org/1999/xhtml">
+ ... Hello World!
... </div>""", translate_xml)
<div>
- <img alt="'Hello World!'" />
+ Hello World!
</div>
-With DOCTYPE:
+:: Setting DOCTYPE
>>> print render("""\
... <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
... "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- ... <html xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <img alt="'Hello World!'" />
+ ... <html xmlns="http://www.w3.org/1999/xhtml">
+ ... Hello World!
... </html>""", translate_xml)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
- <img alt="'Hello World!'" />
+ Hello World!
</html>
-
-Attributes and contents:
+
+:: Unicode
+
+ >>> print render("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml">
+ ... <img alt="La Peña" />
+ ... </div>""", translate_xml)
+ <div>
+ <img alt="La Peña" />
+ </div>
+
+Zope TAL
+--------
+
+tal:define, tal:attributes, tal:contents
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -64,43 +73,43 @@
<span></span>
</div>
-Repeats:
-
+tal:repeat
+
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:tal="http://xml.zope.org/namespaces/tal">
... <ul>
- ... <li tal:repeat="i range(5)"><span tal:replace="str(i) + ' ' + str(repeat['i'].even())" /></li>
+ ... <li tal:repeat="i range(5)"><span tal:replace="'Item ' + str(i) + ')'" /></li>
... </ul>
... </div>""", translate_xml)
<div>
<ul>
- <li>0 True</li>
- <li>1 False</li>
- <li>2 True</li>
- <li>3 False</li>
- <li>4 True</li>
+ <li>Item 0)</li>
+ <li>Item 1)</li>
+ <li>Item 2)</li>
+ <li>Item 3)</li>
+ <li>Item 4)</li>
</ul>
</div>
+tal:repeat (repeat-variable)
+
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:tal="http://xml.zope.org/namespaces/tal">
... <ul>
- ... <li tal:repeat="i range(5)"><span tal:replace="'Item ' + str(i) + ')'" /></li>
+ ... <li tal:repeat="i range(3)"><span tal:replace="str(i) + ' ' + str(repeat['i'].even())" /></li>
... </ul>
... </div>""", translate_xml)
<div>
<ul>
- <li>Item 0)</li>
- <li>Item 1)</li>
- <li>Item 2)</li>
- <li>Item 3)</li>
- <li>Item 4)</li>
+ <li>0 True</li>
+ <li>1 False</li>
+ <li>2 True</li>
</ul>
</div>
-Conditions:
+tal:condition
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -118,7 +127,7 @@
</div>
</div>
-Comments:
+:: HTML comments
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -135,7 +144,7 @@
<!-- a comment with an expression -->
</div>
-Namespacing:
+:: TAL elements with namespace prefix
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -154,7 +163,7 @@
True
</div>
-Omitting tags:
+tal:omit-tag
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -169,20 +178,11 @@
<p>A paragraph here.</p>
</div>
-Unicode:
-
+:: Unicode with dynamic attributes and content
+
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <img alt="La Peña" />
- ... </div>""", translate_xml)
- <div>
- <img alt="La Peña" />
- </div>
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
... <img tal:attributes="title '%sHello%s' % (chr(60), chr(62))" />
... <span tal:replace="structure '%sbr /%s' % (chr(60), chr(62))" />
... <span tal:replace="'%sbr /%s' % (chr(60), chr(62))" />
@@ -195,50 +195,11 @@
<span>La Peña</span>
</div>
-Interpolation:
+:: Setting default expression
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <span>inter${'pol' + 'ati'}on</span>is ${int('test') | 'convenient'}!
- ... <span>${'a'}${'b'}${'c'} ${'d'}</span>
- ... <span tal:define="hello 'Hello'" class="${hello} World!" />
- ... <span class="my-${'class'} item${'Last'}" />
- ... <span style="position: ${'abs'}olute"
- ... class="my-${int('test') | 'class'} item${'Last'}" />
- ... </div>""", translate_xml)
- <div>
- <span>interpolation</span>is convenient!
- <span>abc d</span>
- <span class="Hello World!" />
- <span class="my-class itemLast" />
- <span style="position: absolute" class="my-class itemLast" />
- </div>
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <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>""", translate_xml)
- <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>
-
-Changing default expression:
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
... <tal:path-expression-testing
... define="request object();
... mydict {'a': 1, 'c': {'a': 2}}">
@@ -269,7 +230,7 @@
<BLANKLINE>
</div>
-Pragmas:
+:: Using TAL pragmas "nocall" and "structure"
>>> print render("""\
... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -283,6 +244,248 @@
<built-in function dir>
</div>
+Genshi
+------
+
+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>""", translate_xml)
+ <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>""", translate_xml)
+ <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>""", translate_xml)
+ <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>""", translate_xml)
+ <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
+
+ >>> 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>
+ ... </div>""", translate_xml)
+ <div>
+ <span>2 7 12</span>
+ 4 3 9
+ </div>
+
+py:attrs
+
+ >>> print render("""\
+ ... <ul xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:py="http://genshi.edgewall.org">
+ ... <li py:attrs="{'class': 'collapse'}">Bar</li>
+ ... </ul>""", translate_xml)
+ <ul>
+ <li class="collapse">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>""", translate_xml)
+ <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>""", translate_xml)
+ <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>""", translate_xml)
+ <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:tal="http://xml.zope.org/namespaces/tal">
+ ... <span>inter${'pol' + 'ati'}on</span>is ${int('test') | 'convenient'}!
+ ... <span>${'a'}${'b'}${'c'} ${'d'}</span>
+ ... <span tal:define="hello 'Hello'" class="${hello} World!" />
+ ... <span class="my-${'class'} item${'Last'}" />
+ ... <span style="position: ${'abs'}olute"
+ ... class="my-${int('test') | 'class'} item${'Last'}" />
+ ... </div>""", translate_xml)
+ <div>
+ <span>interpolation</span>is convenient!
+ <span>abc d</span>
+ <span class="Hello World!" />
+ <span class="my-class itemLast" />
+ <span style="position: absolute" class="my-class itemLast" />
+ </div>
+
+:: Genshi variable interpolation and unicode values
+
+ >>> print render("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <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>""", translate_xml)
+ <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>
+
Text templates
--------------
Modified: z3c.pt/trunk/src/z3c/pt/types.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/types.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/types.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -28,3 +28,12 @@
class escape(parts):
def __repr__(self):
return 'escape'+tuple.__repr__(self)
+
+class method(object):
+ def __init__(self, name, args):
+ self.name = name
+ self.args = args
+
+ def __repr__(self):
+ return "%s(%s)" % (self.name, ", ".join(arg for arg in self.args))
+
Modified: z3c.pt/trunk/src/z3c/pt/utils.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/utils.py 2008-08-07 00:12:42 UTC (rev 89477)
+++ z3c.pt/trunk/src/z3c/pt/utils.py 2008-08-07 01:09:02 UTC (rev 89478)
@@ -1,5 +1,6 @@
import sys
import logging
+import config
# check if we're able to coerce unicode to str
try:
@@ -31,7 +32,6 @@
if value is not None:
if factory is None:
return value
-
f = factory(self.translator)
return f(value)
elif default is not None:
@@ -99,3 +99,15 @@
self[key] = (value, length)
return value
+
+def xml_attr(name):
+ return "{%s}%s" % (config.XML_NS, name)
+
+def tal_attr(name):
+ return "{%s}%s" % (config.TAL_NS, name)
+
+def i18n_attr(name):
+ return "{%s}%s" % (config.I18N_NS, name)
+
+def py_attr(name):
+ return "{%s}%s" % (config.PY_NS, name)
More information about the Checkins
mailing list