0

How can I have sphinx add a link that varies based on the build destination, such as html or pdf?

I'd like to add an external link to my reStructuredText file such that it points to:

  1. example.com/a when sphinx is building an html file and
  2. example.com/b when sphinx is building a pdf file

How can I update my source .rst files with variable external links with a conditional based on the build destination format?

Michael Altfield
  • 2,083
  • 23
  • 39
  • 1
    Have a look at [`ifconfig`](https://www.sphinx-doc.org/en/master/usage/extensions/ifconfig.html). Although this does not tie directly to `make pdf/html`, you could include a configuration argument to accomplish what you need. – Steve Piercy Jul 26 '20 at 12:56
  • Thanks, but it looks like sphinx has some limitations on declaring targets conditionally using `ifconfig` per https://stackoverflow.com/q/63129590/1174102 A simple extension might be more appropriate – Michael Altfield Jul 28 '20 at 09:18

1 Answers1

0

I was successfully able to generate targets conditionally based on the buildername by using a simple python sphinx extension.

Preface

First, it's worth noting that the buildername (html for html or rinoh for pdf) is not made available to conf.py or extensions as the builder is setup after the extensions when the Sphinx class is initialized.

To overcome this, I choose to parse sys.argv, but this might not work if you build sphinx from python rather than calling sphinx-build from bash as I do.

A more robust solution might be to use a tag.

conf.py

First, add the following block somewhere the sphinx conf.py file, which will make an environment variable named 'buildername' available from extensions:

def setup(app):

   argv = ' '.join(sys.argv)
   if '-b html' in argv:
      app.add_config_value('buildername', 'html', 'env')
   else:
      app.add_config_value('buildername', 'not-html', 'env')

Then update your extensions list to include conditionaltarget:

sys.path.insert(0, os.path.abspath('_extensions'))
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinx.ext.viewcode',
    'conditionaltarget',
]

_extensions/conditionaltarget.py

Create the example "Conditional Target" extension at docs/_extensions/conditionaltarget.py with the following contents:

from docutils import nodes
from docutils.parsers.rst import Directive

from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective

class ConditionalTarget(SphinxDirective):

    def run(self):
        target_node = nodes.target( '', '' )
        target_node['names'] = ['targetname1']

      if self.env.config.buildername == 'html':
            target_node['refuri'] = 'https://example.com/a'
        else:
            target_node['refuri'] = 'https://example.com/b'

        return [target_node]

def setup(app):
    app.add_directive("conditionaltarget", ConditionalTarget)

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

index.rst

Finally, adding the following to the source .rst file:

test link to targetname1_

.. conditionaltarget::

Now the link should point to example.com/a when built using sphinx-build -b html . _build/html/ and example.com/b when built using with any other command that doesn't include -b html (eg sphinx-build -b rinoh . _build/html/)

Of course, this is just a very crude proof-of-concept, and a better extension would take inputs from its directive by parsing the newline-delimited self.content list for target names and URIs.

Michael Altfield
  • 2,083
  • 23
  • 39