changeset 475:bb939ed3058c

Added include directive for text templates (#115). Thanks to Alastair for the original patch.
author cmlenz
date Fri, 11 May 2007 21:54:49 +0000
parents 5870bdf03fca
children 081c70ff7acd
files ChangeLog genshi/template/base.py genshi/template/markup.py genshi/template/tests/text.py genshi/template/text.py
diffstat 5 files changed, 83 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Version 0.5
+http://svn.edgewall.org/repos/genshi/tags/0.5.0/
+(?, from branches/stable/0.5.x)
+
+ * Added #include directive for text templates (ticket #115).
+
+
 Version 0.4.1
 http://svn.edgewall.org/repos/genshi/tags/0.4.1/
 (?, from branches/stable/0.4.x)
--- a/genshi/template/base.py
+++ b/genshi/template/base.py
@@ -279,6 +279,9 @@
     EXPR = StreamEventKind('EXPR')
     """Stream event kind representing a Python expression."""
 
+    INCLUDE = StreamEventKind('INCLUDE')
+    """Stream event kind representing the inclusion of another template."""
+
     SUB = StreamEventKind('SUB')
     """Stream event kind representing a nested stream to which one or more
     directives should be applied.
@@ -320,6 +323,8 @@
         except ParseError, e:
             raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
         self.filters = [self._flatten, self._eval]
+        if loader:
+            self.filters.append(self._include)
 
     def __repr__(self):
         return '<%s "%s">' % (self.__class__.__name__, self.filename)
@@ -359,6 +364,8 @@
                     for event in substream:
                         yield event
             else:
+                if kind is INCLUDE:
+                    data = data[0], list(self._prepare(data[1]))
                 yield kind, data, pos
 
     def generate(self, *args, **kwargs):
@@ -449,6 +456,37 @@
             else:
                 yield event
 
+    def _include(self, stream, ctxt):
+        """Internal stream filter that performs inclusion of external
+        template files.
+        """
+        from genshi.template.loader import TemplateNotFound
+
+        for event in stream:
+            if event[0] is INCLUDE:
+                href, fallback = event[1]
+                if not isinstance(href, basestring):
+                    parts = []
+                    for subkind, subdata, subpos in self._eval(href, ctxt):
+                        if subkind is TEXT:
+                            parts.append(subdata)
+                    href = u''.join([x for x in parts if x is not None])
+                try:
+                    tmpl = self.loader.load(href, relative_to=event[2][0],
+                                            cls=self.__class__)
+                    for event in tmpl.generate(ctxt):
+                        yield event
+                except TemplateNotFound:
+                    if fallback is None:
+                        raise
+                    for filter_ in self.filters:
+                        fallback = filter_(iter(fallback), ctxt)
+                    for event in fallback:
+                        yield event
+            else:
+                yield event
+
 
 EXPR = Template.EXPR
+INCLUDE = Template.INCLUDE
 SUB = Template.SUB
--- a/genshi/template/markup.py
+++ b/genshi/template/markup.py
@@ -21,10 +21,10 @@
 from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT
 from genshi.input import XMLParser
 from genshi.template.base import BadDirectiveError, Template, \
-                                 TemplateSyntaxError, _apply_directives, SUB
+                                 TemplateSyntaxError, _apply_directives, \
+                                 INCLUDE, SUB
 from genshi.template.eval import Suite
 from genshi.template.interpolation import interpolate
-from genshi.template.loader import TemplateNotFound
 from genshi.template.directives import *
 
 if sys.version_info < (2, 4):
@@ -50,9 +50,6 @@
     EXEC = StreamEventKind('EXEC')
     """Stream event kind representing a Python code suite to execute."""
 
-    INCLUDE = StreamEventKind('INCLUDE')
-    """Stream event kind representing the inclusion of another template."""
-
     DIRECTIVE_NAMESPACE = Namespace('http://genshi.edgewall.org/')
     XINCLUDE_NAMESPACE = Namespace('http://www.w3.org/2001/XInclude')
 
@@ -73,10 +70,7 @@
                  encoding=None, lookup='lenient'):
         Template.__init__(self, source, basedir=basedir, filename=filename,
                           loader=loader, encoding=encoding, lookup=lookup)
-
         self.filters += [self._exec, self._match]
-        if loader:
-            self.filters.append(self._include)
 
     def _parse(self, source, encoding):
         streams = [[]] # stacked lists of events of the "compiled" template
@@ -223,12 +217,6 @@
         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 _exec(self, stream, ctxt):
         """Internal stream filter that executes code in ``<?python ?>``
         processing instructions.
@@ -239,33 +227,6 @@
             else:
                 yield event
 
-    def _include(self, stream, ctxt):
-        """Internal stream filter that performs inclusion of external
-        template files.
-        """
-        for event in stream:
-            if event[0] is INCLUDE:
-                href, fallback = event[1]
-                if not isinstance(href, basestring):
-                    parts = []
-                    for subkind, subdata, subpos in self._eval(href, ctxt):
-                        if subkind is TEXT:
-                            parts.append(subdata)
-                    href = u''.join([x for x in parts if x is not None])
-                try:
-                    tmpl = self.loader.load(href, relative_to=event[2][0])
-                    for event in tmpl.generate(ctxt):
-                        yield event
-                except TemplateNotFound:
-                    if fallback is None:
-                        raise
-                    for filter_ in self.filters:
-                        fallback = filter_(iter(fallback), ctxt)
-                    for event in fallback:
-                        yield event
-            else:
-                yield event
-
     def _match(self, stream, ctxt, match_templates=None):
         """Internal stream filter that applies any defined match templates
         to the stream.
@@ -341,4 +302,3 @@
 
 
 EXEC = MarkupTemplate.EXEC
-INCLUDE = MarkupTemplate.INCLUDE
--- a/genshi/template/tests/text.py
+++ b/genshi/template/tests/text.py
@@ -12,14 +12,24 @@
 # history and logs, available at http://genshi.edgewall.org/log/.
 
 import doctest
+import os
+import shutil
+import tempfile
 import unittest
 
+from genshi.template.loader import TemplateLoader
 from genshi.template.text import TextTemplate
 
 
 class TextTemplateTestCase(unittest.TestCase):
     """Tests for text template processing."""
 
+    def setUp(self):
+        self.dirname = tempfile.mkdtemp(suffix='markup_test')
+
+    def tearDown(self):
+        shutil.rmtree(self.dirname)
+
     def test_escaping(self):
         tmpl = TextTemplate('\\#escaped')
         self.assertEqual('#escaped', str(tmpl.generate()))
@@ -74,7 +84,28 @@
 
 """, tmpl.generate(items=range(3)).render('text'))
 
