diff options
| author | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2022-06-10 11:08:46 +0000 |
|---|---|---|
| committer | milde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04> | 2022-06-10 11:08:46 +0000 |
| commit | 893829bf0b14a89e283ceded0acb2eb5c44ff3f8 (patch) | |
| tree | 9f60f3e7859b2cbf51ad5cf13c99eb81a9e6225e /docutils | |
| parent | 9e79a514f07b2a2a7e7e71cefb510750076a43e8 (diff) | |
| download | docutils-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.py | 7 | ||||
| -rwxr-xr-x | docutils/test/test_nodes.py | 39 |
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() |
