Search code examples
pythonpython-sphinxdocutils

How to properly define a new admonition in Sphinx?


With Sphinx it is easy to write extensions, but is it very hard to extend Sphinx in the proper way.

I would like to define a custom Admonition so I have:

class exercise(nodes.Admonition, nodes.Element):
    pass

class ExerciseDirective(BaseAdmonition):
    node_class = exercise

def visit_exercise(self, node, name=''):
    self.visit_admonition(node, 'exercise')

def depart_exercise(self, node=None):
    self.depart_admonition(node)

def setup(app):
    app.add_directive('exercise', ExerciseDirective)

    app.add_node(exercise,
        html=(visit_exercise, depart_exercise)
    )

Unfortunately this is not enough because my exercise have no title. I noticed the translations for admonitions is on docutils/languages which can be later read with language.labels. As my label exercise is not defined, I have no text.

A correct way would be to add a new label for the defined language. I do not know how....

One very ugly solution would be to hack the hardcoded admonitionlabels:

sphinx.locale.admonitionlabels['exercise'] = 'Exercise'

Solution

  • You could also manually do what the base visit_admonition does and add the title node yourself:

    def visit_exercise(self, node, name=''):
        self.visit_admonition(node, 'exercise')
        if not isinstance(node[0], nodes.title):
            node.insert(0, nodes.title('exercise', _('Exercise')))
    

    I used _('Exercise') as an example, but you could also put just 'Exercise' if you have no intentions of localization.