This directory contains a small Sphinx extension that integrates UMLet diagrams into
Sphinx builds. The extension locates .uxf source files, converts them to SVG during
the Sphinx build, and emits the correct HTML image references in the generated pages.
Purpose
- Make UMLet feel like a native Sphinx asset type so authors only need to write a simple directive in RST and the extension does the rest (resolve source, convert, register dependencies, and produce document-relative URIs).
Example RST usage
.. uml:: models/applications
:alt: Applications Architecture
:align: center
:width: 100%What the extension does
- Resolve the
.uxfsource path relative to the current document. - Register the UXF file as a dependency in the Sphinx environment for incremental builds.
- Convert UXF → SVG during the build and write the SVG under
app.outdir, preserving the source-relative directory structure. - Emit an HTML
<img>element whosesrcis a document-relative URI pointing at the generated SVG.
Important: path distinctions
- The extension treats the following independently:
- Source filesystem path (e.g.
source/nodes/models/applications.uxf). - Output filesystem path (e.g.
build/html/nodes/models/applications.svg). - Document-relative URI used in HTML (e.g. from
build/html/nodes/index.htmlthe correct URI ismodels/applications.svg).
- Source filesystem path (e.g.
Installation / Enable
- Ensure the extension package is available under
source/_extension/umlet. - Add the extension to
extensionsinconf.py:
extensions = [
"_extension.umlet",
]Usage and build
-
Place your
.uxffiles in thesource/tree where appropriate. The directive argument is interpreted relative to the RST document containing the directive.Example source layout:
source/ └── nodes/ ├── index.rst (contains the directive) └── models/ └── applications.uxf
-
In
index.rstuse the directive shown above. -
Build the docs as usual:
make htmlAfter a successful build the generated SVG will be placed under build/html/... and
the produced HTML will contain an <img> tag with a document-relative src, for
example:
<img alt="Applications Architecture" class="align-center" width="100%" src="models/applications.svg" />Directive options
:alt:— alternative text for the image:align:— alignment class (for examplecenter):width:— width value (e.g.100%,600px):height:— height value:class:— additional classes applied to the image element
Development notes
- Conversion happens during the Sphinx build stage; the directive does not invoke UMLet directly while parsing.
- The extension records UXF dependencies in
envso Sphinx incremental builds avoid unnecessary conversions. - Generated files are never written into the
source/directory. Output is underapp.outdir(usePath(app.srcdir)andPath(app.outdir)in code; do not hard-codebuild/html). - The extension keeps a custom node (
UMLetAsset) and produces HTML directly. It does not convert assets intonodes.imageto avoid interfering with Sphinx's image post-processing.
Error handling and logging
- If a
.uxffile is missing the extension will produce a Sphinx warning/error tied to the document rather than allowing raw exceptions to escape. - If the UMLet conversion fails the log will include source and output paths,
the command used, the return code and
stderrto help debugging.
Files and responsibilities (summary)
__init__.py— register node, directive and event handlersasset.py—UMLetAssetnode definitiondirective.py—.. uml::directive and registration of assets inenvevents.py— generate SVGs at the correct build stage and manage environment datarenderer.py— UXF → SVG execution logic (independent of specific Snap revisions)html.py— HTML emitter forUMLetAsset
If you want, I can add a minimal .uxf example and a local testing guide for running
UMLet (JAR or Snap) to the README.
Use the current document's docname / source path information provided by Sphinx rather than manually assuming a fixed directory.
The generated SVG must preserve the source-relative directory.
Example:
source/
└── nodes/
├── index.rst
└── models/
├── applications.uxf
└── cluster.uxf
must generate:
build/html/
└── nodes/
├── index.html
└── models/
├── applications.svg
└── cluster.svg
Never generate:
build/html/models/applications.svg
for the above source structure.
Filesystem paths and HTML URIs must be calculated independently.
For:
build/html/nodes/index.html
and:
build/html/nodes/models/applications.svg
the URI is:
models/applications.svg
The extension should use Sphinx's URI/path utilities where appropriate.
Do not manually prepend the document name.
Do not produce:
nodes/models/applications.svg
when the current HTML document is already inside:
nodes/
The generated src must always be relative to the current output document.
Input:
source/nodes/index.rst
source/nodes/models/applications.uxf
RST:
.. uml:: models/applications
:alt: Applications Architecture
:align: center
:width: 100%Output:
build/html/nodes/index.html
build/html/nodes/models/applications.svg
HTML:
<img
alt="Applications Architecture"
class="align-center"
width="100%"
src="models/applications.svg"
/>The extension should support Sphinx incremental builds.
Do not blindly regenerate every UML diagram on every build.
The implementation should compare:
UXF modification time
SVG modification time
or use appropriate Sphinx environment dependency tracking.
If:
applications.uxf
has not changed and:
applications.svg
already exists, the renderer should normally avoid unnecessary conversion.
If the UXF changes, the SVG must be regenerated.
If the SVG is deleted, it must be regenerated.
If a document is removed, its associated environment information should be purged.
The extension must be designed with Sphinx parallel builds in mind.
The setup metadata should accurately declare:
"parallel_read_safe": Trueand:
"parallel_write_safe": Trueonly if the implementation is actually safe.
Avoid global mutable state.
Do not use module-level dictionaries as the source of truth for assets.
Store persistent information in the Sphinx environment where appropriate.
If a UXF file does not exist, produce a proper Sphinx warning/error associated with the document.
Example:
UMLet file not found: models/applications.uxf
Do not allow raw Python exceptions such as:
FileNotFoundError
to escape unless it represents a genuine programming error.
If UMLet conversion fails, provide:
- source UXF path
- output SVG path
- UMLet command
- process return code
- stderr
This should make debugging easy.
Use Sphinx's logging API:
from sphinx.util import logging
logger = logging.getLogger(__name__)Do not use print() for normal logging.
Examples:
logger.info(
"Generating UMLet asset: %s",
source_path,
)logger.info(
"UMLet output: %s",
output_path,
)logger.warning(
"UMLet file not found: %s",
source_path,
)All code comments and docstrings must be written in International English.
The following pattern must NOT be used:
image = nodes.image(
uri=svg_uri,
)This previously caused Sphinx HTML post-processing to fail with:
KeyError: 'candidates'
The correct design is:
UMLetAsset
↓
HTML translator
↓
<img ...>
The current project uses:
Ubuntu 24.04
Python 3.12
Sphinx 9.1
Docutils 0.22
UMLet 15.1
pydata-sphinx-theme
The extension must work with the project's current Sphinx version.
The active HTML translator may be a theme-specific translator such as:
BootstrapHTML5Translator
Therefore the extension must not assume that the active translator is exactly:
HTML5TranslatorUse Sphinx's supported node registration mechanisms rather than replacing the entire HTML translator.
The final implementation should look like:
source/
└── _extension/
└── umlet/
├── __init__.py
├── asset.py
├── directive.py
├── events.py
├── html.py
└── renderer.py
Responsibilities:
__init__.py
Extension registration
asset.py
UMLetAsset Docutils node
directive.py
.. uml:: directive
events.py
Sphinx build lifecycle
Asset generation
Environment management
renderer.py
UMLet execution
UXF → SVG conversion
html.py
UMLetAsset → HTML rendering
When modifying the extension:
- Preserve the separation between source paths, output paths and HTML URIs.
- Never write generated files into
source/. - Never convert
UMLetAssetintonodes.image. - Never hard-code
build/html. - Never hard-code a Snap revision such as
/snap/umlet-standalone/54. - Use
pathlib.Path. - Use Sphinx logging.
- Use Sphinx/Docutils APIs instead of manipulating generated HTML after rendering.
- Keep the extension independent from the active Sphinx theme.
- Keep the implementation compatible with incremental Sphinx builds.
- Keep parallel build safety in mind.
- Add tests for path resolution and URI generation before changing those algorithms.
- All code comments and docstrings must use International English.
- Do not introduce unnecessary dependencies.
The extension should make UMLet feel like a native Sphinx asset type.
The author should only need to write:
.. uml:: models/applications
:alt: Applications Architecture
:align: center
:width: 100%Everything else must be automatic:
Resolve UXF
↓
Register dependency
↓
Generate SVG
↓
Place SVG in build output
↓
Calculate document-relative URI
↓
Render HTML
The implementation should favour a clean Sphinx-native architecture over shortcuts or post-processing generated HTML.