# HG changeset patch # User cmlenz # Date 1206569436 0 # Node ID 07e3f6f0ef576e52eb19c1999d86f27e9d4d7f9e # Parent 35e14338870583ec9dd8e10ebe2a06dba27e5ecd Match templates are now applied in a more controlled fashion: in the order they are declared in the template source, all match templates up to (and including) the matching template itself are applied to the matched content, whereas the match templates declared after the matching template are only applied to the generated content. Fixes #186. Many thanks to Matt Chaput for reporting the problem and providing a test case. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -51,6 +51,11 @@ output, instead of building a big string and returning it. * The XHTML serializer now strips `xml:space` attributes as they are only allowed on very few tags. + * Match templates are now applied in a more controlled fashion: in the order + they are declared in the template source, all match templates up to (and + including) the matching template itself are applied to the matched content, + whereas the match templates declared after the matching template are only + applied to the generated content (ticket #186). Version 0.4.4 diff --git a/doc/upgrade.txt b/doc/upgrade.txt --- a/doc/upgrade.txt +++ b/doc/upgrade.txt @@ -30,6 +30,18 @@ warned that lenient error handling may be removed completely in a future release. +There has also been a subtle change to how ``py:match`` templates are +processed: in previous versions, all match templates would be applied +to the content generated by the matching template, and only the +matching template itself was applied recursively to the original +content. This behavior resulted in problems with many kinds of +recursive matching, and hence was changed for 0.5: now, all match +templates declared before the matching template are applied to the +original content, and match templates declared after the matching +template are applied to the generated content. This change should not +have any effect on most applications, but you may want to check your +use of match templates to make sure. + Upgrading from Genshi 0.3.x to 0.4.x ------------------------------------ diff --git a/doc/xml-templates.txt b/doc/xml-templates.txt --- a/doc/xml-templates.txt +++ b/doc/xml-templates.txt @@ -322,6 +322,12 @@ .. _`Using XPath`: streams.html#using-xpath +Match templates are applied both to the original markup as well to the +generated markup. The order in which they are applied depends on the order +they are declared in the template source: a match template defined after +another match template is applied to the output generated by the first match +template. The match templates basically form a pipeline. + This directive can also be used as an element: .. code-block:: genshi diff --git a/genshi/template/markup.py b/genshi/template/markup.py --- a/genshi/template/markup.py +++ b/genshi/template/markup.py @@ -272,9 +272,10 @@ # Consume and store all events until an end event # corresponding to this start event is encountered inner = _strip(stream) - if 'match_once' not in hints \ - and 'not_recursive' not in hints: - inner = self._match(inner, ctxt, [match_templates[idx]]) + pre_match_templates = match_templates[:idx + 1] + if 'match_once' not in hints and 'not_recursive' in hints: + pre_match_templates.pop() + inner = self._match(inner, ctxt, pre_match_templates) content = list(self._include(chain([event], inner, tail), ctxt)) @@ -290,11 +291,9 @@ # Recursively process the output template = _apply_directives(template, ctxt, directives) remaining = match_templates - if 'match_once' not in hints: - remaining = remaining[:idx] + remaining[idx + 1:] for event in self._match(self._exec( self._eval(self._flatten(template, ctxt), - ctxt), ctxt), ctxt, remaining): + ctxt), ctxt), ctxt, match_templates[idx + 1:]): yield event ctxt.pop() diff --git a/genshi/template/tests/directives.py b/genshi/template/tests/directives.py --- a/genshi/template/tests/directives.py +++ b/genshi/template/tests/directives.py @@ -631,6 +631,32 @@ """, str(tmpl.generate())) + def test_recursive_match_3(self): + tmpl = MarkupTemplate(""" + + ${select('*|text()')} + + + + + + ${select('*|text()')} + + + + + 1 + 2 + + + + """) + self.assertEqual(""" + + + + """, str(tmpl.generate())) + def test_not_match_self(self): """ See http://genshi.edgewall.org/ticket/77