[Checkins] SVN: Sandbox/malthe/chameleon.core/ Support template fragments, e.g. documents with more than one root element.

Malthe Borch mborch at gmail.com
Tue Dec 2 09:53:27 EST 2008


Log message for revision 93545:
  Support template fragments, e.g. documents with more than one root element.

Changed:
  U   Sandbox/malthe/chameleon.core/CHANGES.txt
  U   Sandbox/malthe/chameleon.core/src/chameleon/core/etree.py
  U   Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt

-=-
Modified: Sandbox/malthe/chameleon.core/CHANGES.txt
===================================================================
--- Sandbox/malthe/chameleon.core/CHANGES.txt	2008-12-02 14:12:21 UTC (rev 93544)
+++ Sandbox/malthe/chameleon.core/CHANGES.txt	2008-12-02 14:53:27 UTC (rev 93545)
@@ -4,6 +4,9 @@
 HEAD
 ~~~~
 
+- Support template fragments, e.g. documents with more than one
+  root element. [malthe]
+
 - Added namespace prefix 'xml' to default namespace mapping. [malthe]
 
 - Fixed root cause of issue with self-closing tags; an empty element

Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/etree.py
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/etree.py	2008-12-02 14:12:21 UTC (rev 93544)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/etree.py	2008-12-02 14:53:27 UTC (rev 93545)
@@ -46,7 +46,8 @@
     element with trivial body text as self-closing."""
     
     root = None
-
+    index = None
+    
     # doctype
     doctype = None
 
@@ -54,7 +55,7 @@
     xml_version = None
     encoding = None
     standalone = None
-    
+
     def __init__(self, parser, body, expat):
         self.parser = parser
         self.body = body
@@ -63,6 +64,7 @@
     def StartElementHandler(self, tag, attrs):
         # update prefix to namespace mapping
         if self.root is None:
+            self.index = self.expat.CurrentByteIndex
             nsmap = {}
         else:
             nsmap = self.root.nsmap.copy()
@@ -243,26 +245,64 @@
     parser = lxml.etree.XMLParser(resolve_entities=False, strip_cdata=False)
     parser.setElementClassLookup(lookup)
 
-    # set up expat parser
-    expat = xml.parsers.expat.ParserCreate(None)
-    expat.UseForeignDTD()
-    expat.SetParamEntityParsing(
-        xml.parsers.expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
+    junk = ""
+    tree = None
+    parts = []
+    while tree is None:
+        # set up expat parser
+        expat = xml.parsers.expat.ParserCreate(None)
+        expat.UseForeignDTD()
+        expat.SetParamEntityParsing(
+            xml.parsers.expat.XML_PARAM_ENTITY_PARSING_ALWAYS)
 
-    # attach expat parser methods
-    parser = ExpatParser(parser, body, expat)
-    for name in type(parser).__dict__.keys():
+        # attach expat parser methods
+        expatparser = ExpatParser(parser, body, expat)
+        for name in type(expatparser).__dict__.keys():
+            try:
+                setattr(expat, name, getattr(expatparser, name))
+            except AttributeError:
+                pass
+
         try:
-            setattr(expat, name, getattr(parser, name))
-        except AttributeError:
-            pass
+            # attempt to parse this body; if we're not successful,
+            # this may be because the document source consists of
+            # several 'parts'; although this is not valid XML, we do
+            # support it, being a template engine, not a XML
+            # validator :-)
+            expat.Parse(body, 1)
 
-    # parse document body 
-    expat.Parse(body, 1)
+            if parts:
+                parts.append(expatparser.root)
+                root = parser.makeelement(
+                    utils.meta_attr('fragments'))
+                for i, part in enumerate(parts):
+                    if isinstance(part, basestring):
+                        parts[i-1].tail = part
+                    else:
+                        root.append(part)
+                tree = root.getroottree()
+            else:
+                tree = expatparser.root.getroottree()
+        except xml.parsers.expat.ExpatError:
+            # if we are not able to find a tree root, we give up and
+            # let the exception through
+            if expatparser.root is None:
+                raise
 
-    # return document root tree
-    return parser.root.getroottree()
+            # add the root as a tree fragment and update the body
+            # source to the next possible chunk
+            parts.append(expatparser.root)
+            body = body[:expatparser.index] + body[expat.CurrentByteIndex:]
 
+            # a simple heuristic is used here to allow chunks of
+            # 'junk' in-between the tree fragments
+            pos = body.find('<')
+            junk = body[:pos]
+            body = body[pos:]
+            parts.append(junk)
+            
+    return tree
+
 def serialize(tree):
     """Serialize tree using lxml."""
     

Modified: Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt
===================================================================
--- Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt	2008-12-02 14:12:21 UTC (rev 93544)
+++ Sandbox/malthe/chameleon.core/src/chameleon/core/template.txt	2008-12-02 14:53:27 UTC (rev 93545)
@@ -67,6 +67,27 @@
     </div>
   </div>
 
+Fragmented documents
+--------------------
+  
+  >>> print Template("""
+  ... <div>
+  ...   Hello, world!
+  ... </div>
+  ...
+  ... Hello &amp; goodbye!
+  ...
+  ... <div>
+  ...   Hello, earth!
+  ... </div>""", mock_parser)()
+  <div>
+    Hello, world!
+  </div>
+  Hello &amp; goodbye!
+  <div>
+    Hello, earth!
+  </div>
+
 XML-support
 -----------
   



More information about the Checkins mailing list