summaryrefslogtreecommitdiff
path: root/docutils
diff options
context:
space:
mode:
authormilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2022-06-10 11:08:46 +0000
committermilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2022-06-10 11:08:46 +0000
commit893829bf0b14a89e283ceded0acb2eb5c44ff3f8 (patch)
tree9f60f3e7859b2cbf51ad5cf13c99eb81a9e6225e /docutils
parent9e79a514f07b2a2a7e7e71cefb510750076a43e8 (diff)
downloaddocutils-893829bf0b14a89e283ceded0acb2eb5c44ff3f8.tar.gz
Fix `nodes.Node.findall()` for Text nodes.
As `nodes.Text` inherits from `str`, Text nodes with the same content are considered equal and ``nodes.Element.index(Text('sample'))`` may return a preceding Text node with content "sample". Therefore, Node.findall() must perform an additional test for identity with the start node. Fixes bug #448. Thanks to Adam Turner. git-svn-id: https://svn.code.sf.net/p/docutils/code/trunk@9067 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'docutils')
-rw-r--r--docutils/docutils/nodes.py7
-rwxr-xr-xdocutils/test/test_nodes.py39
2 files changed, 44 insertions, 2 deletions
diff --git a/docutils/docutils/nodes.py b/docutils/docutils/nodes.py
index abbb698fd..72e25bb82 100644
--- a/docutils/docutils/nodes.py
+++ b/docutils/docutils/nodes.py
@@ -297,6 +297,9 @@ class Node:
node = self
while node.parent:
index = node.parent.index(node)
+ # extra check since Text nodes have value-equality
+ while node.parent[index] is not node:
+ index = node.parent.index(node, index + 1)
for sibling in node.parent[index+1:]:
yield from sibling.findall(
condition=condition,
@@ -734,8 +737,8 @@ class Element(Node):
def remove(self, item):
self.children.remove(item)
- def index(self, item):
- return self.children.index(item)
+ def index(self, item, start=0, stop=sys.maxsize):
+ return self.children.index(item, start, stop)
def is_not_default(self, key):
if self[key] == [] and key in self.list_attributes:
diff --git a/docutils/test/test_nodes.py b/docutils/test/test_nodes.py
index 70241d388..2e5a5f8ad 100755
--- a/docutils/test/test_nodes.py
+++ b/docutils/test/test_nodes.py
@@ -64,6 +64,11 @@ class TextTests(unittest.TestCase):
self.assertEqual(self.longtext.shortrepr(),
r"<#text: 'Mary had a lit ...'>")
+ def test_comparison(self):
+ # Text nodes are compared by value
+ self.assertEqual(self.text, 'Line 1.\nLine 2.')
+ self.assertEqual(self.text, nodes.Text('Line 1.\nLine 2.'))
+
def test_Text_rawsource_deprection_warning(self):
with self.assertWarnsRegex(DeprecationWarning,
'"rawsource" is ignored'):
@@ -119,6 +124,22 @@ class ElementTests(unittest.TestCase):
self.assertEqual(element.pformat(),
'<Element attr="1">\n text\n more\n')
+ def test_index(self):
+ # Element.index() behaves like list.index() on the element's children
+ e = nodes.Element()
+ e += nodes.Element()
+ e += nodes.Text('sample')
+ e += nodes.Element()
+ e += nodes.Text('other sample')
+ e += nodes.Text('sample')
+ # return element's index for the first four children:
+ for i in range(4):
+ self.assertEqual(e.index(e[i]), i)
+ # Caution: mismatches are possible for Text nodes
+ # as they are compared by value (like `str` instances)
+ self.assertEqual(e.index(e[4]), 1)
+ self.assertEqual(e.index(e[4], start=2), 4)
+
def test_clear(self):
element = nodes.Element()
element += nodes.Element()
@@ -545,6 +566,24 @@ class MiscTests(unittest.TestCase):
[e[0]])
self.assertEqual(list(e.findall(nodes.TextElement)), [e[0][1]])
+ def test_findall_duplicate_texts(self):
+ e = nodes.Element()
+ e += nodes.TextElement()
+ e[0] += nodes.Text('one')
+ e[0] += nodes.Text('two')
+ e[0] += nodes.Text('three')
+ e[0] += nodes.Text('two')
+ e[0] += nodes.Text('five')
+ full_list = list(e[0][0].findall(siblings=True))
+ self.assertEqual(len(full_list), 5)
+ for i in range(5):
+ self.assertIs(full_list[i], e[0][i])
+
+ partial_list = list(e[0][3].findall(siblings=True))
+ self.assertEqual(len(partial_list), 2)
+ self.assertIs(partial_list[0], e[0][3])
+ self.assertIs(partial_list[1], e[0][4])
+
def test_next_node(self):
e = nodes.Element()
e += nodes.Element()