# HG changeset patch # User cmlenz # Date 1162921828 0 # Node ID e5ba8c5a30fe8b7a264aea6dd125289ceb383515 # Parent 97b0d21b81b79df68d9db188e104c1fa1723a35f Fix XPath traversal in match templates. Previously, `div/p` would be treated the same as `div//p`, i.e. it would match all descendants and not just the immediate children. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -21,7 +21,9 @@ value. * Unsuccessful attribute or item lookups now return `Undefined` objects for nicer error messages. - + * Fix XPath traversal in match templates. Previously, `div/p` would be treated + the same as `div//p`, i.e. it would match all descendants and not just the + immediate children. Version 0.3.4 http://svn.edgewall.org/repos/genshi/tags/0.3.4/ diff --git a/genshi/path.py b/genshi/path.py --- a/genshi/path.py +++ b/genshi/path.py @@ -159,7 +159,9 @@ ... print event ('START', (QName(u'child'), Attrs([(QName(u'id'), u'2')])), (None, 1, 34)) """ - paths = [(p, len(p), [0], [], [0] * len(p)) for p in self.paths] + paths = [(p, len(p), [0], [], [0] * len(p)) for p in [ + (ignore_context and [_DOTSLASHSLASH] or []) + p for p in self.paths + ]] def _test(event, namespaces, variables, updateonly=False): kind, data, pos = event[:3] @@ -228,45 +230,38 @@ cursors[-1] = cursor cutoff[:] = [] - elif not ignore_context and kind is START: - cutoff[:] = [depth] - - if last_step and not ignore_context and kind is START: - if (axis is not DESCENDANT and - axis is not DESCENDANT_OR_SELF): + if kind is START: + if last_step and not (axis is DESCENDANT or + axis is DESCENDANT_OR_SELF): cutoff[:] = [depth] - if kind is START and not last_step: - next_axis = steps[cursor][0] - if next_axis is ATTRIBUTE: + elif steps[cursor][0] is ATTRIBUTE: # If the axis of the next location step is the - # attribute axis, we need to move on to processing - # that step without waiting for the next markup - # event + # attribute axis, we need to move on to + # processing that step without waiting for the + # next markup event continue # We're done with this step if it's the last step or the # axis isn't "self" - if last_step or (axis is not SELF and - axis is not DESCENDANT_OR_SELF): + if last_step or not (axis is SELF or + axis is DESCENDANT_OR_SELF): break - if kind is START and axis is not DESCENDANT \ - and axis is not DESCENDANT_OR_SELF: + if (retval or not matched) and kind is START and \ + not (axis is DESCENDANT or axis is DESCENDANT_OR_SELF): # If this step is not a closure, it cannot be matched until # the current element is closed... so we need to move the # cursor back to the previous closure and retest that # against the current element - backsteps = [(k, d, p) for k, d, p in steps[:cursor] + backsteps = [(i, k, d, p) for i, (k, d, p) + in enumerate(steps[:cursor]) if k is DESCENDANT or k is DESCENDANT_OR_SELF] backsteps.reverse() - for axis, nodetest, predicates in backsteps: - matched = nodetest(kind, data, pos, namespaces, - variables) - if not matched: - cursor -= 1 - cutoff[:] = [] - break + for cursor, axis, nodetest, predicates in backsteps: + if nodetest(kind, data, pos, namespaces, variables): + cutoff[:] = [] + break cursors[-1] = cursor return retval @@ -1077,3 +1072,6 @@ _operator_map = {'=': EqualsOperator, '!=': NotEqualsOperator, '>': GreaterThanOperator, '>=': GreaterThanOrEqualOperator, '<': LessThanOperator, '>=': LessThanOrEqualOperator} + + +_DOTSLASHSLASH = (DESCENDANT_OR_SELF, PrincipalTypeTest(None), ()) diff --git a/genshi/tests/template.py b/genshi/tests/template.py --- a/genshi/tests/template.py +++ b/genshi/tests/template.py @@ -764,21 +764,20 @@ """, str(tmpl.generate())) - # FIXME - #def test_match_without_closure(self): - # tmpl = MarkupTemplate(""" - #

${select('*|text()')}

- # - #

Foo

- #

Bar

- # - # """) - # self.assertEqual(""" - # - #

Foo

- #

Bar

- # - # """, str(tmpl.generate())) + def test_match_without_closure(self): + tmpl = MarkupTemplate(""" +

${select('*|text()')}

+ +

Foo

+

Bar

+ + """) + self.assertEqual(""" + +

Foo

+

Bar

+ + """, str(tmpl.generate())) # FIXME #def test_match_after_step(self):