From b26b9ba9711dbada32051846217fc76d6012fd81 Mon Sep 17 00:00:00 2001 From: hofmandl1 <44606069+hofmandl1@users.noreply.github.com> Date: Mon, 2 Jan 2023 11:46:56 +0100 Subject: [PATCH] Add faster ``TocTree._toctree_copy`` method (#10988) As in the standalone html builder the navigation is flattened out for every single html page, the code needs to create a specialised toctree for every html page. Previously this was done by deep-copying the complete navigation toctree and then stripping out the parts not needed on the particular page. With this change the code only (deep)-copies the needed parts of the toctree avoiding unnecessary copying and throwing-away. The performance improvements seems to be smaller for smaller page counts and get bigger the more pages are involved. Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> --- sphinx/environment/adapters/toctree.py | 53 ++++++++++++++------------ 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/sphinx/environment/adapters/toctree.py b/sphinx/environment/adapters/toctree.py index 4e0b6f00723..c4306d1c849 100644 --- a/sphinx/environment/adapters/toctree.py +++ b/sphinx/environment/adapters/toctree.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Iterable, cast +from typing import TYPE_CHECKING, Any, Iterable, TypeVar, cast from docutils import nodes from docutils.nodes import Element, Node @@ -160,10 +160,12 @@ def _entries_from_toctree(toctreenode: addnodes.toctree, parents: list[str], location=ref, type='toc', subtype='circular') continue refdoc = ref - toc = self.env.tocs[ref].deepcopy() maxdepth = self.env.metadata[ref].get('tocdepth', 0) + toc = self.env.tocs[ref] if ref not in toctree_ancestors or (prune and maxdepth > 0): - self._toctree_prune(toc, 2, maxdepth, collapse) + toc = self._toctree_copy(toc, 2, maxdepth, collapse) + else: + toc = toc.deepcopy() process_only_nodes(toc, builder.tags) if title and toc.children and len(toc.children) == 1: child = toc.children[0] @@ -258,7 +260,7 @@ def _entries_from_toctree(toctreenode: addnodes.toctree, parents: list[str], # prune the tree to maxdepth, also set toc depth and current classes _toctree_add_classes(newnode, 1) - self._toctree_prune(newnode, 1, maxdepth if prune else 0, collapse) + newnode = self._toctree_copy(newnode, 1, maxdepth if prune else 0, collapse) if isinstance(newnode[-1], nodes.Element) and len(newnode[-1]) == 0: # No titles found return None @@ -283,34 +285,35 @@ def get_toctree_ancestors(self, docname: str) -> list[str]: d = parent[d] return ancestors - def _toctree_prune(self, node: Element, depth: int, maxdepth: int, collapse: bool = False - ) -> None: - """Utility: Cut a TOC at a specified depth.""" - for subnode in node.children[:]: - if isinstance(subnode, (addnodes.compact_paragraph, - nodes.list_item)): + ET = TypeVar('ET', bound=Element) + + def _toctree_copy(self, node: ET, depth: int, maxdepth: int, collapse: bool) -> ET: + """Utility: Cut and deep-copy a TOC at a specified depth.""" + keep_bullet_list_sub_nodes = (depth <= 1 + or ((depth <= maxdepth or maxdepth <= 0) + and (not collapse or 'iscurrent' in node))) + + copy = node.copy() + for subnode in node.children: + if isinstance(subnode, (addnodes.compact_paragraph, nodes.list_item)): # for

and

  • , just recurse - self._toctree_prune(subnode, depth, maxdepth, collapse) + copy.append(self._toctree_copy(subnode, depth, maxdepth, collapse)) elif isinstance(subnode, nodes.bullet_list): - # for