pythondocumentationpython-sphinxdocutils

In Sphinx, how can I generate a page with information about all items in a domain?


I've written a custom domain in Sphinx. It has directives that register items (which all end up in the data member) then parallel builds get merged together with merge_domaindata.

I want to create an index-like page that lists every item registered in the domain. I want more control than I could get with a regular Index -- in particular, I'd like to generate some custom nodes for each item in my domain's data.

Is there any way to do that? Looking at the core events sequence, it looks like I'd have to delay until after all the env-merge-info event finishes (because before then, we haven't merged the multiple domain objects together). But I want to be able to use cross-references in my generated nodes, so it should be before the reference resolver post-transform.

Could I maybe register a post-transform to do it? How could I go about that, and what would be the recommended transform priority?


Solution

  • I'm still looking for better answers, but here's at least one way to achieve this.

    The key idea is:

    1. Use a custom directive to insert an identifiable node into the tree.
    2. Register a post-transform to detect that node and replace it with what you want.

    For me, this ended up looking something like this.

    First, a directive that just inserts a pending node:

    class CustomIndexDirective(SphinxDirective):
        def run(self) -> list[nodes.Node]:
            return [nodes.pending(None, transform_target="custom_index_transform")]
    

    Then, a post-transform transformer that finds the pending node and replaces it with whatever we want to generate:

    class CustomIndexTransform(SphinxPostTransform):
        default_priority = 0 # still not sure if this is a best practice, but 0 seems to work
    
        for pending_node in self.document.findall(
                lambda node: isinstance(node, nodes.pending)
                and node.attributes.get("transform_target") == "custom_index_transform"
            ):
    
            my_domain = self.env.get_domain("my_domain")
    
            new_node = ... # implement as necessary based on my_domain.data
    
            pending_node.replace_self(new_node)
    

    In the extension root, register both the directive and the transform:

    def setup(app: Sphinx) -> ExtensionMetadata:
        app.add_directive("custom-index", CustomIndexDirective)
        app.add_post_transform(CustomIndexTransform)
    

    Finally, in one of the .rst files in the source tree, invoke the custom directive:

    .. custom-index::