changeset 333:e5ba8c5a30fe trunk

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.
author cmlenz
date Tue, 07 Nov 2006 17:50:28 +0000
parents 97b0d21b81b7
children 8e651f5f2ee0
files ChangeLog genshi/path.py genshi/tests/template.py
diffstat 3 files changed, 40 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- 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/
--- 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), ())
--- a/genshi/tests/template.py
+++ b/genshi/tests/template.py
@@ -764,21 +764,20 @@
           </body>
         </html>""", str(tmpl.generate()))
 
-    # FIXME
-    #def test_match_without_closure(self):
-    #    tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
-    #      <p py:match="body/p" class="para">${select('*|text()')}</p>
-    #      <body>
-    #        <p>Foo</p>
-    #        <div><p>Bar</p></div>
-    #      </body>
-    #    </html>""")
-    #    self.assertEqual("""<html>
-    #      <body>
-    #        <p class="para">Foo</p>
-    #        <div><p>Bar</p></div>
-    #      </body>
-    #    </html>""", str(tmpl.generate()))
+    def test_match_without_closure(self):
+        tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/">
+          <p py:match="body/p" class="para">${select('*|text()')}</p>
+          <body>
+            <p>Foo</p>
+            <div><p>Bar</p></div>
+          </body>
+        </html>""")
+        self.assertEqual("""<html>
+          <body>
+            <p class="para">Foo</p>
+            <div><p>Bar</p></div>
+          </body>
+        </html>""", str(tmpl.generate()))
 
     # FIXME
     #def test_match_after_step(self):
Copyright (C) 2012-2017 Edgewall Software