+    def test_include(self):
+        file1 = open(os.path.join(self.dirname, 'tmpl1.txt'), 'w')
+        try:
+            file1.write("Included\n")
+        finally:
+            file1.close()
 
+        file2 = open(os.path.join(self.dirname, 'tmpl2.txt'), 'w')
+        try:
+            file2.write("""----- Included data below this line -----
+            #include tmpl1.txt
+            ----- Included data above this line -----""")
+        finally:
+            file2.close()
+
+        loader = TemplateLoader([self.dirname])
+        tmpl = loader.load('tmpl2.txt', cls=TextTemplate)
+        self.assertEqual("""----- Included data below this line -----
+Included
+            ----- Included data above this line -----""",
+                         tmpl.generate().render())
+        
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(TextTemplate.__module__))
--- a/genshi/template/text.py
+++ b/genshi/template/text.py
@@ -15,8 +15,9 @@
 
 import re
 
-from genshi.template.base import BadDirectiveError, Template, SUB
+from genshi.template.base import BadDirectiveError, Template, INCLUDE, SUB
 from genshi.template.directives import *
+from genshi.template.directives import Directive, _apply_directives
 from genshi.template.interpolation import interpolate
 
 __all__ = ['TextTemplate']
@@ -95,6 +96,9 @@
                     substream = stream[start_offset:]
                     stream[start_offset:] = [(SUB, ([directive], substream),
                                               (self.filepath, lineno, 0))]
+            elif command == 'include':
+                pos = (self.filename, lineno, 0)
+                stream.append((INCLUDE, (value.strip(), []), pos))
             elif command != '#':
                 cls = self._dir_by_name.get(command)
                 if cls is None:
Copyright (C) 2012-2017 Edgewall Software