summaryrefslogtreecommitdiff
path: root/docutils
diff options
context:
space:
mode:
authormilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2019-09-11 11:39:13 +0000
committermilde <milde@929543f6-e4f2-0310-98a6-ba3bd3dd1d04>2019-09-11 11:39:13 +0000
commit5c6612e2337a8320c3020ad54aa4d90a9fffa58a (patch)
treeb6d5b54f1ce4b5091cd513d01d88bae15f858723 /docutils
parentc3afbe9ff80b3658acea109f04cd9558ea67c35c (diff)
downloaddocutils-5c6612e2337a8320c3020ad54aa4d90a9fffa58a.tar.gz
Speed up Node.next_node() ...
... by not creating a full list of following nodes in the process. Although the documented API of Node.traverse() only promises an "iterable" as return value, the implementation returned a list up to v. 0.15. Some 3rd party code still relies on this (e.g. Sphinx as of 2019-09-07). Therefore, Node.traverse() returns a list until this is sorted out. git-svn-id: http://svn.code.sf.net/p/docutils/code/trunk@8389 929543f6-e4f2-0310-98a6-ba3bd3dd1d04
Diffstat (limited to 'docutils')
-rw-r--r--docutils/docutils/nodes.py68
-rw-r--r--docutils/docutils/transforms/frontmatter.py4
2 files changed, 42 insertions, 30 deletions
diff --git a/docutils/docutils/nodes.py b/docutils/docutils/nodes.py
index 2911f9198..267a22f3d 100644
--- a/docutils/docutils/nodes.py
+++ b/docutils/docutils/nodes.py
@@ -199,21 +199,19 @@ class Node(object):
return stop
def _fast_traverse(self, cls):
- """Specialized traverse() that only supports instance checks."""
- result = []
+ """Return iterator that only supports instance checks."""
if isinstance(self, cls):
- result.append(self)
+ yield self
for child in self.children:
- result.extend(child._fast_traverse(cls))
- return result
+ for subnode in child._fast_traverse(cls):
+ yield subnode
def _all_traverse(self):
- """Specialized traverse() that doesn't check for a condition."""
- result = []
- result.append(self)
+ """Return iterator that doesn't check for a condition."""
+ yield self
for child in self.children:
- result.extend(child._all_traverse())
- return result
+ for subnode in child._all_traverse():
+ yield subnode
def traverse(self, condition=None, include_self=True, descend=True,
siblings=False, ascend=False):
@@ -252,43 +250,58 @@ class Node(object):
[<strong>, <#text: Foo>, <#text: Bar>, <reference>, <#text: Baz>]
"""
+ # Although the documented API only promises an "iterable" as return
+ # value, the implementation returned a list up to v. 0.15. Some 3rd
+ # party code still relies on this (e.g. Sphinx as of 2019-09-07).
+ # Therefore, let's return a list until this is sorted out:
+ return list(self._traverse(condition, include_self,
+ descend, siblings, ascend))
+
+ def _traverse(self, condition=None, include_self=True, descend=True,
+ siblings=False, ascend=False):
+ """Return iterator over nodes following `self`. See `traverse()`."""
if ascend:
siblings=True
# Check for special argument combinations that allow using an
# optimized version of traverse()
if include_self and descend and not siblings:
if condition is None:
- return self._all_traverse()
+ for subnode in self._all_traverse():
+ yield subnode
+ return
elif isinstance(condition, type):
- return self._fast_traverse(condition)
+ for subnode in self._fast_traverse(condition):
+ yield subnode
+ 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
def condition(node, node_class=node_class):
return isinstance(node, node_class)
- r = []
+
+
if include_self and (condition is None or condition(self)):
- r.append(self)
+ yield self
if descend and len(self.children):
for child in self:
- r.extend(child.traverse(include_self=True, descend=True,
- siblings=False, ascend=False,
- condition=condition))
+ for subnode in child._traverse(condition=condition,
+ include_self=True, descend=True,
+ siblings=False, ascend=False):
+ yield subnode
if siblings or ascend:
node = self
while node.parent:
index = node.parent.index(node)
for sibling in node.parent[index+1:]:
- r.extend(sibling.traverse(include_self=True,
- descend=descend,
- siblings=False, ascend=False,
- condition=condition))
+ for subnode in sibling._traverse(condition=condition,
+ include_self=True, descend=descend,
+ siblings=False, ascend=False):
+ yield subnode
if not ascend:
break
else:
node = node.parent
- return r
def next_node(self, condition=None, include_self=False, descend=True,
siblings=False, ascend=False):
@@ -297,14 +310,13 @@ class Node(object):
or None if the iterable is empty.
Parameter list is the same as of traverse. Note that
- include_self defaults to 0, though.
+ include_self defaults to False, though.
"""
- iterable = self.traverse(condition=condition,
- include_self=include_self, descend=descend,
- siblings=siblings, ascend=ascend)
+ node_iterator = self._traverse(condition, include_self,
+ descend, siblings, ascend)
try:
- return iterable[0]
- except IndexError:
+ return next(node_iterator)
+ except StopIteration:
return None
if sys.version_info < (3, 0):
diff --git a/docutils/docutils/transforms/frontmatter.py b/docutils/docutils/transforms/frontmatter.py
index 1611456b5..602dea7b5 100644
--- a/docutils/docutils/transforms/frontmatter.py
+++ b/docutils/docutils/transforms/frontmatter.py
@@ -281,10 +281,10 @@ class SectionSubTitle(TitlePromoter):
def apply(self):
if not getattr(self.document.settings, 'sectsubtitle_xform', 1):
return
- for section in self.document.traverse(nodes.section):
+ for section in self.document._traverse(nodes.section):
# On our way through the node tree, we are modifying it
# but only the not-yet-visited part, so that the iterator
- # returned by traverse() is not corrupted.
+ # returned by _traverse() is not corrupted.
self.promote_subtitle(section)