Mercurial > genshi > mirror
changeset 36:ed370ebfa794 trunk
Fix for #7: match templates no longer process their own output.
author | cmlenz |
---|---|
date | Sun, 02 Jul 2006 22:39:03 +0000 |
parents | 35b9e9318fb1 |
children | 37557b8fb925 |
files | markup/template.py markup/tests/template.py |
diffstat | 2 files changed, 91 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/markup/template.py +++ b/markup/template.py @@ -407,51 +407,6 @@ Hello Dude </span> </div> - - A match template can produce the same kind of element that it matched - without entering an infinite recursion: - - >>> tmpl = Template('''<doc xmlns:py="http://purl.org/kid/ns#"> - ... <elem py:match="elem"> - ... <div class="elem">${select('*/text()')}</div> - ... </elem> - ... <elem>Hey Joe</elem> - ... </doc>''') - >>> print tmpl.generate() - <doc> - <elem> - <div class="elem">Hey Joe</div> - </elem> - </doc> - - Match directives are applied recursively, meaning that they are also - applied to any content they may have produced themselves: - - >>> tmpl = Template('''<doc xmlns:py="http://purl.org/kid/ns#"> - ... <elem py:match="elem"> - ... <div class="elem"> - ... ${select('*/*')} - ... </div> - ... </elem> - ... <elem> - ... <subelem> - ... <elem/> - ... </subelem> - ... </elem> - ... </doc>''') - >>> print tmpl.generate() - <doc> - <elem> - <div class="elem"> - <subelem> - <elem> - <div class="elem"> - </div> - </elem> - </subelem> - </div> - </elem> - </doc> """ __slots__ = ['path', 'stream'] @@ -798,10 +753,13 @@ raise TemplateSyntaxError(err, self.filename, pos[1], pos[2] + (err.offset or 0)) - def _match(self, stream, ctxt=None): + def _match(self, stream, ctxt=None, match_templates=None): """Internal stream filter that applies any defined match templates to the stream. """ + if match_templates is None: + match_templates = ctxt._match_templates + for kind, data, pos in stream: # We (currently) only care about start and end events for matching @@ -810,12 +768,7 @@ yield kind, data, pos continue - for idx, (test, path, template) in enumerate(ctxt._match_templates): - if (kind, data, pos) in template[::len(template)]: - # This is the event this match template produced itself, so - # matching it again would result in an infinite loop - continue - + for idx, (test, path, template) in enumerate(match_templates): result = test(kind, data, pos) if result: @@ -835,10 +788,12 @@ test(*event) content = list(self._flatten(content, ctxt)) - ctxt.push(select=lambda path: Stream(content).select(path)) - for event in self._match(self._eval(iter(template), ctxt), - ctxt): + + template = self._match(self._eval(iter(template), ctxt), + ctxt, match_templates[:idx] + + match_templates[idx + 1:]) + for event in template: yield event ctxt.pop()
--- a/markup/tests/template.py +++ b/markup/tests/template.py @@ -20,7 +20,87 @@ TemplateSyntaxError +class MatchDirectiveTestCase(unittest.TestCase): + """Tests for the `py:match` template directive.""" + + def test_without_strip(self): + """ + Verify that a match template can produce the same kind of element that + it matched without entering an infinite recursion. + """ + tmpl = Template('''<doc xmlns:py="http://purl.org/kid/ns#"> + <elem py:match="elem"> + <div class="elem">${select('*/text()')}</div> + </elem> + <elem>Hey Joe</elem> + </doc>''') + self.assertEqual("""<doc> + <elem> + <div class="elem">Hey Joe</div> + </elem> + </doc>""", str(tmpl.generate())) + + def test_recursive_match_1(self): + """ + Match directives are applied recursively, meaning that they are also + applied to any content they may have produced themselves: + """ + tmpl = Template('''<doc xmlns:py="http://purl.org/kid/ns#"> + <elem py:match="elem"> + <div class="elem"> + ${select('*/*')} + </div> + </elem> + <elem> + <subelem> + <elem/> + </subelem> + </elem> + </doc>''') + self.assertEqual("""<doc> + <elem> + <div class="elem"> + <subelem> + <elem> + <div class="elem"> + </div> + </elem> + </subelem> + </div> + </elem> + </doc>""", str(tmpl.generate())) + + def test_recursive_match_2(self): + """ + When two or more match templates match the same element and also + themselves output the element they match, avoiding recursion is even + more complex, but should work. + """ + tmpl = Template('''<html xmlns:py="http://purl.org/kid/ns#"> + <body py:match="body"> + <div id="header"/> + ${select('*/*')} + </body> + <body py:match="body"> + ${select('*/*')} + <div id="footer"/> + </body> + <body> + <h1>Foo</h1> + </body> + </html>''') + self.assertEqual("""<html> + <body> + <div id="header"/><h1>Foo</h1> + <div id="footer"/> + </body> + </html>""", str(tmpl.generate())) + + class TemplateTestCase(unittest.TestCase): + """Tests for basic template processing, expression evaluation and error + reporting. + """ def test_interpolate_string(self): parts = list(Template._interpolate('bla')) @@ -105,6 +185,7 @@ suite = unittest.TestSuite() suite.addTest(doctest.DocTestSuite(Template.__module__)) suite.addTest(unittest.makeSuite(TemplateTestCase, 'test')) + suite.addTest(unittest.makeSuite(MatchDirectiveTestCase, 'test')) return suite if __name__ == '__main__':