[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