Search code examples
pythoneventspython-sphinxdirectiveplantuml

sphinx doc: How to render dynamic rst content inside sphinx event handlers?


I'm the maintainer of sphinx-needs, which allows the definition of requirements, bugs, ...

These objects get stored inside the related directive run function and I finally get a list of all objects inside my sphinx documentation.

The objects can be linked to each other and I want to create a diagram of these links (using plantuml and related sphinx-plantuml extension).

The problem is, that I don't know how to let sphinx add and rerender a dynamically created plantuml diagram specification.

Inside a directive, I could use the state_machine to get this done:

diagram = generate_plantuml_spec(objects)
self.state_machine.insert_input(
        diagram.split('\n'),
        self.state_machine.document.attributes['source'])

But that's the wrong place, as my extension has not collected all objects yet.

So the right place should be inside a function, which gets executed when the event "doctree-resolved" is fired by sphinx.

Inside this function I'm able to add any kind of docutils nodes. My idea is to create a node.Text() with the generated diagram spec

 def process_needfilters(app, doctree, fromdocname):
     diagram_data = """"
                    .. plantuml::

                       @startuml
                       node Test
                       @enduml
                    """
     for node in doctree.traverse(needs_diagram):
         content = []
         diagram = nodes.Text(diagram_data, diagram_data)
         content.append(diagram)
         node.replace_self(content)

However this will not force sphinx to render the content.

I also found a sphinx function called nested_parse_with_titles(), but this needs a 'state' object, which is only available inside a directive and not inside an event handler.

So, any idea how I can render rst content inside sphinx event handlers?


Solution

  • Solution is to use directly the plantuml node from the plantuml-extension:

    Simplified code snippet from sphinx-needs:

    from sphinxcontrib.plantuml import plantuml
    
    for node in doctree.traverse(MyDirective):
        plantuml_block_text = ".. plantuml::\n" \
                              "\n" \
                              "   @startuml" \
                              "   @enduml"
        puml_node = plantuml(plantuml_block_text, **dict())
        node.replace_self(puml_node)