From 3efc088b5efdd577e703893dd35067b3632cdbf8 Mon Sep 17 00:00:00 2001 From: Jeff Forcier Date: Fri, 30 Dec 2022 17:03:21 -0500 Subject: [PATCH] Fix singlehtml builder explosions, most of the time --- releases/__init__.py | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/releases/__init__.py b/releases/__init__.py index 869e33e..bec2153 100644 --- a/releases/__init__.py +++ b/releases/__init__.py @@ -581,34 +581,48 @@ def construct_nodes(releases): class BulletListVisitor(nodes.NodeVisitor): - def __init__(self, document, app): + def __init__(self, document, app, docnames): nodes.NodeVisitor.__init__(self, document) self.found_changelog = False self.app = app + # document names to seek out (eg "changelog") + self.docnames = docnames def visit_bullet_list(self, node): - # The first found bullet list (which should be the first one at the top - # level of the document) is the changelog. - if not self.found_changelog: - self.found_changelog = True - # Walk + parse into release mapping - releases, _ = construct_releases(node.children, self.app) - # Construct new set of nodes to replace the old, and we're done - node.replace_self(construct_nodes(releases)) + # Short circuit if already mutated a changelog bullet list or if the + # one being visited doesn't appear to apply. + if self.found_changelog: + return + # The bullet list's parent should be a 'section' node with 'ids' + # attribute derived from the original document name (even in singlehtml + # mode). If we don't see this, move on. + if not set(self.docnames).intersection(node.parent.attributes["ids"]): + return + # At this point, we can safely assume the node we're visiting is the + # right one to mutate. + self.found_changelog = True + # Walk + parse into release mapping + releases, _ = construct_releases(node.children, self.app) + # Construct new set of nodes to replace the old, and we're done + node.replace_self(construct_nodes(releases)) def unknown_visit(self, node): pass def generate_changelog(app, doctree, docname): - # Don't scan/mutate documents that don't match the configured document name - # (which by default is ['changelog.rst', ]). - if docname not in app.config.releases_document_name: + desired_docnames = app.config.releases_document_name + # Ensure we still work mostly-correctly in singlehtml builder situations + # (must use name substring test as RTD's singlehtml builder doesn't + # actually inherit from Sphinx's own!) + is_singlepage = "singlehtml" in app.builder.name + changelog_names = ["index"] if is_singlepage else desired_docnames + if docname not in changelog_names: return - # Find the first bullet-list node & replace it with our organized/parsed - # elements. - changelog_visitor = BulletListVisitor(doctree, app) + # Find an appropriate bullet-list node & replace it with our + # organized/parsed elements. + changelog_visitor = BulletListVisitor(doctree, app, desired_docnames) doctree.walk(changelog_visitor)