'Substitution in a file name with reStructuredText (Sphinx)?

I want to create several files from a single template, which differ only by a variable name. For example :

(file1.rst):

.. |variable| replace:: 1
.. include template.rst

(template.rst) :

Variable |variable|
=====================

Image
-------

.. image:: ./images/|variable|-image.png

where of course I have an image called "./images/1-image.png". The substitution of "|variable|" by "1" works well in the title, but not in the image file name, and at compilation I get :

WARNING: image file not readable: ./images/|variable|-image.png

How can I get reST to make the substitution in the variable name too? (if this changes anything, am using Sphinx).



Solution 1:[1]

You have to create a custom directive in this case as Sphinx doesn't allow you to substitute image paths. You can change Sphinx figure directive as follows and use it instead of the image directive.

from typing import Any, Dict, List, Tuple
from typing import cast

from docutils import nodes
from docutils.nodes import Node, make_id, system_message
from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, html, tables

from sphinx import addnodes
from sphinx.directives import optional_int
from sphinx.domains.math import MathDomain
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import set_source_info

if False:
    # For type annotation
    from sphinx.application import Sphinx


class CustomFigure(images.Figure):
    """The figure directive which applies `:name:` option to the figure node
    instead of the image node.
    """

    def run(self) -> List[Node]:
        name = self.options.pop('name', None)
        path = self.arguments[0]  #path = ./images/variable-image.png
        #replace 'variable' from th.e given value 
        self.argument[0] = path.replace("variable", "string substitution")
        result = super().run()
        if len(result) == 2 or isinstance(result[0], nodes.system_message):
            return result

        assert len(result) == 1
        figure_node = cast(nodes.figure, result[0])
        if name:
            # set ``name`` to figure_node if given
            self.options['name'] = name
            self.add_name(figure_node)

        # copy lineno from image node
        if figure_node.line is None and len(figure_node) == 2:
            caption = cast(nodes.caption, figure_node[1])
            figure_node.line = caption.line

        return [figure_node]


def setup(app: "Sphinx") -> Dict[str, Any]:
    directives.register_directive('figure', Figure)

    return {
        'version': 'builtin',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }

You can add this CustomFigure.py directive in the conf.py of the project and use the customfigure directive across Sphinx project instead of the Image directive. Refer http://www.sphinx-doc.org/en/master/usage/extensions/index.html to add a custom directive to your Sphinx project.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 bad_coder