SF.net SVN: docutils:[10286 ] trunk/docutils

milde--- via Docutils-checkins <[email protected]> Wed, 07 Jan 2026 13:40:45 +0000
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 10286
          http://sourceforge.net/p/docutils/code/10286
Author:   milde
Date:     2026-01-07 13:40:45 +0000 (Wed, 07 Jan 2026)
Log Message:
-----------
nodes.Element.findall() now accepts also a tuple of types as condition.

The "condition" argument to the may also be a tuple of node classes
It is converted to ``isinstance(node, class_or_tuple)``.

Modified Paths:
--------------
    trunk/docutils/HISTORY.rst
    trunk/docutils/docutils/nodes.py
    trunk/docutils/test/test_nodes.py

Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst	2026-01-07 07:52:49 UTC (rev 10285)
+++ trunk/docutils/HISTORY.rst	2026-01-07 13:40:45 UTC (rev 10286)
@@ -51,6 +51,10 @@
   - Ensure `FileInput.read()` returns a `str` (decode if `source.read`
     returns `bytes`. Fixes bug #514.
 
+* docutils/nodes.py
+
+  - Element.findall() now accepts also a tuple of types as condition.
+
 * docutils/parsers/rst/directives/body.py,
   docutils/parsers/rst/directives/parts.py
 

Modified: trunk/docutils/docutils/nodes.py
===================================================================
--- trunk/docutils/docutils/nodes.py	2026-01-07 07:52:49 UTC (rev 10285)
+++ trunk/docutils/docutils/nodes.py	2026-01-07 13:40:45 UTC (rev 10286)
@@ -245,7 +245,7 @@
             visitor.dispatch_departure(self)
         return stop
 
-    def _fast_findall(self, cls: type) -> Iterator:
+    def _fast_findall(self, cls: type|tuple[type]) -> Iterator:
         """Return iterator that only supports instance checks."""
         if isinstance(self, cls):
             yield self
@@ -262,7 +262,7 @@
             yield from child._superfast_findall()
 
     def findall(self,
-                condition: type | Callable[[Node], bool] | None = None,
+                condition: type|tuple[type]|Callable[[Node], bool]|None = None,
                 include_self: bool = True,
                 descend: bool = True,
                 siblings: bool = False,
@@ -279,9 +279,9 @@
           their descendants (if also `descend` is true), and so on.
 
         If `condition` is not None, the iterator yields only nodes
-        for which ``condition(node)`` is true.  If `condition` is a
-        type ``cls``, it is equivalent to a function consisting
-        of ``return isinstance(node, cls)``.
+        for which ``condition(node)`` is true.
+        If `condition` is a type (or tuple of types) ``cls``, it is equivalent
+        to a function consisting of ``return isinstance(node, cls)``.
 
         If `ascend` is true, assume `siblings` to be true as well.
 
@@ -314,16 +314,16 @@
             if condition is None:
                 yield from self._superfast_findall()
                 return
-            elif isinstance(condition, type):
+            elif isinstance(condition, (type, tuple)):
                 yield from self._fast_findall(condition)
                 return
         # Check if `condition` is a class (check for TypeType for Python
         # implementations that use only new-style classes, like PyPy).
-        if isinstance(condition, type):
-            node_class = condition
+        if isinstance(condition, (type, tuple)):
+            class_or_tuple = condition
 
-            def condition(node, node_class=node_class):
-                return isinstance(node, node_class)
+            def condition(node, class_or_tuple=class_or_tuple):
+                return isinstance(node, class_or_tuple)
 
         if include_self and (condition is None or condition(self)):
             yield self
@@ -349,13 +349,14 @@
                 else:
                     node = node.parent
 
-    def traverse(self,
-                 condition: type | Callable[[Node], bool] | None = None,
-                 include_self: bool = True,
-                 descend: bool = True,
-                 siblings: bool = False,
-                 ascend: bool = False,
-                 ) -> list:
+    def traverse(
+            self,
+            condition: type|tuple[type]|Callable[[Node], bool]|None = None,
+            include_self: bool = True,
+            descend: bool = True,
+            siblings: bool = False,
+            ascend: bool = False,
+            ) -> list:
         """Return list of nodes following `self`.
 
         For looping, Node.findall() is faster and more memory efficient.
@@ -366,13 +367,14 @@
         return list(self.findall(condition, include_self, descend,
                                  siblings, ascend))
 
-    def next_node(self,
-                  condition: type | Callable[[Node], bool] | None = None,
-                  include_self: bool = False,
-                  descend: bool = True,
-                  siblings: bool = False,
-                  ascend: bool = False,
-                  ) -> Node | None:
+    def next_node(
+            self,
+            condition: type|tuple[type]|Callable[[Node], bool]|None = None,
+            include_self: bool = False,
+            descend: bool = True,
+            siblings: bool = False,
+            ascend: bool = False,
+            ) -> Node | None:
         """
         Return the first node in the iterator returned by findall(),
         or None if the iterable is empty.

Modified: trunk/docutils/test/test_nodes.py
===================================================================
--- trunk/docutils/test/test_nodes.py	2026-01-07 07:52:49 UTC (rev 10285)
+++ trunk/docutils/test/test_nodes.py	2026-01-07 13:40:45 UTC (rev 10286)
@@ -58,6 +58,7 @@
         self.assertEqual(list(e[0].findall()),
                          [e[0], e[0][0], e[0][1], e[0][1][0]])
         self.testlist = [e[0][0], e[0][1]]
+        # Condition is function returning a boolean, class or tuple of classes:
         self.assertEqual(list(e[0].findall(condition=self.not_in_testlist)),
                          [e[0], e[0][1][0]])
         self.testlist.append(e[0][1][0])
@@ -64,6 +65,8 @@
         self.assertEqual(list(e[0].findall(condition=self.not_in_testlist)),
                          [e[0]])
         self.assertEqual(list(e.findall(nodes.TextElement)), [e[0][1]])
+        self.assertEqual(list(e.findall((nodes.TextElement, nodes.Text))),
+                         [e[0][1], e[0][1][0]])
 
     def test_findall_duplicate_texts(self):
         e = nodes.Element()

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.