Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sphinx: how to cross-reference a target generated by a custom directive

I'm having trouble cross-referencing a section generated by a custom directive.

Here is the directive:

from docutils import nodes
from docutils.parsers import rst


class TestDirective(rst.Directive):

    has_content = False
    required_arguments = 1
    option_spec = {}

    def run(self):
        my_arg = self.arguments[0]

        target_node = nodes.target('', '', refid=nodes.make_id(my_arg))
        section = nodes.section(
            '',
            nodes.title(text=my_arg),
            ids=[nodes.make_id(my_arg)],
            names=[nodes.fully_normalize_name(my_arg)])

        return [target_node, section]


def setup(app):
   app.add_directive('mytest', TestDirective)

And here is how it's used:

=============
Test document
=============

.. mytest:: section1

Section 1 content.


.. _section2:

section2
========

Section 2 content.

Now, the following works only for section2:

Here are links to :ref:`section1` and :ref:`section2`.

The link is only generated properly for section2 and I get the following error:

test.rst:19: WARNING: undefined label: section1 (if the link has no caption the
label must precede a section header)

How can I make this work?

like image 396
abey Avatar asked Apr 22 '20 15:04

abey


1 Answers

You seem to be confusing the function of a reference label versus a directive.

To get the terminology clear, in your markup:

  • The part which worked has a reference label (.. _section2:) directly before a section title ("section2" with adornment underline). That provides a target which can be linked to via an interpreted text role (:ref:`section2`). That's regular Sphinx boilerplate.
  • The part which doesn't work has your custom directive (.. mytest::) with its single required argument (section1) and no content block (there is no indented text following the directive).
  • The line "Section 1 content." is a stand-alone paragraph.

Maybe you wanted a custom interpreted text role to provide special functionality, or a roles.XRefRole subclass, or the autosectionlabel extension?


EDIT

The :ref: role is for arbitrary locations ("labels", whatever that means) but you can also cross-reference to custom objects via :any: once it is registered:

def setup(app):
    app.add_directive('mytest', TestDirective)

    app.add_object_type(
        directivename='mytest',
        rolename='banana',
    )

then ReST content:

See :any:`section1` which is a TestDirective.

Or also works via the rolename :banana:`section1`.

Many would agree that all this functionality is poorly documented.

like image 124
Apropos Avatar answered Oct 22 '22 08:10

Apropos