# HG changeset patch # User cmlenz # Date 1151879943 0 # Node ID ed370ebfa7944a90ffec2e8fb5ea5214f8541ec8 # Parent 35b9e9318fb15bdbe56dc924f14a86269e62a137 Fix for #7: match templates no longer process their own output. diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -407,51 +407,6 @@ Hello Dude - - A match template can produce the same kind of element that it matched - without entering an infinite recursion: - - >>> tmpl = Template(''' - ... - ...
${select('*/text()')}
- ...
- ... Hey Joe - ...
''') - >>> print tmpl.generate() - - -
Hey Joe
-
-
- - Match directives are applied recursively, meaning that they are also - applied to any content they may have produced themselves: - - >>> tmpl = Template(''' - ... - ...
- ... ${select('*/*')} - ...
- ...
- ... - ... - ... - ... - ... - ...
''') - >>> print tmpl.generate() - - -
- - -
-
-
-
-
-
-
""" __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() diff --git a/markup/tests/template.py b/markup/tests/template.py --- 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(''' + +
${select('*/text()')}
+
+ Hey Joe +
''') + self.assertEqual(""" + +
Hey Joe
+
+
""", 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(''' + +
+ ${select('*/*')} +
+
+ + + + + +
''') + self.assertEqual(""" + +
+ + +
+
+
+
+
+
+
""", 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(''' + +