[Checkins] SVN: z3c.pt/trunk/ Added limited support for the XInclude include directive.
Malthe Borch
mborch at gmail.com
Sun Aug 24 06:20:50 EDT 2008
Log message for revision 90165:
Added limited support for the XInclude include directive.
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/genshi.py
U z3c.pt/trunk/src/z3c/pt/genshi.txt
U z3c.pt/trunk/src/z3c/pt/pagetemplate.py
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
A z3c.pt/trunk/src/z3c/pt/tests/xinclude1.pt
A z3c.pt/trunk/src/z3c/pt/tests/xinclude2.pt
A z3c.pt/trunk/src/z3c/pt/tests/xinclude3.pt
A z3c.pt/trunk/src/z3c/pt/tests/xinclude4.pt
U z3c.pt/trunk/src/z3c/pt/texttemplate.py
U z3c.pt/trunk/src/z3c/pt/translation.py
U z3c.pt/trunk/src/z3c/pt/zpt.py
-=-
Modified: z3c.pt/trunk/CHANGES.txt
===================================================================
--- z3c.pt/trunk/CHANGES.txt 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/CHANGES.txt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -4,6 +4,11 @@
Version 1.0dev
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+- Added limited support for the XInclude ``include`` directive. The
+ implemented subset corresponds to the Genshi implementation, except
+ Match-templates, which are not made available to the calling
+ template. [malthe]
+
- Use a global template registry for templates on the
file-system. This makes it inexpensive to have multiple template
class instances pointing to the same file. [malthe]
Modified: z3c.pt/trunk/src/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/clauses.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -752,12 +752,17 @@
def begin(self, stream):
temp = stream.save()
+ symbols = stream.symbols.as_dict()
+ value = self.value
+
+ if isinstance(value, types.template):
+ value = types.value(value % symbols)
def write(template):
- stream.write(template % stream.symbols.as_dict())
+ stream.write(template % symbols)
- if self.value:
- expr = self.value
+ if value:
+ expr = value
else:
self.assign.begin(stream, temp)
expr = temp
@@ -889,6 +894,7 @@
>>> from z3c.pt import testing
>>> _out, _write, stream = testing.setup_stream()
+ >>> _scope = {}
>>> method = Method('test', ('a', 'b', 'c'))
>>> method.begin(stream)
>>> stream.write('print a, b, c')
@@ -902,12 +908,15 @@
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()
-
-
+ assign = Assign(
+ types.value(self.name), "%s['%s']" % \
+ (stream.symbols.scope, self.name))
+ assign.begin(stream)
+ assign.end(stream)
Modified: z3c.pt/trunk/src/z3c/pt/config.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/config.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/config.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -27,15 +27,18 @@
TAL_NS = "http://xml.zope.org/namespaces/tal"
META_NS = "http://xml.zope.org/namespaces/meta"
METAL_NS = "http://xml.zope.org/namespaces/metal"
+XI_NS = "http://www.w3.org/2001/XInclude"
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)
# the symbols table below is used internally be the compiler
class SYMBOLS(object):
+ # internal use only
init = '_init'
slot = '_slot'
metal = '_metal'
+ include = '_include'
macro = '_macro'
scope = '_scope'
out = '_out'
@@ -51,9 +54,12 @@
translate = '_translate'
elementtree = '_et'
path = '_path'
+
+ # advertised symbols
repeat = 'repeat'
language = 'target_language'
-
+ xincludes = 'xincludes'
+
@classmethod
def as_dict(cls):
return dict((name, getattr(cls, name)) for name in dir(cls))
Modified: z3c.pt/trunk/src/z3c/pt/genshi.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/genshi.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/genshi.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -21,7 +21,9 @@
return self.element.meta_omit or True
if self.element.py_replace or self.element.meta_replace:
return True
-
+ if self.element.xi_href:
+ return True
+
@property
def define(self):
return self.element.py_with
@@ -75,6 +77,14 @@
def cdata(self):
return self.element.meta_cdata
+ @property
+ def include(self):
+ return self.element.xi_href
+
+ @property
+ def format(self):
+ return self.element.xi_parse
+
node = property(node)
def update(self):
@@ -197,7 +207,9 @@
utils.py_attr('replace'), lambda p: p.output)
py_strip = utils.attribute(
utils.py_attr('strip'), lambda p: p.expression)
-
+ xi_href = None
+ xi_parse = None
+
class PyElement(XHTMLElement):
py_strip = utils.attribute("strip", lambda p: p.expression, u"")
@@ -216,12 +228,18 @@
class PyMatchElement(PyElement):
py_match = utils.attribute("path")
-
+
+class XiIncludeElement(XHTMLElement):
+ xi_href = utils.attribute(
+ "href", lambda p: expressions.StringTranslation(p).expression)
+ xi_parse = utils.attribute("parse", default="xml")
+
class GenshiParser(etree.Parser):
""" The parser implementation for Genshi templates """
element_mapping = {
config.XHTML_NS: {None: XHTMLElement},
config.META_NS: {None: XHTMLElement},
+ config.XI_NS: {'include': XiIncludeElement},
config.PY_NS: {'if': PyIfElement,
'for': PyForElement,
'def': PyDefElement,
Modified: z3c.pt/trunk/src/z3c/pt/genshi.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/genshi.txt 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/genshi.txt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -417,6 +417,6 @@
</ul>
<BLANKLINE>
</div>
-
+
.. _py:with specs: http://genshi.edgewall.org/wiki/Documentation/0.4.x/xml-templates.html#py-with
Modified: z3c.pt/trunk/src/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/pagetemplate.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/pagetemplate.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -30,6 +30,7 @@
super(PageTemplate, self).__init__(body, parser)
def prepare(self, kwargs):
+ super(PageTemplate, self).prepare(kwargs)
prepare_language_support(kwargs)
class PageTemplateFile(template.BaseTemplateFile):
@@ -41,6 +42,7 @@
super(PageTemplateFile, self).__init__(filename, parser, **kwargs)
def prepare(self, kwargs):
+ super(PageTemplateFile, self).prepare(kwargs)
prepare_language_support(kwargs)
class ViewPageTemplate(property):
Modified: z3c.pt/trunk/src/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/template.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -8,14 +8,23 @@
"""Constructs a template object using the template language
defined by ``parser`` (``ZopePageTemplateParser`` or
``GenshiParser`` as of this writing). Must be passed an input
- string as ``body``."""
+ string as ``body``. The ``format`` parameter supports values 'xml'
+ and 'text'."""
+
+ compilers = {
+ 'xml': translation.Compiler,
+ 'text': translation.Compiler.from_text}
+
+ format = 'xml'
- def __init__(self, body, parser):
+ def __init__(self, body, parser, format=None):
self.body = body
self.parser = parser
self.signature = hash(body)
self.registry = {}
-
+ if format is not None:
+ self.format = format
+
@property
def translate(self):
return NotImplementedError("Must be provided by subclass.")
@@ -26,7 +35,7 @@
@property
def compiler(self):
- return translation.Compiler(self.body, self.parser)
+ return self.compilers[self.format](self.body, self.parser)
def cook(self, **kwargs):
return self.compiler(**kwargs)
@@ -64,9 +73,9 @@
global_registry = {}
- def __init__(self, filename, parser, auto_reload=False):
+ def __init__(self, filename, parser, format=None, auto_reload=False):
BaseTemplate.__init__(
- self, None, parser)
+ self, None, parser, format=format)
self.auto_reload = auto_reload
self.filename = filename = os.path.abspath(
@@ -83,6 +92,13 @@
self.registry = self.global_registry.setdefault(
filename, filecache.TemplateCache(filename))
+ self.xincludes = XIncludes(
+ self.global_registry, os.path.dirname(filename), self.clone)
+
+ def clone(self, filename, format=None):
+ cls = type(self)
+ return cls(filename, self.parser, format=format)
+
def _get_filename(self):
return getattr(self, '_filename', None)
@@ -105,6 +121,9 @@
return BaseTemplate.cook_check(self, *args)
+ def prepare(self, kwargs):
+ kwargs[config.SYMBOLS.xincludes] = self.xincludes
+
def mtime(self):
try:
return os.path.getmtime(self.filename)
@@ -113,3 +132,22 @@
def __repr__(self):
return u"<%s %s>" % (self.__class__.__name__, self.filename)
+
+class XIncludes(object):
+ """Dynamic XInclude registry providing a ``get``-method that will
+ resolve a filename to a template instance. Format must be
+ explicitly provided."""
+
+ def __init__(self, registry, relpath, factory):
+ self.registry = registry
+ self.relpath = relpath
+ self.factory = factory
+
+ def get(self, filename, format):
+ if not os.path.isabs(filename):
+ filename = os.path.join(self.relpath, filename)
+ template = self.registry.get(filename)
+ if template is not None:
+ return template
+ return self.factory(filename, format=format)
+
Modified: z3c.pt/trunk/src/z3c/pt/template.txt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/template.txt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -200,7 +200,35 @@
I replace you.
</div>
</html>
+
+xi:include
+
+ >>> from z3c.pt.template import BaseTemplateFile
+ >>> template = BaseTemplateFile(path+"/xinclude1.pt", MockParser)
+ >>> print template()
+ <div>
+ <div>
+ <span>Hello, world!</span>
+ </div>
+ </div>
+
+:: XInclude in Genshi
+
+When using XInclude-statements in Genshi, macro-definitions are
+carried over from the included template. This is demonstrated below.
+
+ >>> template = BaseTemplateFile(path+"/xinclude3.pt", GenshiParser)
+ >>> print template()
+ <div>
+ <BLANKLINE>
+ <BLANKLINE>
+ <p class="greeting">
+ Hello, world!
+ </p>
+ <BLANKLINE>
+ </div>
+
Error handling
--------------
Modified: z3c.pt/trunk/src/z3c/pt/testing.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/testing.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -79,7 +79,9 @@
return self.element.meta_omit or True
if self.content:
return True
-
+ if self.include:
+ return True
+
@property
def content(self):
return self.element.meta_replace
@@ -88,9 +90,22 @@
def cdata(self):
return self.element.meta_cdata
+ @property
+ def include(self):
+ return self.element.xi_href
+
+ @property
+ def format(self):
+ return self.element.xi_parse
+
node = property(node)
+ xi_href = utils.attribute(
+ "href", lambda p: expressions.StringTranslation(p).expression)
+ xi_parse = utils.attribute("parse", default="xml")
+
class MockParser(etree.Parser):
element_mapping = {
config.XHTML_NS: {None: MockElement},
- config.META_NS: {None: MockElement}}
+ config.META_NS: {None: MockElement},
+ config.XI_NS: {None: MockElement}}
Added: z3c.pt/trunk/src/z3c/pt/tests/xinclude1.pt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/tests/xinclude1.pt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/tests/xinclude1.pt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -0,0 +1,4 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="xinclude${1+1}.pt" />
+</div>
Added: z3c.pt/trunk/src/z3c/pt/tests/xinclude2.pt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/tests/xinclude2.pt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/tests/xinclude2.pt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -0,0 +1,3 @@
+<div xmlns="http://www.w3.org/1999/xhtml">
+ <span>Hello, world!</span>
+</div>
Added: z3c.pt/trunk/src/z3c/pt/tests/xinclude3.pt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/tests/xinclude3.pt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/tests/xinclude3.pt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -0,0 +1,6 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="xinclude${4}.pt" />
+ ${greeting('world')}
+</div>
Added: z3c.pt/trunk/src/z3c/pt/tests/xinclude4.pt
===================================================================
--- z3c.pt/trunk/src/z3c/pt/tests/xinclude4.pt (rev 0)
+++ z3c.pt/trunk/src/z3c/pt/tests/xinclude4.pt 2008-08-24 10:20:47 UTC (rev 90165)
@@ -0,0 +1,7 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org"
+ py:strip="">
+ <p py:def="greeting(name)" class="greeting">
+ Hello, ${name}!
+ </p>
+</div>
Modified: z3c.pt/trunk/src/z3c/pt/texttemplate.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/texttemplate.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/texttemplate.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -3,15 +3,11 @@
class TextTemplate(pagetemplate.PageTemplate):
__doc__ = pagetemplate.PageTemplate.__doc__ # for Sphinx autodoc
- @property
- def compiler(self):
- return translation.Compiler.from_text(self.body, self.parser)
+ format = 'text'
class TextTemplateFile(pagetemplate.PageTemplateFile):
__doc__ = pagetemplate.PageTemplateFile.__doc__ # for Sphinx autodoc
- @property
- def compiler(self):
- return translation.Compiler.from_text(self.body, self.parser)
+ format = 'text'
class ViewTextTemplate(property):
def __init__(self, body):
Modified: z3c.pt/trunk/src/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/translation.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -90,9 +90,6 @@
if macro is not None:
_.append(clauses.Method(
macro.name, macro.args))
- _.append(clauses.Assign(
- types.value(macro.name), "%s['%s']" % \
- (self.symbols.scope, macro.name)))
# tag tail (deferred)
tail = self.element.tail
@@ -199,6 +196,26 @@
_.append(clauses.Translate())
_.append(clauses.Write(content))
+ # include
+ elif self.include:
+ # compute macro function arguments and create argument string
+ arguments = ", ".join(
+ ("%s=%s" % (arg, arg) for arg in \
+ itertools.chain(*self.stream.scope)))
+
+ # XInclude's are similar to METAL macros, except the macro
+ # is always defined as the entire template.
+
+ # first we compute the filename expression and write it to
+ # an internal variable
+ _.append(clauses.Assign(self.include, self.symbols.include))
+
+ # call template
+ _.append(clauses.Write(
+ types.template(
+ "%%(xincludes)s.get(%%(include)s, %s).render(macro='', %s)" % \
+ (repr(self.format), arguments))))
+
# use macro
elif self.use_macro:
# for each fill-slot element, create a new output stream
@@ -436,7 +453,7 @@
# if macro is non-trivial, start compilation at the element
# where the macro is defined
- if macro is not None:
+ if macro:
elements = self.root.xpath(
'descendant-or-self::*[@metal:define-macro="%s"]' % macro,
namespaces={'metal': config.METAL_NS})
@@ -447,7 +464,8 @@
self.root = elements[0]
del self.root.attrib[utils.metal_attr('define-macro')]
- # set up code generation stream
+ # choose function wrapper; note that if macro is the empty
+ # string, we'll still use the macro wrapper
if macro is not None:
wrapper = generation.macro_wrapper
else:
Modified: z3c.pt/trunk/src/z3c/pt/zpt.py
===================================================================
--- z3c.pt/trunk/src/z3c/pt/zpt.py 2008-08-23 23:11:31 UTC (rev 90164)
+++ z3c.pt/trunk/src/z3c/pt/zpt.py 2008-08-24 10:20:47 UTC (rev 90165)
@@ -100,6 +100,9 @@
@property
def default_expression(self):
return self.element.tal_default_expression
+
+ include = None
+ format = None
node = property(node)
More information about the Checkins
mailing list