changeset 381:b9fc7a1f76ca trunk

Fix for #80: fallback only shown when the template to include wasn't found. In addition, the nesting of includes and fallback content should work correctly, and directives/expressions/etc inside fallback content are processed. Thanks to Christian Boos for the original patch and unit tests.
author cmlenz
date Wed, 29 Nov 2006 14:26:45 +0000
parents bf2068f5ef74
children 2682dabbcd04
files ChangeLog genshi/template/markup.py genshi/template/tests/markup.py
diffstat 3 files changed, 145 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -31,6 +31,7 @@
    to strings and file-like objects (ticket #69).
  * Improve handling of incorrectly nested tags in the HTML parser.
  * The builder API now accepts streams as children of elements and fragments.
+ * Template includes can you be nested inside fallback content.
 
 
 Version 0.3.5
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -65,12 +65,11 @@
 
     def _parse(self, source, encoding):
         """Parse the template from an XML document."""
-        stream = [] # list of events of the "compiled" template
+        streams = [[]] # stacked lists of events of the "compiled" template
         dirmap = {} # temporary mapping of directives to elements
         ns_prefix = {}
         depth = 0
-        in_fallback = False
-        fallback_stream = None
+        in_fallback = 0
         include_href = None
 
         if not isinstance(source, Stream):
@@ -78,6 +77,7 @@
                                encoding=encoding)
 
         for kind, data, pos in source:
+            stream = streams[-1]
 
             if kind is START_NS:
                 # Strip out the namespace declaration for template directives
@@ -138,9 +138,9 @@
                         if not include_href:
                             raise TemplateSyntaxError('Include misses required '
                                                       'attribute "href"', *pos)
+                        streams.append([])
                     elif tag.localname == 'fallback':
-                        in_fallback = True
-                        fallback_stream = []
+                        in_fallback += 1
 
                 else:
                     stream.append((kind, (tag, new_attrs), pos))
@@ -150,13 +150,12 @@
             elif kind is END:
                 depth -= 1
 
-                if in_fallback:
-                    if data == self.XINCLUDE_NAMESPACE['fallback']:
-                        in_fallback = False
-                    else:
-                        fallback_stream.append((kind, data, pos))
+                if in_fallback and data == self.XINCLUDE_NAMESPACE['fallback']:
+                    in_fallback -= 1
                 elif data == self.XINCLUDE_NAMESPACE['include']:
-                    stream.append((INCLUDE, (include_href, fallback_stream), pos))
+                    fallback = streams.pop()
+                    stream = streams[-1]
+                    stream.append((INCLUDE, (include_href, fallback), pos))
                 else:
                     stream.append((kind, data, pos))
 
@@ -182,7 +181,14 @@
             else:
                 stream.append((kind, data, pos))
 
-        return stream
+        assert len(streams) == 1
+        return streams[0]
+
+    def _prepare(self, stream):
+        for kind, data, pos in Template._prepare(self, stream):
+            if kind is INCLUDE:
+                data = data[0], list(self._prepare(data[1]))
+            yield kind, data, pos
 
     def _include(self, stream, ctxt):
         """Internal stream filter that performs inclusion of external
@@ -204,6 +210,8 @@
                 except TemplateNotFound:
                     if fallback is None:
                         raise
+                    for filter_ in self.filters:
+                        fallback = filter_(iter(fallback), ctxt)
                     for event in fallback:
                         yield event
             else:
--- a/genshi/template/tests/markup.py
+++ b/genshi/template/tests/markup.py
@@ -276,6 +276,130 @@
         finally:
             shutil.rmtree(dirname)
 
+    def test_fallback_when_include_found(self):
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            file1 = open(os.path.join(dirname, 'tmpl1.html'), 'w')
+            try:
+                file1.write("""<div>Included</div>""")
+            finally:
+                file1.close()
+
+            file2 = open(os.path.join(dirname, 'tmpl2.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
+                  <xi:include href="tmpl1.html"><xi:fallback>
+                    Missing</xi:fallback></xi:include>
+                </html>""")
+            finally:
+                file2.close()
+
+            loader = TemplateLoader([dirname])
+            tmpl = loader.load('tmpl2.html')
+            self.assertEqual("""<html>
+                  <div>Included</div>
+                </html>""", tmpl.generate().render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_fallback_when_include_not_found(self):
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            file2 = open(os.path.join(dirname, 'tmpl2.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
+                  <xi:include href="tmpl1.html"><xi:fallback>
+                  Missing</xi:fallback></xi:include>
+                </html>""")
+            finally:
+                file2.close()
+
+            loader = TemplateLoader([dirname])
+            tmpl = loader.load('tmpl2.html')
+            self.assertEqual("""<html>
+                  Missing
+                </html>""", tmpl.generate().render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_include_in_fallback(self):
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            file1 = open(os.path.join(dirname, 'tmpl1.html'), 'w')
+            try:
+                file1.write("""<div>Included</div>""")
+            finally:
+                file1.close()
+
+            file2 = open(os.path.join(dirname, 'tmpl3.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
+                  <xi:include href="tmpl2.html">
+                    <xi:fallback>
+                      <xi:include href="tmpl1.html">
+                        <xi:fallback>Missing</xi:fallback>
+                      </xi:include>
+                    </xi:fallback>
+                  </xi:include>
+                </html>""")
+            finally:
+                file2.close()
+
+            loader = TemplateLoader([dirname])
+            tmpl = loader.load('tmpl3.html')
+            self.assertEqual("""<html>
+                  <div>Included</div>
+                </html>""", tmpl.generate().render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_nested_include_fallback(self):
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            file2 = open(os.path.join(dirname, 'tmpl3.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude">
+                  <xi:include href="tmpl2.html">
+                    <xi:fallback>
+                      <xi:include href="tmpl1.html">
+                        <xi:fallback>Missing</xi:fallback>
+                      </xi:include>
+                    </xi:fallback>
+                  </xi:include>
+                </html>""")
+            finally:
+                file2.close()
+
+            loader = TemplateLoader([dirname])
+            tmpl = loader.load('tmpl3.html')
+            self.assertEqual("""<html>
+                        Missing
+                </html>""", tmpl.generate().render())
+        finally:
+            shutil.rmtree(dirname)
+
+    def test_include_fallback_with_directive(self):
+        dirname = tempfile.mkdtemp(suffix='genshi_test')
+        try:
+            file2 = open(os.path.join(dirname, 'tmpl2.html'), 'w')
+            try:
+                file2.write("""<html xmlns:xi="http://www.w3.org/2001/XInclude"
+                      xmlns:py="http://genshi.edgewall.org/">
+                  <xi:include href="tmpl1.html"><xi:fallback>
+                    <py:if test="True">tmpl1.html not found</py:if>
+                  </xi:fallback></xi:include>
+                </html>""")
+            finally:
+                file2.close()
+
+            loader = TemplateLoader([dirname])
+            tmpl = loader.load('tmpl2.html')
+            self.assertEqual("""<html>
+                    tmpl1.html not found
+                </html>""", tmpl.generate(debug=True).render())
+        finally:
+            shutil.rmtree(dirname)
+
 
 def suite():
     suite = unittest.TestSuite()
Copyright (C) 2012-2017 Edgewall Software