Edgewall Software

Ticket #115: textinclude.diff

File textinclude.diff, 8.9 KB (added by cmlenz, 16 years ago)

Alternative patch

  • genshi/template/base.py

     
    279279    EXPR = StreamEventKind('EXPR')
    280280    """Stream event kind representing a Python expression."""
    281281
     282    INCLUDE = StreamEventKind('INCLUDE')
     283    """Stream event kind representing the inclusion of another template."""
     284
    282285    SUB = StreamEventKind('SUB')
    283286    """Stream event kind representing a nested stream to which one or more
    284287    directives should be applied.
     
    320323        except ParseError, e:
    321324            raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset)
    322325        self.filters = [self._flatten, self._eval]
     326        if loader:
     327            self.filters.append(self._include)
    323328
    324329    def __repr__(self):
    325330        return '<%s "%s">' % (self.__class__.__name__, self.filename)
     
    359364                    for event in substream:
    360365                        yield event
    361366            else:
     367                if kind is INCLUDE:
     368                    data = data[0], list(self._prepare(data[1]))
    362369                yield kind, data, pos
    363370
    364371    def generate(self, *args, **kwargs):
     
    449456            else:
    450457                yield event
    451458
     459    def _include(self, stream, ctxt):
     460        """Internal stream filter that performs inclusion of external
     461        template files.
     462        """
     463        from genshi.template.loader import TemplateNotFound
    452464
     465        for event in stream:
     466            if event[0] is INCLUDE:
     467                href, fallback = event[1]
     468                if not isinstance(href, basestring):
     469                    parts = []
     470                    for subkind, subdata, subpos in self._eval(href, ctxt):
     471                        if subkind is TEXT:
     472                            parts.append(subdata)
     473                    href = u''.join([x for x in parts if x is not None])
     474                try:
     475                    tmpl = self.loader.load(href, relative_to=event[2][0],
     476                                            cls=self.__class__)
     477                    for event in tmpl.generate(ctxt):
     478                        yield event
     479                except TemplateNotFound:
     480                    if fallback is None:
     481                        raise
     482                    for filter_ in self.filters:
     483                        fallback = filter_(iter(fallback), ctxt)
     484                    for event in fallback:
     485                        yield event
     486            else:
     487                yield event
     488
     489
    453490EXPR = Template.EXPR
     491INCLUDE = Template.INCLUDE
    454492SUB = Template.SUB
  • genshi/template/tests/text.py

     
    1212# history and logs, available at http://genshi.edgewall.org/log/.
    1313
    1414import doctest
     15import os
     16import shutil
     17import tempfile
    1518import unittest
    1619
     20from genshi.template.loader import TemplateLoader
    1721from genshi.template.text import TextTemplate
    1822
    1923
    2024class TextTemplateTestCase(unittest.TestCase):
    2125    """Tests for text template processing."""
    2226
     27    def setUp(self):
     28        self.dirname = tempfile.mkdtemp(suffix='markup_test')
     29
     30    def tearDown(self):
     31        shutil.rmtree(self.dirname)
     32
    2333    def test_escaping(self):
    2434        tmpl = TextTemplate('\\#escaped')
    2535        self.assertEqual('#escaped', str(tmpl.generate()))
     
    7484
    7585""", tmpl.generate(items=range(3)).render('text'))
    7686
     87    def test_include(self):
     88        file1 = open(os.path.join(self.dirname, 'tmpl1.txt'), 'w')
     89        try:
     90            file1.write("Included\n")
     91        finally:
     92            file1.close()
    7793
     94        file2 = open(os.path.join(self.dirname, 'tmpl2.txt'), 'w')
     95        try:
     96            file2.write("""----- Included data below this line -----
     97            #include tmpl1.txt
     98            ----- Included data above this line -----""")
     99        finally:
     100            file2.close()
     101
     102        loader = TemplateLoader([self.dirname])
     103        tmpl = loader.load('tmpl2.txt', cls=TextTemplate)
     104        self.assertEqual("""----- Included data below this line -----
     105Included
     106            ----- Included data above this line -----""",
     107                         tmpl.generate().render())
     108       
    78109def suite():
    79110    suite = unittest.TestSuite()
    80111    suite.addTest(doctest.DocTestSuite(TextTemplate.__module__))
  • genshi/template/markup.py

     
    2121from genshi.core import START, END, START_NS, END_NS, TEXT, PI, COMMENT
    2222from genshi.input import XMLParser
    2323from genshi.template.base import BadDirectiveError, Template, \
    24                                  TemplateSyntaxError, _apply_directives, SUB
     24                                 TemplateSyntaxError, _apply_directives, \
     25                                 INCLUDE, SUB
    2526from genshi.template.eval import Suite
    2627from genshi.template.interpolation import interpolate
    27 from genshi.template.loader import TemplateNotFound
    2828from genshi.template.directives import *
    2929
    3030if sys.version_info < (2, 4):
     
    5050    EXEC = StreamEventKind('EXEC')
    5151    """Stream event kind representing a Python code suite to execute."""
    5252
    53     INCLUDE = StreamEventKind('INCLUDE')
    54     """Stream event kind representing the inclusion of another template."""
    55 
    5653    DIRECTIVE_NAMESPACE = Namespace('http://genshi.edgewall.org/')
    5754    XINCLUDE_NAMESPACE = Namespace('http://www.w3.org/2001/XInclude')
    5855
     
    7370                 encoding=None, lookup='lenient'):
    7471        Template.__init__(self, source, basedir=basedir, filename=filename,
    7572                          loader=loader, encoding=encoding, lookup=lookup)
    76 
    7773        self.filters += [self._exec, self._match]
    78         if loader:
    79             self.filters.append(self._include)
    8074
    8175    def _parse(self, source, encoding):
    8276        streams = [[]] # stacked lists of events of the "compiled" template
     
    223217        assert len(streams) == 1
    224218        return streams[0]
    225219
    226     def _prepare(self, stream):
    227         for kind, data, pos in Template._prepare(self, stream):
    228             if kind is INCLUDE:
    229                 data = data[0], list(self._prepare(data[1]))
    230             yield kind, data, pos
    231 
    232220    def _exec(self, stream, ctxt):
    233221        """Internal stream filter that executes code in ``<?python ?>``
    234222        processing instructions.
     
    239227            else:
    240228                yield event
    241229
    242     def _include(self, stream, ctxt):
    243         """Internal stream filter that performs inclusion of external
    244         template files.
    245         """
    246         for event in stream:
    247             if event[0] is INCLUDE:
    248                 href, fallback = event[1]
    249                 if not isinstance(href, basestring):
    250                     parts = []
    251                     for subkind, subdata, subpos in self._eval(href, ctxt):
    252                         if subkind is TEXT:
    253                             parts.append(subdata)
    254                     href = u''.join([x for x in parts if x is not None])
    255                 try:
    256                     tmpl = self.loader.load(href, relative_to=event[2][0])
    257                     for event in tmpl.generate(ctxt):
    258                         yield event
    259                 except TemplateNotFound:
    260                     if fallback is None:
    261                         raise
    262                     for filter_ in self.filters:
    263                         fallback = filter_(iter(fallback), ctxt)
    264                     for event in fallback:
    265                         yield event
    266             else:
    267                 yield event
    268 
    269230    def _match(self, stream, ctxt, match_templates=None):
    270231        """Internal stream filter that applies any defined match templates
    271232        to the stream.
     
    341302
    342303
    343304EXEC = MarkupTemplate.EXEC
    344 INCLUDE = MarkupTemplate.INCLUDE
  • genshi/template/text.py

     
    1515
    1616import re
    1717
    18 from genshi.template.base import BadDirectiveError, Template, SUB
     18from genshi.template.base import BadDirectiveError, Template, INCLUDE, SUB
    1919from genshi.template.directives import *
     20from genshi.template.directives import Directive, _apply_directives
    2021from genshi.template.interpolation import interpolate
    2122
    2223__all__ = ['TextTemplate']
     
    9596                    substream = stream[start_offset:]
    9697                    stream[start_offset:] = [(SUB, ([directive], substream),
    9798                                              (self.filepath, lineno, 0))]
     99            elif command == 'include':
     100                pos = (self.filename, lineno, 0)
     101                stream.append((INCLUDE, (value.strip(), []), pos))
    98102            elif command != '#':
    99103                cls = self._dir_by_name.get(command)
    100104                if cls is None: