[Checkins] SVN: z3c.pt/trunk/ Split out element compiler classes; refactored large parts of the codebase.
Malthe Borch
mborch at gmail.com
Fri Aug 15 19:50:06 EDT 2008
Log message for revision 89902:
Split out element compiler classes; refactored large parts of the codebase.
Changed:
U z3c.pt/trunk/CHANGES.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/etree.py
U z3c.pt/trunk/src/z3c/pt/generation.py
A z3c.pt/trunk/src/z3c/pt/genshi.py
A z3c.pt/trunk/src/z3c/pt/genshi.txt
U z3c.pt/trunk/src/z3c/pt/template.py
U z3c.pt/trunk/src/z3c/pt/template.txt
U z3c.pt/trunk/src/z3c/pt/testing.py
U z3c.pt/trunk/src/z3c/pt/tests/test_doctests.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/utils.py
A z3c.pt/trunk/src/z3c/pt/zpt.py
A z3c.pt/trunk/src/z3c/pt/zpt.txt
-=-
Modified: z3c.pt/trunk/CHANGES.txt
===================================================================
--- z3c.pt/trunk/CHANGES.txt 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/CHANGES.txt 2008-08-15 23:50:05 UTC (rev 89902)
@@ -4,6 +4,12 @@
Version 1.0dev
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+- Substantially reduced compiler overhead for lxml CDATA
+ workaround. [malthe]
+
+- Split out element compiler classes for Genshi and Zope language
+ dialects. [malthe]
+
- Make lxml a setuptools "extra". To install with lxml support
(currently required by Genshi), specify "z3c.pt [lxml]" in
any references you need to make to the package in buildout or
Modified: z3c.pt/trunk/src/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -458,7 +458,7 @@
"""
- def __init__(self, tag, attributes={},
+ def __init__(self, tag, attributes=None,
selfclosing=False, expression=None, cdata=False):
i = tag.find('}')
@@ -468,7 +468,7 @@
self.tag = tag
self.selfclosing = selfclosing
- self.attributes = attributes
+ self.attributes = attributes or {}
self.expression = expression and Assign(expression)
self.cdata = cdata
Modified: z3c.pt/trunk/src/z3c/pt/config.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/config.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/config.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -14,9 +14,10 @@
DISABLE_I18N = os.environ.get(DISABLE_I18N_KEY, 'false')
DISABLE_I18N = DISABLE_I18N.lower() in truevals
-XML_NS = "http://www.w3.org/1999/xhtml"
+XHTML_NS = "http://www.w3.org/1999/xhtml"
TAL_NS = "http://xml.zope.org/namespaces/tal"
-METAL_NS = "http://xml.zope.org/namespaces/metal"
+META_NS = "http://xml.zope.org/namespaces/meta"
+METAL_NS = "http://xml.zope.org/namespaces/metal"
I18N_NS = "http://xml.zope.org/namespaces/i18n"
PY_NS = "http://genshi.edgewall.org"
NS_MAP = dict(py=PY_NS, tal=TAL_NS, metal=METAL_NS)
Modified: z3c.pt/trunk/src/z3c/pt/etree.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/etree.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/etree.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -2,6 +2,7 @@
import config
import utils
import cgi
+import copy
from StringIO import StringIO
def import_elementtree():
@@ -15,23 +16,18 @@
return ET
+class Parser(object):
+ element_mapping = {}
+
+ @classmethod
+ def parse(self, body):
+ global parse
+ return parse(body, self.element_mapping)
+
try:
import lxml.etree
- lookup = lxml.etree.ElementNamespaceClassLookup()
- parser = lxml.etree.XMLParser(resolve_entities=False, strip_cdata=False)
- parser.setElementClassLookup(lookup)
-
- # lxml 1.3-compatibility
- try:
- ns_lookup = lookup.get_namespace
- except AttributeError:
- ns_lookup = lxml.etree.Namespace
-
class ElementBase(lxml.etree.ElementBase):
- def _init(self):
- self._convert_cdata_sections()
-
def tostring(self):
return lxml.etree.tostring(self)
@@ -46,27 +42,29 @@
before, rest = text.split(start, 1)
cdata, after = rest.split(end, 1)
- element = parser.makeelement(
- utils.xml_attr('cdata'))
- element.attrib[utils.tal_attr('cdata')] = ""
+ element = self.makeelement(
+ utils.xhtml_attr('cdata'))
+ element.attrib[utils.meta_attr('cdata')] = ""
element.text = cdata
element.tail = after
self.text = before
self.insert(0, element)
+ element._convert_cdata_sections()
if start in tail:
before, rest = tail.split(start, 1)
cdata, after = rest.split(end, 1)
- element = parser.makeelement(
- utils.xml_attr('cdata'))
- element.attrib[utils.tal_attr('cdata')] = ""
+ element = self.makeelement(
+ utils.xhtml_attr('cdata'))
+ element.attrib[utils.meta_attr('cdata')] = ""
self.addnext(element)
element.text = cdata
element.tail = after
self.tail = before
+ element._convert_cdata_sections()
@property
def _raw_text(self):
@@ -81,12 +79,13 @@
if self.text in ("", None):
return self.text
+
elements = tuple(self)
del self[:]
xml = lxml.etree.tostring(self, encoding='utf-8', with_tail=False)
self.extend(elements)
-
- element = parser.makeelement(self.tag, nsmap=self.nsmap)
+
+ element = self.makeelement(self.tag, nsmap=self.nsmap)
for attr, value in self.items():
element.attrib[attr] = value
@@ -119,7 +118,7 @@
# wrap element
index = parent.index(self)
- element = parser.makeelement(self.tag, nsmap=self.nsmap)
+ element = self.makeelement(self.tag, nsmap=self.nsmap)
element.append(self)
xml = lxml.etree.tostring(element, encoding='utf-8', with_tail=False)
self.extend(elements)
@@ -140,12 +139,42 @@
tail = xml[length+tag:-tag-1]
return tail
+
+ def convert_cdata_section(node):
+ parent = node.getparent()
+ if parent is not None:
+ index = parent.index(node)
+ element = node.makeelement(node.tag, nsmap=node.nsmap)
+ element.append(node)
+ xml = lxml.etree.tostring(element, encoding='utf-8', with_tail=False)
+ parent.insert(index, node)
+ else:
+ xml = lxml.etree.tostring(node, encoding='utf-8', with_tail=False)
- element_factory = parser.makeelement
+ if 'CDATA' in xml:
+ node._convert_cdata_sections()
+ for child in tuple(node):
+ convert_cdata_section(child)
+
+ def parse(body, element_mapping):
+ lookup = lxml.etree.ElementNamespaceClassLookup()
+ parser = lxml.etree.XMLParser(resolve_entities=False, strip_cdata=False)
+ parser.setElementClassLookup(lookup)
- def parse(body):
+ # lxml 1.3-compatibility
+ try:
+ ns_lookup = lookup.get_namespace
+ except AttributeError:
+ ns_lookup = lxml.etree.Namespace
+
+ for key, mapping in element_mapping.items():
+ ns_lookup(key).update(mapping)
+
tree = lxml.etree.parse(StringIO(body), parser)
root = tree.getroot()
+
+ convert_cdata_section(root)
+
return root, tree.docinfo.doctype
except ImportError:
@@ -184,7 +213,7 @@
@property
def nsmap(self):
# TODO: Return correct namespace map
- return {None: config.XML_NS}
+ return {None: config.XHTML_NS}
@property
def prefix(self):
@@ -209,7 +238,8 @@
parent = None
elem = ET.TreeBuilder.start(self, tag, attrs)
elem._parent = parent
-
+ elem.makeelement = self._factory
+
class XMLParser(ET.XMLParser):
def __init__(self, **kwargs):
ET.XMLParser.__init__(self, **kwargs)
@@ -238,33 +268,33 @@
self._target.end(name)
def handle_cdata_start(self):
- self._target.start(utils.xml_attr('cdata'), {
+ self._target.start(utils.xhtml_attr('cdata'), {
utils.tal_attr('cdata'): ''})
def handle_cdata_end(self):
- self._target.end(utils.xml_attr('cdata'))
+ self._target.end(utils.xhtml_attr('cdata'))
- def element_factory(tag, attrs=None, nsmap=None):
- if attrs is None:
- attrs = {}
+ def parse(body, element_mapping):
+ def element_factory(tag, attrs=None, nsmap=None):
+ if attrs is None:
+ attrs = {}
- if '{' in tag:
- ns = tag[tag.find('{')+1:tag.find('}')]
- ns_tag = tag[tag.find('}')+1:]
- else:
- ns = None
- ns_tag = None
+ if '{' in tag:
+ ns = tag[tag.find('{')+1:tag.find('}')]
+ ns_tag = tag[tag.find('}')+1:]
+ else:
+ ns = None
+ ns_tag = None
- namespace = ns_lookup(ns)
- factory = namespace.get(ns_tag) or namespace.get(None) or ElementBase
-
- element = object.__new__(factory)
- element.__init__(tag, attrs)
- return element
+ namespace = element_mapping[ns]
+ factory = namespace.get(ns_tag) or namespace.get(None) or ElementBase
- def parse(body):
+ element = object.__new__(factory)
+ element.__init__(tag, attrs)
+
+ return element
+
target = TreeBuilder(element_factory=element_factory)
-
parser = XMLParser(target=target)
parser.entity = dict([(name, "&%s;" % name) for name in htmlentitydefs.entitydefs])
parser.feed(body)
Modified: z3c.pt/trunk/src/z3c/pt/generation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/generation.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -210,7 +210,7 @@
self.begin(clause)
else:
clauses.begin(self)
-
+
def end(self, clauses):
if isinstance(clauses, (list, tuple)):
for clause in reversed(clauses):
Added: z3c.pt/trunk/src/z3c/pt/genshi.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/genshi.py (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/genshi.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -0,0 +1,218 @@
+import translation
+import expressions
+import utils
+import config
+import etree
+
+class GenshiElement(translation.Element, translation.VariableInterpolation):
+ """Genshi element.
+
+ Implements the Genshi subset of the attribute template language.
+ """
+
+ translator = expressions.PythonTranslation
+
+ class node(object):
+ def __init__(self, element):
+ self.element = element
+
+ @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
+
+ @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):
+ return self.element.meta_attributes
+
+ translated_attributes = None
+
+ @property
+ def static_attributes(self):
+ return utils.get_attributes_from_namespace(
+ self.element, config.XHTML_NS)
+
+ translate = None
+ translation_domain = None
+
+ @property
+ def method(self):
+ return self.element.py_def
+
+ use_macro = None
+ define_macro = None
+ define_slot = None
+ fill_slot = None
+
+ @property
+ def cdata(self):
+ return self.element.meta_cdata
+
+ 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 = "python: %s == %s" % (
+ choose_variable, variable)
+ else:
+ expression = "python: %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 = "python: %s not in %s" % (
+ choose_variable, repr(tuple(variables)))
+ else:
+ expression = "python: not(%s)" % " or ".join(variables)
+
+ element.attrib[utils.py_attr('if')] = 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.py_attr('replace')] = expression
+
+ # Step 3: Variable interpolation
+ translation.VariableInterpolation.update(self)
+
+ 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
+
+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.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 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.PythonTranslation.definitions)
+
+class PyDefElement(PyElement):
+ py_def = utils.attribute("function", lambda p: p.method)
+
+class PyMatchElement(PyElement):
+ py_match = utils.attribute("path")
+
+class GenshiParser(etree.Parser):
+ element_mapping = {
+ config.XHTML_NS: {None: XHTMLElement},
+ config.META_NS: {None: XHTMLElement},
+ config.PY_NS: {'if': PyIfElement,
+ 'for': PyForElement,
+ 'def': PyDefElement,
+ 'with': PyWithElement,
+ 'match': PyMatchElement}}
Added: z3c.pt/trunk/src/z3c/pt/genshi.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/genshi.txt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/genshi.txt 2008-08-15 23:50:05 UTC (rev 89902)
@@ -0,0 +1,299 @@
+Genshi
+======
+
+ >>> from z3c.pt.testing import render_genshi
+
+py:if
+
+ >>> print render_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <ul xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:py="http://genshi.edgewall.org">
+ ... <li class="expand" py:attrs="{'class': 'collapse'}">Bar</li>
+ ... </ul>""")
+ <ul>
+ <li class="collapse">Bar</li>
+ </ul>
+
+py:content, py:replace
+
+ >>> print render_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <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_genshi("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:py="http://genshi.edgewall.org">
+ ... <span>inter${'pol' + 'ati'}on</span>is ${int('test') | 'convenient'}!
+ ... <span>${'a'}${'b'}${'c'} ${'d'}</span>
+ ... <span py:with="hello='Hello'" class="${hello} World!" />
+ ... <span class="my-${'class'} item${'Last'}" />
+ ... <span style="position: ${'abs'}olute"
+ ... class="my-${int('test') | 'class'} item${'Last'}" />
+ ... </div>""")
+ <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_genshi("""\
+ ... <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>""")
+ <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>
+
+:: Variables containing quotes
+ >> print render_xhtml("""\
+ ... <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_genshi("""\
+ ... <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 z3c.pt import config
+ >>> config.VALIDATION = True
+
+ >>> print render_genshi("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml">
+ ... ${message}
+ ... </div>""", message="Hello, <em>World!")
+ Traceback (most recent call last):
+ ...
+ ExpatError: ...
+
+ >>> config.VALIDATION = False
+
+:: Unless we are in a CDATA block
+
+ >>> print render_genshi("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml">
+ ... /* <![CDATA[ */
+ ... ${message}
+ ... /* ]]> */
+ ... </div>""", message="Hello, <em>World!")
+ <div>
+ /* <![CDATA[ */
+ Hello, <em>World!
+ /* ]]> */
+ </div>
Modified: z3c.pt/trunk/src/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/template.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -3,6 +3,7 @@
import macro
import codegen
import traceback
+import zpt
from z3c.pt.config import DEBUG_MODE, PROD_MODE
from z3c.pt import filecache
@@ -17,9 +18,10 @@
registry = {}
cachedir = None
+ parser = zpt.ZopePageTemplateParser
default_expression = 'python'
- def __init__(self, body, default_expression=None):
+ def __init__(self, body, parser=None, default_expression=None):
self.body = body
self.signature = hash(body)
self.source = ''
@@ -27,6 +29,9 @@
if default_expression:
self.default_expression = default_expression
+ if parser:
+ self.parser = parser
+
@property
def translate(self):
return NotImplementedError("Must be implemented by subclass.")
@@ -37,7 +42,7 @@
def cook(self, params, macro=None):
generator = self.translate(
- self.body, macro=macro, params=params,
+ self.body, self.parser, macro=macro, params=params,
default_expression=self.default_expression)
source, _globals = generator()
@@ -127,8 +132,9 @@
default if nothing is passed."""
def __init__(self, filename, auto_reload=False, cachedir=None,
- default_expression=None):
- BaseTemplate.__init__(self, None, default_expression=default_expression)
+ parser=None, default_expression=None):
+ BaseTemplate.__init__(
+ self, None, parser=parser, default_expression=default_expression)
self.auto_reload = auto_reload
self.cachedir = cachedir
Modified: z3c.pt/trunk/src/z3c/pt/template.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-15 23:50:05 UTC (rev 89902)
@@ -7,7 +7,8 @@
--------------------------------
>>> from z3c.pt import PageTemplate
-
+ >>> from z3c.pt.testing import MockParser
+
>>> print PageTemplate("""\
... <div xmlns="http://www.w3.org/1999/xhtml">
... Hello World!
@@ -38,7 +39,8 @@
>>> from z3c.pt import ViewPageTemplate
>>> class ViewPageTemplateView(object):
- ... __call__ = ViewPageTemplate(open(path+'/view.pt').read())
+ ... __call__ = ViewPageTemplate(
+ ... open(path+'/view.pt').read())
... request = u'request'
... context = u'context'
@@ -114,13 +116,15 @@
py:match integration
+ >>> from z3c.pt.genshi import GenshiParser
+
>>> print PageTemplate("""\
... <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>
- ... """)()
+ ... """, parser=GenshiParser)()
<div>
Hello World!
<BLANKLINE>
Modified: z3c.pt/trunk/src/z3c/pt/testing.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -1,6 +1,13 @@
+import translation
import expressions
import macro
+import etree
+import config
+import utils
+import zpt
+import genshi
+
def pyexp(string):
return expressions.PythonTranslation.expression(string)
@@ -10,24 +17,80 @@
exec source in _globals, _locals
return _locals['render']
-def render(body, translator, **kwargs):
- generator = translator(body, params=sorted(kwargs.keys()))
- return _render(generator, **kwargs)
-
def _render(generator, **kwargs):
cooked = cook(generator, **kwargs)
kwargs.update(generator.stream.selectors)
return cooked(**kwargs)
+def render_xhtml(body, **kwargs):
+ generator = translation.translate_xml(
+ body, MockParser, params=sorted(kwargs.keys()))
+ return _render(generator, **kwargs)
+
+def render_text(body, **kwargs):
+ generator = translation.translate_text(
+ body, MockParser, params=sorted(kwargs.keys()))
+ return _render(generator, **kwargs)
+
+def render_zpt(body, **kwargs):
+ generator = translation.translate_xml(
+ body, zpt.ZopePageTemplateParser, params=sorted(kwargs.keys()))
+ return _render(generator, **kwargs)
+
+def render_genshi(body, **kwargs):
+ generator = translation.translate_xml(
+ body, genshi.GenshiParser, params=sorted(kwargs.keys()))
+ return _render(generator, **kwargs)
+
class MockTemplate(object):
- def __init__(self, body, translator):
+ def __init__(self, body, parser):
self.body = body
- self.translator = translator
+ self.parser = parser
@property
def macros(self):
def render(macro=None, **kwargs):
- generator = self.translator(
- self.body, macro=macro, params=kwargs.keys())
+ generator = translation.translate_xml(
+ self.body, self.parser, macro=macro, params=kwargs.keys())
return _render(generator, **kwargs)
return macro.Macros(render)
+
+class MockElement(translation.Element, translation.VariableInterpolation):
+ translator = expressions.PythonTranslation
+
+ def update(self):
+ translation.VariableInterpolation.update(self)
+
+ class node(object):
+ def __init__(self, element):
+ self.element = element
+
+ def __getattr__(self, name):
+ return None
+
+ @property
+ def static_attributes(self):
+ return utils.get_attributes_from_namespace(
+ self.element, config.XHTML_NS)
+
+ @property
+ def omit(self):
+ if self.element.meta_omit is not None:
+ return self.element.meta_omit or True
+ if self.content:
+ return True
+
+ @property
+ def content(self):
+ return self.element.meta_replace
+
+ @property
+ def cdata(self):
+ return self.element.meta_cdata
+
+ node = property(node)
+
+class MockParser(etree.Parser):
+ element_mapping = {
+ config.XHTML_NS: {None: MockElement},
+ config.META_NS: {None: MockElement}}
Modified: z3c.pt/trunk/src/z3c/pt/tests/test_doctests.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/tests/test_doctests.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/tests/test_doctests.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -14,7 +14,8 @@
zope.configuration.xmlconfig.XMLConfig('configure.zcml', z3c.pt)()
def test_suite():
- filesuites = ('template.txt', 'translation.txt', 'i18n.txt', 'codegen.txt')
+ filesuites = ('zpt.txt', 'genshi.txt', 'template.txt',
+ 'i18n.txt', 'codegen.txt', 'translation.txt')
testsuites = ('z3c.pt.translation', 'z3c.pt.clauses', 'z3c.pt.expressions')
return unittest.TestSuite(
Modified: z3c.pt/trunk/src/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -21,14 +21,16 @@
"""
metal_slot_prefix = '_fill'
-
+ metal_variable = '_metal'
+ macro_variable = '_macro'
+
def start(self, stream):
self._stream = stream
self.visit()
def update(self):
- self._preprocess_genshi()
-
+ pass
+
def begin(self):
self.stream.scope.append(set())
self.stream.begin(self._serialize())
@@ -38,11 +40,7 @@
self.stream.scope.pop()
def body(self):
- skip = self._replace or self._content or \
- self.metal_define or self.metal_use or \
- self.i18n_translate is not None
-
- if not skip:
+ if not self.node.skip:
for element in self:
element.update()
@@ -52,8 +50,7 @@
def visit(self, skip_macro=True):
assert self.stream is not None, "Must use ``start`` method."
- macro = self.py_def or self.metal_define
- if skip_macro and macro is not None:
+ if skip_macro and (self.node.method or self.node.define_macro):
return
for element in self:
@@ -75,179 +72,47 @@
raise ValueError("Can't locate stream object.")
- @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)
-
- # 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
- element.attrib[utils.tal_attr('condition')] = 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 = "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 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().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:
- while self.text:
- text = self.text
- m = translator.interpolate(text)
- if m is None:
- break
-
- t = etree.element_factory(utils.tal_attr('interpolation'))
- t.attrib['replace'] = "structure "+m.group('expression')
- t.tail = text[m.end():]
- self.insert(0, t)
- t.update()
-
- if m.start() == 0:
- self.text = text[1:m.start()+1]
- else:
- self.text = text[:m.start()+1]
-
- if self.tail is not None:
- while self.tail:
- m = translator.interpolate(self.tail)
- if m is None:
- break
-
- t = etree.element_factory(utils.tal_attr('interpolation'))
- t.attrib['replace'] = "structure "+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]
-
- for name in self._get_static_attributes():
- value = self.attrib[name]
-
- if translator.interpolate(value):
- del self.attrib[name]
-
- 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
-
def _serialize(self):
"""Serialize element into clause-statements."""
_ = []
# i18n domain
- if self.i18n_domain is not None:
+ if self.node.translation_domain is not None:
_.append(clauses.Define(
- "_domain", types.value(repr(self.i18n_domain))))
+ "_domain", types.value(repr(self.node.translation_domain))))
# variable definitions
- if self._define is not None:
- for variables, expression in self._define:
+ if self.node.define is not None:
+ for variables, expression in self.node.define:
_.append(clauses.Define(variables, expression))
- # genshi macro
+ # macro method
for element in tuple(self):
if not isinstance(element, Element):
continue
- macro = element.py_def
+ macro = element.node.method
if macro is not None:
# define macro
subclauses = []
subclauses.append(clauses.Method(
- "_macro", macro.args))
+ self.macro_variable, macro.args))
subclauses.append(clauses.Visit(element))
_.append(clauses.Group(subclauses))
# assign to variable
_.append(clauses.Define(
- macro.name, types.parts((types.value("_macro"),))))
+ macro.name, types.parts((types.value(self.macro_variable),))))
# condition
- if self._condition is not None:
- _.append(clauses.Condition(self._condition))
+ if self.node.condition is not None:
+ _.append(clauses.Condition(self.node.condition))
# repeat
- if self._repeat is not None:
- variables, expression = self._repeat
+ if self.node.repeat is not None:
+ variables, expression = self.node.repeat
if len(variables) != 1:
raise ValueError(
"Cannot unpack more than one variable in a "
@@ -256,37 +121,75 @@
# tag tail (deferred)
tail = self.tail
- if tail and not self.metal_fillslot:
+ if tail and not self.node.fill_slot:
if isinstance(tail, unicode):
tail = tail.encode('utf-8')
_.append(clauses.Out(tail, defer=True))
- # dynamic content and content translation
- replace = self._replace
- content = self._content
+ content = self.node.content
- if self.metal_defineslot:
+ # macro slot definition
+ if self.node.define_slot:
# check if slot has been filled
- variable = self.metal_slot_prefix+self.metal_defineslot
+ variable = self.metal_slot_prefix + self.node.define_slot
if variable in itertools.chain(*self.stream.scope):
content = types.value(variable)
-
- # compute dynamic flag
- dynamic = replace or content or self.i18n_translate is not None
+ # set dynamic content flag
+ dynamic = content or self.node.translate is not None
+
+ # static attributes are at the bottom of the food chain
+ attributes = self.node.static_attributes
+
+ # dynamic attributes
+ attrs = self.node.dynamic_attributes or ()
+ dynamic_attributes = tuple(attrs)
+
+ for variables, expression in attrs:
+ if len(variables) != 1:
+ raise ValueError("Tuple definitions in assignment clause "
+ "is not supported.")
+
+ variable = variables[0]
+ attributes[variable] = expression
+
+ # translated attributes
+ translated_attributes = self.node.translated_attributes or ()
+ for variable, msgid in translated_attributes:
+ if msgid:
+ if variable in dynamic_attributes:
+ raise ValueError(
+ "Message id not allowed in conjunction with "
+ "a dynamic attribute.")
+
+ value = types.value('"%s"' % msgid)
+
+ if variable in attributes:
+ default = '"%s"' % attributes[variable]
+ expression = _translate(value, default=default)
+ else:
+ expression = _translate(value)
+ else:
+ if variable in dynamic_attributes or variable in attributes:
+ text = '"%s"' % attributes[variable]
+ expression = _translate(text)
+ else:
+ raise ValueError("Must be either static or dynamic "
+ "attribute when no message id "
+ "is supplied.")
+
+ attributes[variable] = expression
+
# tag
- if replace is None:
+ if self.node.omit is not True:
selfclosing = self.text is None and not dynamic and len(self) == 0
- tag = clauses.Tag(self.tag, self._get_attributes(),
- expression=self.py_attrs, selfclosing=selfclosing,
- cdata=self.tal_cdata is not None)
-
- if self._omit:
- _.append(clauses.Condition(_not(self._omit), [tag],
- finalize=False))
- elif self._omit is not None or \
- self.metal_use or self.metal_fillslot:
- pass
+ tag = clauses.Tag(
+ self.tag, attributes,
+ expression=self.node.dict_attributes, selfclosing=selfclosing,
+ cdata=self.node.cdata is not None)
+ if self.node.omit:
+ _.append(clauses.Condition(
+ _not(self.node.omit), [tag], finalize=False))
else:
_.append(tag)
@@ -297,26 +200,25 @@
text = text.encode('utf-8')
_.append(clauses.Out(text))
- if replace and content:
- raise ValueError("Can't use replace clause together with "
- "content clause.")
-
- if replace or content:
- if self.i18n_translate is not None:
- if self.i18n_translate != "":
- raise ValueError("Can't use message id with "
- "dynamic content translation.")
+ # dynamic content
+ if content:
+ msgid = self.node.translate
+ if msgid is not None:
+ if msgid:
+ raise ValueError(
+ "Can't use message id with dynamic content translation.")
+
_.append(clauses.Translate())
+ _.append(clauses.Write(content))
- _.append(clauses.Write(replace or content))
- elif self.metal_use:
+ # use macro
+ elif self.node.use_macro:
# for each fill-slot element, create a new output stream
# and save value in a temporary variable
kwargs = []
-
for element in self.xpath(
'.//*[@metal:fill-slot]', namespaces={'metal': config.METAL_NS}):
- variable = self.metal_slot_prefix+element.metal_fillslot
+ variable = self.metal_slot_prefix+element.node.fill_slot
kwargs.append((variable, variable))
subclauses = []
@@ -328,7 +230,7 @@
types.value('_out.getvalue()'), variable))
_.append(clauses.Group(subclauses))
- _.append(clauses.Assign(self.metal_use, '_metal'))
+ _.append(clauses.Assign(self.node.use_macro, self.metal_variable))
# compute macro function arguments and create argument string
arguments = ", ".join(
@@ -336,69 +238,69 @@
itertools.chain(*self.stream.scope))+
tuple("%s=%s" % kwarg for kwarg in kwargs))
- _.append(clauses.Write(types.value("_metal(%s)" % arguments)))
-
- else:
- if self.i18n_translate is not None:
- msgid = self.i18n_translate
- if not msgid:
- msgid = self._msgid()
+ _.append(clauses.Write(types.value("%s(%s)" % (self.metal_variable, arguments))))
- # 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]
+ # translate body
+ elif self.node.translate is not None:
+ msgid = self.node.translate
+ if not msgid:
+ msgid = self._msgid()
- if elements:
- mapping = '_mapping'
- _.append(clauses.Assign(types.value('{}'), mapping))
- else:
- mapping = 'None'
-
- for element in elements:
- name = element.i18n_name
-
- subclauses = []
- subclauses.append(clauses.Define(
- ('_out', '_write'),
- types.value('generation.initialize_stream()')))
- subclauses.append(clauses.Visit(element))
- subclauses.append(clauses.Assign(
- types.value('_out.getvalue()'),
- "%s['%s']" % (mapping, name)))
+ # 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]
- _.append(clauses.Group(subclauses))
+ if elements:
+ mapping = '_mapping'
+ _.append(clauses.Assign(types.value('{}'), mapping))
+ else:
+ mapping = 'None'
- _.append(clauses.Assign(
- _translate(types.value(repr(msgid)), mapping=mapping,
- default='_marker'), '_result'))
+ for element in elements:
+ name = element.i18n_name
- # write translation to output if successful, otherwise
- # fallback to default rendition;
- result = types.value('_result')
- condition = types.value('_result is not _marker')
- _.append(clauses.Condition(condition,
- [clauses.UnicodeWrite(result)]))
-
subclauses = []
- if self.text:
- subclauses.append(clauses.Out(self.text.encode('utf-8')))
- for element in self:
- name = element.i18n_name
- if name:
- value = types.value("%s['%s']" % (mapping, name))
- subclauses.append(clauses.Write(value))
- else:
- subclauses.append(clauses.Out(element.tostring()))
- if subclauses:
- _.append(clauses.Else(subclauses))
+ subclauses.append(clauses.Define(
+ ('_out', '_write'),
+ types.value('generation.initialize_stream()')))
+ subclauses.append(clauses.Visit(element))
+ subclauses.append(clauses.Assign(
+ types.value('_out.getvalue()'),
+ "%s['%s']" % (mapping, name)))
+ _.append(clauses.Group(subclauses))
+
+ _.append(clauses.Assign(
+ _translate(types.value(repr(msgid)), mapping=mapping,
+ default='_marker'), '_result'))
+
+ # write translation to output if successful, otherwise
+ # fallback to default rendition;
+ result = types.value('_result')
+ condition = types.value('_result is not _marker')
+ _.append(clauses.Condition(condition,
+ [clauses.UnicodeWrite(result)]))
+
+ subclauses = []
+ if self.text:
+ subclauses.append(clauses.Out(self.text.encode('utf-8')))
+ for element in self:
+ name = element.i18n_name
+ if name:
+ value = types.value("%s['%s']" % (mapping, name))
+ subclauses.append(clauses.Write(value))
+ else:
+ subclauses.append(clauses.Out(element.tostring()))
+ if subclauses:
+ _.append(clauses.Else(subclauses))
+
return _
def _wrap_literal(self, element):
index = self.index(element)
- t = etree.element_factory(utils.tal_attr('literal'))
- t.attrib['omit-tag'] = ''
+ t = self.makeelement(utils.meta_attr('literal'))
+ t.attrib[utils.meta_attr('omit-tag')] = ''
t.tail = element.tail
t.text = unicode(element)
for child in element.getchildren():
@@ -424,65 +326,6 @@
return msgid
- def _get_static_attributes(self):
- attributes = {}
-
- for key in self.keys():
- if not key.startswith('{'):
- attributes[key] = self.attrib[key]
-
- return attributes
-
- def _get_attributes(self):
- """Aggregate static, dynamic and translatable attributes."""
-
- # static attributes are at the bottom of the food chain
- attributes = self._get_static_attributes()
-
- # dynamic attributes
- attrs = self.tal_attributes
- if attrs is not None:
- for variables, expression in attrs:
- if len(variables) != 1:
- raise ValueError("Tuple definitions in assignment clause "
- "is not supported.")
-
- variable = variables[0]
- attributes[variable] = expression
- else:
- attrs = []
-
- dynamic = [key for (key, expression) in attrs]
-
- # translated attributes
- if self.i18n_attributes:
- for variable, msgid in self.i18n_attributes:
- if msgid:
- if variable in dynamic:
- raise ValueError(
- "Message id not allowed in conjunction with "
- "a dynamic attribute.")
-
- value = types.value('"%s"' % msgid)
-
- if variable in attributes:
- default = '"%s"' % attributes[variable]
- expression = _translate(value, default=default)
- else:
- expression = _translate(value)
- else:
- if variable in dynamic or variable in attributes:
- text = '"%s"' % attributes[variable]
- expression = _translate(text)
- else:
- raise ValueError("Must be either static or dynamic "
- "attribute when no message id "
- "is supplied.")
-
- attributes[variable] = expression
-
- return attributes
-
def _pull_attribute(self, name, default=None):
if name in self.attrib:
value = self.attrib[name]
@@ -490,166 +333,76 @@
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
+ def node(self):
+ return NotImplementedError("Must be provided by subclass.")
- @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
+ meta_cdata = utils.attribute(
+ utils.meta_attr('cdata'))
- tal_define = utils.attribute(
- utils.tal_attr('define'), lambda p: p.definitions)
- tal_condition = utils.attribute(
- utils.tal_attr('condition'), lambda p: p.expression)
- tal_repeat = utils.attribute(
- utils.tal_attr('repeat'), lambda p: p.definition)
- tal_attributes = utils.attribute(
- utils.tal_attr('attributes'), lambda p: p.definitions)
- tal_content = utils.attribute(
- utils.tal_attr('content'), lambda p: p.output)
- tal_replace = utils.attribute(
- utils.tal_attr('replace'), lambda p: p.output)
- tal_omit = utils.attribute(
- utils.tal_attr('omit-tag'), lambda p: p.expression)
- tal_default_expression = utils.attribute(
- utils.tal_attr('default-expression'))
- tal_cdata = utils.attribute(
- utils.tal_attr('cdata'))
- metal_define = utils.attribute(
- utils.metal_attr('define-macro'), lambda p: p.method)
- metal_use = utils.attribute(
- utils.metal_attr('use-macro'), lambda p: p.expression)
- metal_fillslot = utils.attribute(
- utils.metal_attr('fill-slot'))
- metal_defineslot = utils.attribute(
- utils.metal_attr('define-slot'))
- i18n_translate = utils.attribute(
- utils.i18n_attr('translate'))
- i18n_attributes = utils.attribute(
- utils.i18n_attr('attributes'), lambda p: p.mapping)
- i18n_domain = utils.attribute(
- utils.i18n_attr('domain'))
- i18n_name = utils.attribute(
- 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)
- tal_condition = utils.attribute("condition", lambda p: p.expression)
- tal_replace = utils.attribute("replace", lambda p: p.output)
- tal_repeat = utils.attribute("repeat", lambda p: p.definition)
- tal_attributes = utils.attribute("attributes", lambda p: p.expression)
- tal_content = utils.attribute("content", lambda p: p.output)
- tal_omit = utils.attribute("omit-tag", lambda p: p.expression, u"")
- tal_default_expression = utils.attribute(
- 'default-expression')
- tal_cdata = utils.attribute("cdata")
+ meta_omit = utils.attribute(
+ utils.meta_attr('omit-tag'))
- def _get_static_attributes(self):
- attributes = {}
+ meta_attributes = utils.attribute(
+ utils.meta_attr('attributes'), lambda p: p.definitions)
- for key in self.keys():
- if key not in ('define',
- 'condition',
- 'replace',
- 'repeat',
- 'attributes',
- 'content',
- 'omit-tag',
- 'default-expression',
- 'cdata'):
- raise ValueError(
- u"Attribute '%s' not allowed in the namespace '%s'" %
- (key, self.nsmap[self.prefix]))
+ meta_replace = utils.attribute(
+ utils.meta_attr('replace'), lambda p: p.output)
- return attributes
+class VariableInterpolation:
+ def update(self):
+ translator = self.translator
+
+ if self.text is not None:
+ while self.text:
+ text = self.text
+ m = translator.interpolate(text)
+ if m is None:
+ break
-class METALElement(Element):
- metal_define = utils.attribute('define-macro', lambda p: p.method)
- metal_use = utils.attribute('use-macro', lambda p: p.expression)
- metal_fillslot = utils.attribute('fill-slot')
- metal_defineslot = utils.attribute('define-slot')
+ t = self.makeelement(utils.meta_attr('interpolation'))
+ expression = "structure "+m.group('expression')
+ t.attrib[utils.meta_attr('replace')] = expression
+ t.tail = text[m.end():]
+ self.insert(0, t)
+ t.update()
-class PyElement(Element):
- tal_omit = utils.attribute("omit-tag", lambda p: p.expression, u"")
+ if m.start() == 0:
+ self.text = text[1:m.start()+1]
+ else:
+ self.text = text[:m.start()+1]
-class PyIfElement(PyElement):
- py_if = utils.attribute("test", lambda p: p.expression)
+ if self.tail is not None:
+ while self.tail:
+ m = translator.interpolate(self.tail)
+ if m is None:
+ break
-class PyForElement(PyElement):
- py_for = utils.attribute("each", lambda p: p.definition)
+ t = self.makeelement(utils.meta_attr('interpolation'))
+ expression = "structure "+m.group('expression')
+ t.attrib[utils.meta_attr('replace')] = 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]
-class PyWithElement(PyElement):
- py_with = utils.attribute(
- "vars", lambda p: expressions.PythonTranslation.definitions)
+ for name in utils.get_attributes_from_namespace(self, config.XHTML_NS):
+ value = self.attrib[name]
-class PyDefElement(PyElement):
- py_def = utils.attribute("function", lambda p: p.method)
+ if translator.interpolate(value):
+ del self.attrib[name]
-class PyMatchElement(PyElement):
- py_match = utils.attribute("path")
+ attributes = utils.meta_attr('attributes')
+ expr = '%s string: %s' % (name, value)
+ if attributes in self.attrib:
+ self.attrib[attributes] += '; %s' % expr
+ else:
+ self.attrib[attributes] = expr
-# set up namespaces for XML parsing
-etree.ns_lookup(config.XML_NS)[None] = Element
-etree.ns_lookup(config.TAL_NS)[None] = TALElement
-etree.ns_lookup(config.METAL_NS)[None] = METALElement
-etree.ns_lookup(config.PY_NS)["if"] = PyIfElement
-etree.ns_lookup(config.PY_NS)["for"] = PyForElement
-etree.ns_lookup(config.PY_NS)["def"] = PyDefElement
-etree.ns_lookup(config.PY_NS)["with"] = PyWithElement
-etree.ns_lookup(config.PY_NS)["match"] = PyMatchElement
-
-def translate_xml(body, *args, **kwargs):
- root, doctype = etree.parse(body)
+def translate_xml(body, parser, *args, **kwargs):
+ root, doctype = parser.parse(body)
return translate_etree(root, doctype=doctype, *args, **kwargs)
def translate_etree(root, macro=None, doctype=None,
@@ -670,9 +423,14 @@
del root.attrib[utils.metal_attr('define-macro')]
# set default expression name
- if not root.tal_default_expression:
- root.tal_default_expression = default_expression
+ if utils.get_namespace(root) == config.TAL_NS:
+ tag = 'default-expression'
+ else:
+ tag = utils.tal_attr('default-expression')
+ if not root.attrib.get(tag):
+ root.attrib[tag] = default_expression
+
# set up code generation stream
if macro is not None:
wrapper = generation.macro_wrapper
@@ -691,16 +449,13 @@
stream.scope.pop()
root.start(stream)
-
return generator
-def translate_text(body, *args, **kwargs):
- root = etree.element_factory(
- utils.xml_attr('text'), nsmap={None: config.XML_NS})
-
+def translate_text(body, parser, *args, **kwargs):
+ root, doctype = parser.parse("<html xmlns='%s'></html>" % config.XHTML_NS)
root.text = body
- root.attrib[utils.tal_attr('omit-tag')] = ''
- return translate_etree(root, *args, **kwargs)
+ root.attrib[utils.meta_attr('omit-tag')] = ''
+ return translate_etree(root, doctype=doctype, *args, **kwargs)
def _translate(value, mapping=None, default=None):
format = ("_translate(%s, domain=_domain, mapping=%s, context=_context, "
Modified: z3c.pt/trunk/src/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.txt 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/translation.txt 2008-08-15 23:50:05 UTC (rev 89902)
@@ -3,30 +3,29 @@
This document contains functional template tests.
- >>> from z3c.pt.testing import render
- >>> from z3c.pt.translation import translate_xml
+ >>> from z3c.pt.testing import render_xhtml
XHTML
-----
:: Plain HTML document
- >>> print render("""\
+ >>> print render_xhtml("""\
... <div xmlns="http://www.w3.org/1999/xhtml">
... Hello World!
- ... </div>""", translate_xml)
+ ... </div>""")
<div>
Hello World!
</div>
:: Setting DOCTYPE
- >>> print render("""\
+ >>> print render_xhtml("""\
... <!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">
... Hello World!
- ... </html>""", translate_xml)
+ ... </html>""")
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
Hello World!
@@ -34,24 +33,24 @@
:: Unicode
- >>> print render("""\
+ >>> print render_xhtml("""\
... <div xmlns="http://www.w3.org/1999/xhtml">
... La Peña
... <img alt="La Peña" />
- ... </div>""", translate_xml)
+ ... </div>""")
<div>
La Peña
<img alt="La Peña" />
</div>
:: CDATA blocks
- >>> print render("""\
+ >>> print render_xhtml("""\
... <div xmlns="http://www.w3.org/1999/xhtml">
... /* <![CDATA[ */
... This is protected
... /* ]]> */
... <span>Not protected</span> <![CDATA[ This is protected ]]>
- ... </div>""", translate_xml)
+ ... </div>""")
<div>
/* <![CDATA[ */
This is protected
@@ -59,66 +58,18 @@
<span>Not protected</span> <![CDATA[ This is protected ]]>
</div>
-:: Variables containing quotes
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml">
- ... <strong>"${quote}"</strong>
- ... </div>""", translate_xml, quote="Hello, World!")
- <div>
- <strong>"Hello, World!"</strong>
- </div>
-
-:: Variables containing markup
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml">
- ... ${message}
- ... </div>""", translate_xml, message="Hello, <em>World</em>!")
- <div>
- Hello, <em>World</em>!
- </div>
-
-:: Variable expansion must preserve XML validity
-
- >>> from z3c.pt import config
- >>> config.VALIDATION = True
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml">
- ... ${message}
- ... </div>""", translate_xml, message="Hello, <em>World!")
- Traceback (most recent call last):
- ...
- ExpatError: ...
-
- >>> config.VALIDATION = False
-
-:: Unless we are in a CDATA block
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml">
- ... /* <![CDATA[ */
- ... ${message}
- ... /* ]]> */
- ... </div>""", translate_xml, message="Hello, <em>World!")
- <div>
- /* <![CDATA[ */
- Hello, <em>World!
- /* ]]> */
- </div>
-
Literals
--------
:: Named entities output literally (note doctype is required to prevent
lxml from raising a XMLSyntaxError :-( )
- >>> print render("""\
+ >>> print render_xhtml("""\
... <!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">
... Hello World!
- ... </html>""", translate_xml)
+ ... </html>""")
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
Hello World!
@@ -126,11 +77,11 @@
:: Processing instructions output literally
- >>> print render("""\
+ >>> print render_xhtml("""\
... <html xmlns="http://www.w3.org/1999/xhtml">
... <?xml-stylesheet href="classic.xsl" type="text/xml"?>
... Hello World!
- ... </html>""", translate_xml)
+ ... </html>""")
<html>
<?xml-stylesheet href="classic.xsl" type="text/xml"?>
Hello World!
@@ -138,589 +89,18 @@
:: Literal comments (without embedded expressions) output literally
- >>> print render("""\
+ >>> print render_xhtml("""\
... <html xmlns="http://www.w3.org/1999/xhtml">
... <!-- hello world -->
- ... </html>""", translate_xml)
+ ... </html>""")
<html>
<!-- hello world -->
</html>
-
-Zope TAL
---------
-
-:: Namespace elements
-
- >>> print render("""\
- ... <tal:block xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... Hello, world!
- ... </tal:block>""", translate_xml)
- <BLANKLINE>
- Hello, world!
- <BLANKLINE>
-
-tal:define, tal:attributes, tal:contents
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <span id="test"
- ... class="dummy"
- ... tal:define="a 'abc'"
- ... tal:attributes="class 'def' + a; style 'hij'"
- ... tal:content="a + 'ghi'" />
- ... <span tal:replace="'Hello World!'">Hello Universe!</span>
- ... <span tal:replace="'Hello World!'"><b>Hello Universe!</b></span>
- ... <span tal:content="None" />
- ... </div>""", translate_xml)
- <div>
- <span id="test" style="hij" class="defabc">abcghi</span>
- Hello World!
- Hello World!
- <span></span>
- </div>
-
-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="'Item ' + str(i) + ')'" /></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>
- </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(3)"><span tal:replace="str(i) + ' ' + str(repeat['i'].even())" /></li>
- ... </ul>
- ... </div>""", translate_xml)
- <div>
- <ul>
- <li>0 True</li>
- <li>1 False</li>
- <li>2 True</li>
- </ul>
- </div>
-
-tal:condition
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <div tal:condition="True">
- ... Show me!
- ... </div>
- ... <div tal:condition="False">
- ... Do not show me!
- ... </div>
- ... </div>""", translate_xml)
- <div>
- <div>
- Show me!
- </div>
- </div>
-
-:: HTML comments
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <!-- a comment -->
- ... <!-- a multi-
- ... line comment -->
- ... <!-- a comment with an ${'expression'} -->
- ... </div>""", translate_xml)
- <div>
- <!-- a comment -->
- <!-- a multi-
- line comment -->
- <!-- a comment with an expression -->
- </div>
-
-:: TAL elements with namespace prefix
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <tal:example replace="'Hello World!'" />
- ... <tal:div content="'Hello World!'" />
- ... <tal:multiple repeat="i range(3)" replace="i" />
- ... <tal:div condition="True">True</tal:div>
- ... </div>""", translate_xml)
- <div>
- Hello World!
- Hello World!
- 0
- 1
- 2
- True
- </div>
-
-tal:omit-tag
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <p tal:omit-tag="">No paragraph here.</p>
- ... <p tal:omit-tag="True">No paragraph here either.</p>
- ... <p tal:omit-tag="False">A paragraph here.</p>
- ... </div>""", translate_xml)
- <div>
- No paragraph here.
- No paragraph here either.
- <p>A paragraph here.</p>
- </div>
-
-:: 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 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))" />
- ... <span tal:content="unicode('La Pe\xc3\xb1a', 'utf-8')" />
- ... </div>""", translate_xml)
- <div>
- <img title="<Hello>" />
- <br />
- <br />
- <span>La Peña</span>
- </div>
-
-:: Using the "path:" expression
-
- >>> class Test(object):
- ... greeting = u'Hello'
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <span tal:replace="path: test1/greeting" />
- ... <span tal:replace="path: test2/greeting" />
- ... </div>""", translate_xml, request=object(),
- ... test1={'greeting': u'Hello'}, test2=Test())
- <div>
- Hello
- Hello
- </div>
-
-:: Using the "string:" expression
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <span tal:replace="string:${greeting}, world!" />
- ... <img tal:attributes="alt string:Leonardo da Vinci;; Musee du Louvre, 1503;
- ... title string:Mona Lisa" />
- ... </div>""", translate_xml, request=object(), greeting=u'Hello')
- <div>
- Hello, world!
- <img alt="Leonardo da Vinci; Musee du Louvre, 1503" title="Mona Lisa" />
- </div>
-
-:: Setting the default expression
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <div tal:default-expression="path">
- ... <span tal:replace="test/greeting" />
- ... </div>
- ... </div>""", translate_xml, request=object(), test={'greeting': u'Hello'})
- <div>
- <div>
- Hello
- </div>
- </div>
-
-:: Using different expressions with try-except operator (|)
-
- >>> 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}}">
- ... <div tal:default-expression="path">
- ... <span tal:replace="mydict/a" />
- ... <span tal:replace="mydict/b|mydict/a" />
- ... <span tal:replace="mydict/c/a" />
- ... <span tal:replace="python: 5+5" />
- ... </div>
- ... <span tal:replace="path: mydict/a" />
- ... <span tal:replace="python: 1+1" />
- ... <span tal:replace="path: mydict/b|True" />
- ... <span tal:replace="int('a')|path: mydict/a" />
- ... </tal:path-expression-testing>
- ... </div>""", translate_xml)
- <div>
- <BLANKLINE>
- <div>
- 1
- 1
- 2
- 10
- </div>
- 1
- 2
- True
- 1
- <BLANKLINE>
- </div>
-
-:: Using TAL pragmas "nocall" and "structure"
-
- >>> print render("""\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal">
- ... <span tal:default-expression="path"
- ... tal:replace="structure nocall: dir" />
- ... <span tal:replace="structure dir" />
- ... </div>""", translate_xml, request=object())
- <div>
- <built-in function dir>
- <built-in function dir>
- </div>
-
-METAL
------
-
-metal:define-macro, metal:use-macro
-
- >>> body = """\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal"
- ... xmlns:metal="http://xml.zope.org/namespaces/metal">
- ... <div class="greeting" metal:define-macro="greeting">
- ... Hello, ${name}!
- ... </div>
- ... <div tal:define="name 'world'">
- ... <div metal:use-macro="template.macros['greeting']" />
- ... </div>
- ... </div>"""
-
- >>> from z3c.pt.testing import MockTemplate
- >>> template = MockTemplate(body, translate_xml)
- >>> print render(body, translate_xml, template=template)
- <div>
- <div>
- <div class="greeting">
- Hello, world!
- </div>
- </div>
- </div>
-
-metal:define-slot, metal:fill-slot
-
- >>> body = """\
- ... <div xmlns="http://www.w3.org/1999/xhtml"
- ... xmlns:tal="http://xml.zope.org/namespaces/tal"
- ... xmlns:metal="http://xml.zope.org/namespaces/metal">
- ... <div metal:define-macro="greeting">
- ... Hello, <b class="name" metal:define-slot="name">stranger!</b>
- ... </div>
- ... <div metal:use-macro="template.macros['greeting']">
- ... <span metal:fill-slot="name">earth!</span>
- ... </div>
- ... <div metal:use-macro="template.macros['greeting']">
- ... <!-- display fallback greeting -->
- ... </div>
- ... <div metal:use-macro="template.macros['greeting']">
- ... <span metal:fill-slot="dummy">dummy!</span>
- ... </div>
- ... </div>"""
-
- >>> from z3c.pt.testing import MockTemplate
- >>> template = MockTemplate(body, translate_xml)
- >>> print render(body, translate_xml, template=template)
- <div>
- <BLANKLINE>
- <div>
- Hello, <b class="name">earth!</b>
- </div>
- <BLANKLINE>
- <BLANKLINE>
- <div>
- Hello, <b class="name">stranger!</b>
- </div>
- <BLANKLINE>
- <BLANKLINE>
- <div>
- Hello, <b class="name">stranger!</b>
- </div>
- <BLANKLINE>
- </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
-
- >>> 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>""", translate_xml, 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>
- ... </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
--------------
- >>> from z3c.pt.translation import translate_text
+ >>> from z3c.pt.testing import render_text
An example with a CSS stylesheet document:
@@ -729,7 +109,7 @@
... background: url(${'http://nohost/plone'}/logo.gif) no-repeat;
... }"""
- >>> print render(css, translate_text)
+ >>> print render_text(css)
#some-region {
background: url(http://nohost/plone/logo.gif) no-repeat;
}
@@ -739,7 +119,7 @@
>>> js = """\
... print '<div class="description">Hello ${'World!'}</div>';"""
- >>> print render(js, translate_text)
+ >>> print render_text(js)
print '<div class="description">Hello World!</div>';
Error handling
@@ -752,7 +132,7 @@
A default namespace must be explicitly declared for the parser to work.
>>> body = '<br />'
- >>> render(body, translate_xml)
+ >>> render_xhtml(body)
Traceback (most recent call last):
...
ValueError: Must define valid namespace for tag: 'br.'
@@ -761,7 +141,7 @@
We expect the xml-parser to raise an exception.
>>> body = '<div xmlns="http://www.w3.org/1999/xhtml"'
- >>> render(body, translate_xml)
+ >>> render_xhtml(body)
Traceback (most recent call last):
...
XMLSyntaxError: Couldn't find end of Start Tag div line 1, line 1, column 11
@@ -775,7 +155,7 @@
... <div xmlns="http://www.w3.org/1999/xhtml" tal:content="'Hello World'" />
... """
- >>> print render(body, translate_xml)
+ >>> print render_xhtml(body)
Traceback (most recent call last):
...
XMLSyntaxError: Namespace prefix tal for content on div is not defined, line 1, column 23
Modified: z3c.pt/trunk/src/z3c/pt/utils.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/utils.py 2008-08-15 22:59:32 UTC (rev 89901)
+++ z3c.pt/trunk/src/z3c/pt/utils.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -110,12 +110,30 @@
return value
-def xml_attr(name):
- return "{%s}%s" % (config.XML_NS, name)
+def get_attributes_from_namespace(element, namespace):
+ if element.nsmap.get(element.prefix) == namespace:
+ return dict([
+ (name, value) for (name, value) in element.attrib.items() \
+ if '{' not in name])
+ return dict([
+ (name, value) for (name, value) in element.attrib.items() \
+ if name.startswith('{%s}' % namespace)])
+
+def get_namespace(element):
+ if '}' in element.tag:
+ return element.tag.split('}')[0][1:]
+ return element.nsmap[None]
+
+def xhtml_attr(name):
+ return "{%s}%s" % (config.XHTML_NS, name)
+
def tal_attr(name):
return "{%s}%s" % (config.TAL_NS, name)
+def meta_attr(name):
+ return "{%s}%s" % (config.META_NS, name)
+
def metal_attr(name):
return "{%s}%s" % (config.METAL_NS, name)
Added: z3c.pt/trunk/src/z3c/pt/zpt.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/zpt.py (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/zpt.py 2008-08-15 23:50:05 UTC (rev 89902)
@@ -0,0 +1,189 @@
+from zope import component
+
+import translation
+import interfaces
+import utils
+import config
+import etree
+
+class ZopePageTemplateElement(
+ translation.Element, translation.VariableInterpolation):
+ """Zope Page Template element.
+
+ Implements the ZPT subset of the attribute template language.
+ """
+
+ class node(object):
+ def __init__(self, element):
+ self.element = element
+
+ @property
+ def omit(self):
+ if self.element.tal_omit is not None:
+ return self.element.tal_omit or True
+ if self.element.meta_omit is not None:
+ return self.element.meta_omit or True
+ if self.element.tal_replace or self.element.meta_replace:
+ return True
+ if self.element.metal_use or self.element.metal_fillslot:
+ return True
+
+ @property
+ def define(self):
+ return self.element.tal_define
+
+ @property
+ def condition(self):
+ return self.element.tal_condition
+
+ @property
+ def repeat(self):
+ return self.element.tal_repeat
+
+ @property
+ def content(self):
+ return self.element.tal_content or self.element.tal_replace or \
+ self.element.meta_replace
+
+ @property
+ def skip(self):
+ return self.content or self.define_macro or \
+ self.use_macro or self.translate is not None
+
+ dict_attributes = None
+
+ @property
+ def dynamic_attributes(self):
+ return (self.element.tal_attributes or ()) + \
+ (self.element.meta_attributes or ())
+
+ @property
+ def translated_attributes(self):
+ return self.element.i18n_attributes
+
+ @property
+ def static_attributes(self):
+ return utils.get_attributes_from_namespace(
+ self.element, config.XHTML_NS)
+
+ @property
+ def translate(self):
+ return self.element.i18n_translate
+
+ @property
+ def translation_domain(self):
+ return self.element.i18n_domain
+
+ method = None
+
+ @property
+ def use_macro(self):
+ return self.element.metal_use
+
+ @property
+ def define_macro(self):
+ return self.element.metal_define
+
+ @property
+ def define_slot(self):
+ return self.element.metal_defineslot
+
+ @property
+ def fill_slot(self):
+ return self.element.metal_fillslot
+
+ @property
+ def cdata(self):
+ return self.element.meta_cdata
+
+ @property
+ def default_expression(self):
+ return self.element.tal_default_expression
+
+ node = property(node)
+
+ @property
+ def translator(self):
+ while self.node.default_expression is None:
+ self = self.getparent()
+ if self is None:
+ raise ValueError("Default expression not set.")
+
+ return component.getUtility(
+ interfaces.IExpressionTranslation, name=self.node.default_expression)
+
+ def update(self):
+ translation.VariableInterpolation.update(self)
+
+class XHTMLElement(ZopePageTemplateElement):
+ """XHTML namespace element."""
+
+ tal_define = utils.attribute(
+ utils.tal_attr('define'), lambda p: p.definitions)
+ tal_condition = utils.attribute(
+ utils.tal_attr('condition'), lambda p: p.expression)
+ tal_repeat = utils.attribute(
+ utils.tal_attr('repeat'), lambda p: p.definition)
+ tal_attributes = utils.attribute(
+ utils.tal_attr('attributes'), lambda p: p.definitions)
+ tal_content = utils.attribute(
+ utils.tal_attr('content'), lambda p: p.output)
+ tal_replace = utils.attribute(
+ utils.tal_attr('replace'), lambda p: p.output)
+ tal_omit = utils.attribute(
+ utils.tal_attr('omit-tag'), lambda p: p.expression)
+ tal_default_expression = utils.attribute(
+ utils.tal_attr('default-expression'))
+ metal_define = utils.attribute(
+ utils.metal_attr('define-macro'), lambda p: p.method)
+ metal_use = utils.attribute(
+ utils.metal_attr('use-macro'), lambda p: p.expression)
+ metal_fillslot = utils.attribute(
+ utils.metal_attr('fill-slot'))
+ metal_defineslot = utils.attribute(
+ utils.metal_attr('define-slot'))
+ i18n_translate = utils.attribute(
+ utils.i18n_attr('translate'))
+ i18n_attributes = utils.attribute(
+ utils.i18n_attr('attributes'), lambda p: p.mapping)
+ i18n_domain = utils.attribute(
+ utils.i18n_attr('domain'))
+ i18n_name = utils.attribute(
+ utils.i18n_attr('name'))
+
+class TALElement(ZopePageTemplateElement):
+ """TAL namespace element."""
+
+ tal_define = utils.attribute("define", lambda p: p.definitions)
+ tal_condition = utils.attribute("condition", lambda p: p.expression)
+ tal_replace = utils.attribute("replace", lambda p: p.output)
+ tal_repeat = utils.attribute("repeat", lambda p: p.definition)
+ tal_attributes = utils.attribute("attributes", lambda p: p.expression)
+ tal_content = utils.attribute("content", lambda p: p.output)
+ tal_omit = utils.attribute("omit-tag", lambda p: p.expression, u"")
+ tal_default_expression = utils.attribute(
+ 'default-expression')
+ tal_cdata = utils.attribute("cdata")
+
+ metal_define = None
+ metal_use = None
+ metal_fillslot = None
+ metal_defineslot = None
+ i18n_domain = None
+ i18n_translate = None
+ i18n_attributes = None
+
+class METALElement(ZopePageTemplateElement):
+ """METAL namespace element."""
+
+ metal_define = utils.attribute('define-macro', lambda p: p.method)
+ metal_use = utils.attribute('use-macro', lambda p: p.expression)
+ metal_fillslot = utils.attribute('fill-slot')
+ metal_defineslot = utils.attribute('define-slot')
+
+class ZopePageTemplateParser(etree.Parser):
+ element_mapping = {
+ config.XHTML_NS: {None: XHTMLElement},
+ config.META_NS: {None: XHTMLElement},
+ config.TAL_NS: {None: TALElement},
+ config.METAL_NS: {None: METALElement}}
Added: z3c.pt/trunk/src/z3c/pt/zpt.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/zpt.txt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/zpt.txt 2008-08-15 23:50:05 UTC (rev 89902)
@@ -0,0 +1,329 @@
+Zope Page Templates (ZPT)
+=========================
+
+ >>> from z3c.pt.testing import render_zpt
+
+TAL
+---
+
+:: Namespace elements
+
+ >>> print render_zpt("""\
+ ... <tal:block xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... Hello, world!
+ ... </tal:block>""")
+ <BLANKLINE>
+ Hello, world!
+ <BLANKLINE>
+
+tal:define, tal:attributes, tal:contents
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <span id="test"
+ ... class="dummy"
+ ... tal:define="a 'abc'"
+ ... tal:attributes="class 'def' + a; style 'hij'"
+ ... tal:content="a + 'ghi'" />
+ ... <span tal:replace="'Hello World!'">Hello Universe!</span>
+ ... <span tal:replace="'Hello World!'"><b>Hello Universe!</b></span>
+ ... <span tal:content="None" />
+ ... </div>""")
+ <div>
+ <span id="test" style="hij" class="defabc">abcghi</span>
+ Hello World!
+ Hello World!
+ <span></span>
+ </div>
+
+tal:repeat
+
+ >>> print render_zpt("""\
+ ... <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>
+ ... </ul>
+ ... </div>""")
+ <div>
+ <ul>
+ <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_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <ul>
+ ... <li tal:repeat="i range(3)"><span tal:replace="str(i) + ' ' + str(repeat['i'].even())" /></li>
+ ... </ul>
+ ... </div>""")
+ <div>
+ <ul>
+ <li>0 True</li>
+ <li>1 False</li>
+ <li>2 True</li>
+ </ul>
+ </div>
+
+tal:condition
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <div tal:condition="True">
+ ... Show me!
+ ... </div>
+ ... <div tal:condition="False">
+ ... Do not show me!
+ ... </div>
+ ... </div>""")
+ <div>
+ <div>
+ Show me!
+ </div>
+ </div>
+
+:: HTML comments
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <!-- 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>
+
+:: TAL elements with namespace prefix
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <tal:example replace="'Hello World!'" />
+ ... <tal:div content="'Hello World!'" />
+ ... <tal:multiple repeat="i range(3)" replace="i" />
+ ... <tal:div condition="True">True</tal:div>
+ ... </div>""")
+ <div>
+ Hello World!
+ Hello World!
+ 0
+ 1
+ 2
+ True
+ </div>
+
+tal:omit-tag
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <p tal:omit-tag="">No paragraph here.</p>
+ ... <p tal:omit-tag="True">No paragraph here either.</p>
+ ... <p tal:omit-tag="False">A paragraph here.</p>
+ ... </div>""")
+ <div>
+ No paragraph here.
+ No paragraph here either.
+ <p>A paragraph here.</p>
+ </div>
+
+:: Unicode with dynamic attributes and content
+
+ >>> print render_zpt("""\
+ ... <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))" />
+ ... <span tal:content="unicode('La Pe\xc3\xb1a', 'utf-8')" />
+ ... </div>""")
+ <div>
+ <img title="<Hello>" />
+ <br />
+ <br />
+ <span>La Peña</span>
+ </div>
+
+:: Using the "path:" expression
+
+ >>> class Test(object):
+ ... greeting = u'Hello'
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <span tal:replace="path: test1/greeting" />
+ ... <span tal:replace="path: test2/greeting" />
+ ... </div>""", request=object(),
+ ... test1={'greeting': u'Hello'}, test2=Test())
+ <div>
+ Hello
+ Hello
+ </div>
+
+:: Using the "string:" expression
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <span tal:replace="string:${greeting}, world!" />
+ ... <img tal:attributes="alt string:Leonardo da Vinci;; Musee du Louvre, 1503;
+ ... title string:Mona Lisa" />
+ ... </div>""", request=object(), greeting=u'Hello')
+ <div>
+ Hello, world!
+ <img alt="Leonardo da Vinci; Musee du Louvre, 1503" title="Mona Lisa" />
+ </div>
+
+:: Setting the default expression
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <div tal:default-expression="path">
+ ... <span tal:replace="test/greeting" />
+ ... </div>
+ ... </div>""", request=object(), test={'greeting': u'Hello'})
+ <div>
+ <div>
+ Hello
+ </div>
+ </div>
+
+:: Using different expressions with try-except operator (|)
+
+ >>> print render_zpt("""\
+ ... <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}}">
+ ... <div tal:default-expression="path">
+ ... <span tal:replace="mydict/a" />
+ ... <span tal:replace="mydict/b|mydict/a" />
+ ... <span tal:replace="mydict/c/a" />
+ ... <span tal:replace="python: 5+5" />
+ ... </div>
+ ... <span tal:replace="path: mydict/a" />
+ ... <span tal:replace="python: 1+1" />
+ ... <span tal:replace="path: mydict/b|True" />
+ ... <span tal:replace="int('a')|path: mydict/a" />
+ ... </tal:path-expression-testing>
+ ... </div>""")
+ <div>
+ <BLANKLINE>
+ <div>
+ 1
+ 1
+ 2
+ 10
+ </div>
+ 1
+ 2
+ True
+ 1
+ <BLANKLINE>
+ </div>
+
+:: Using TAL pragmas "nocall" and "structure"
+
+ >>> print render_zpt("""\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal">
+ ... <span tal:default-expression="path"
+ ... tal:replace="structure nocall: dir" />
+ ... <span tal:replace="structure dir" />
+ ... </div>""", request=object())
+ <div>
+ <built-in function dir>
+ <built-in function dir>
+ </div>
+
+METAL
+-----
+
+metal:define-macro, metal:use-macro
+
+ >>> body = """\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal"
+ ... xmlns:metal="http://xml.zope.org/namespaces/metal">
+ ... <div class="greeting" metal:define-macro="greeting">
+ ... Hello, ${name}!
+ ... </div>
+ ... <div tal:define="name 'world'">
+ ... <div metal:use-macro="template.macros['greeting']" />
+ ... </div>
+ ... </div>"""
+
+ >>> from z3c.pt.testing import MockTemplate
+ >>> from z3c.pt.zpt import ZopePageTemplateParser
+
+ >>> template = MockTemplate(body, ZopePageTemplateParser)
+ >>> print render_zpt(body, template=template)
+ <div>
+ <div>
+ <div class="greeting">
+ Hello, world!
+ </div>
+ </div>
+ </div>
+
+metal:define-slot, metal:fill-slot
+
+ >>> body = """\
+ ... <div xmlns="http://www.w3.org/1999/xhtml"
+ ... xmlns:tal="http://xml.zope.org/namespaces/tal"
+ ... xmlns:metal="http://xml.zope.org/namespaces/metal">
+ ... <div metal:define-macro="greeting">
+ ... Hello, <b class="name" metal:define-slot="name">stranger!</b>
+ ... </div>
+ ... <div metal:use-macro="template.macros['greeting']">
+ ... <span metal:fill-slot="name">earth!</span>
+ ... </div>
+ ... <div metal:use-macro="template.macros['greeting']">
+ ... <!-- display fallback greeting -->
+ ... </div>
+ ... <div metal:use-macro="template.macros['greeting']">
+ ... <span metal:fill-slot="dummy">dummy!</span>
+ ... </div>
+ ... </div>"""
+
+ >>> from z3c.pt.testing import MockTemplate
+
+ >>> template = MockTemplate(body, ZopePageTemplateParser)
+ >>> print render_zpt(body, template=template)
+ <div>
+ <BLANKLINE>
+ <div>
+ Hello, <b class="name">earth!</b>
+ </div>
+ <BLANKLINE>
+ <BLANKLINE>
+ <div>
+ Hello, <b class="name">stranger!</b>
+ </div>
+ <BLANKLINE>
+ <BLANKLINE>
+ <div>
+ Hello, <b class="name">stranger!</b>
+ </div>
+ <BLANKLINE>
+ </div>
+
+
More information about the Checkins
mailing list