Sam Hooke

PlantUML in Sphinx (using MyST Markdown) and GitLab

tl;dr: PlantUML in Sphinx cannot be written in a way that is compatible with both MyST Markdown and the GitLab UI preview. You need to either use reStructuredText instead of Markdown or stick with Markdown and forget about GitLab UI preview support).

Context §

I have some documentation written in Markdown, and use Sphinx to convert it into HTML. By default, Sphinx uses reStructuredText, but I have used MyST to get Markdown support.

With this existing setup, my goal was to do the following:

  • In Markdown, add diagrams within code fences using PlantUML, which are then rendered into the HTML when Sphinx executes.
  • Additionally, be able to view the diagrams in GitHub preview.

Long story short, as far as I can tell, this isn’t currently possible. The crux of the issue is:

Example of issue §

Works in GitLab Markdown (but not MyST) §

The following will render in GitLab Markdown:

```plantuml
Bob -> Alice : hello
```

But under Sphinx with MyST Markdown will print WARNING: Pygments lexer name 'plantuml' is not known at build time.

Works in MyST Markdown (but not GitLab) §

The following will render in Sphinx with MyST Markdown:

```{uml}
Bob -> Alice : hello
```

But will show as plain-text in the GitLab UI.

Setup §

First, here are the steps for adding PlantUML support to Sphinx, using MyST Markdown.

Assumptions §

  • Sphinx is already set up using MyST for Markdown support.
  • Platform is Windows 10.

Install Java §

Chances are you already have Java installed. But if not, PlantUML needs Java for it to run.

Install PlantUML §

Download the PlantUML compiled jar file, and save it to a safe location. I placed it at C:\plantuml\plantuml-1.2023.12.jar.

Install sphinxcontrib-plantuml §

The following Python packages is required to add PlantUML support to Sphinx:

pip install sphinxcontrib-plantuml

Update your conf.py §

To correctly configure sphinxcontrib-plantuml, make the following changes:

  • Add sphinxcontrib.plantuml to the list of extensions in conf.py:
extensions = [
    # ...
    "sphinxcontrib.plantuml",
]
  • Append the following line to your conf.py. Update the path to match where you put the PlantUML jar file23.
plantuml = ["java", "-jar", "C:\plantuml\plantuml-1.2023.12.jar"]

Example §

Now you can include PlantUML diagrams in your MyST Markdown as follows:

```{uml}
Bob -> Alice : hello
```

Which should get rendered as:

Example of PlantUML
Example of PlantUML

However, the challenge is to get this working in both MyST Markdown and the GitLab UI preview. Following are three possible solutions, none of which are ideal:

  1. Put the diagram in a separate *.rst file, but use *.md for the main document.
  2. Just use *.rst for documents with diagrams (but use *.md for other documents).
  3. Just use *.md for all documents (and forget about GitLab UI preview support) (my preferred option).

Solutions §

Include diagram as reStructuredText §

MyST provides the eval-rst directive, which allows using the reStructuredText to include another reStructuredText file:

# A MyST Markdown document

Here is my diagram using reStructuredText:

```{eval-rst}
.. include:: diagram.rst
```
diagram.rst §
.. uml::

   Bob -> Alice : hello

This means we can write the document in Markdown, and the diagrams will render in GitLab UI if you are viewing the file directly (e.g. in this case, if you had diagram.rst open in the GitLab UI).

This workaround is not ideal, because the reader must open each diagram individually in order for it to render within the GitLab UI. By this point it is probably better to just use reStructuredText.

Pros:

  • Technically, the PlantUML does get rendered in Sphinx and in the GitLab UI preview.

Cons:

  • Have to put diagrams in separate files, which are reStructuredText.
  • In the GitLab UI, have to open the individual files to see them rendered.

Just use reStructuredText §

With Sphinx and MyST, you can use reStructuredText and Markdown side-by-side.

A reStructuredText document
===========================

Here is my diagram:

.. uml::

   Bob -> Alice : hello

This will render in both Sphinx and the GitLab UI.

Pros:

  • The PlantUML does get rendered in Sphinx and the GitLab UI preview, all in one document.

Cons:

  • We forgoe using Markdown.

Forget about GitLab UI support (and just use GitLab Pages) §

Alternatively, just stick with Markdown, and forget about trying to also support rendering in the GitLab UI. Assuming you are building the documentation in CI and pushing it to GitLab Pages, you can just include a link in the project’s README.md to the GitLab Pages URL.

# A MyST Markdown document

Here is my diagram:

```{uml}
Bob -> Alice : hello
```

Pros:

  • We avoid using reStructuredText, and just use Markdown.

Cons:

  • We forgoe GitLab UI preview support.

Ideal Solution §

In my opinion, the ideal solution would be for GitLab to add support for rendering PlantUML written in Markdown files within the {uml} directive (in adddition to the already supported plantuml directive). As a precedence, GitLab already added support for rendering PlantUML in reStructuredText files within the uml:: directive, specifically for compatibility with sphinxcontrib-plantuml:

Although you can use the uml:: directive for compatibility with sphinxcontrib-plantuml, GitLab supports only the caption option.

If GitLabs upported the {uml} directive then it would be possible to use MyST Markdown without forgoing GitLab UI preview support.

Dead ends §

Embed diagram as reStructredText §

Following on from the earlier workaround, you might be tempted to use uml:: inside a an {eval-rst} block, since as previous mentioned GitLab UI supports rendering the uml:: directive in reStructuredText, e.g.:

```{eval-rst}
.. uml::

  @startuml
  Bob -> Alice : hello
  @enduml
```

Unfortunately the uml:: block does not render in GitLab, presumably because it is embedded within the {eval-rst} block.

Executable Books issue §

This two year old issue on the Executable Books GitHub page describes a very similar issue, but has no answer.


  1. The {uml} directive is because sphinxcontrib-plantuml uses the .. uml:: directive, which in MyST parlance becomes a code fence with curly braces around the directive name. ↩︎

  2. This issue was useful in figuring out how to get the plantuml = ... line to work correctly under Windows. ↩︎

  3. If the plantuml = ... line is omitted, or if the command fails for any reason, at Sphinx build time you will get WARNING: plantuml command 'plantuml' cannot be run. This can be a little opaque to debug. ↩︎

These are rough notes that vary greatly in quality and length, but prove useful to me, and hopefully to you too!

← Previous: Managing multiple remotes in Git
Next: python-systemd != python-systemd →