Conditional toctree in Sphinx

conditional-statements, docutils, python-sphinx, restructuredtext, toctree

Solution

My previous answer fails if you have hierarchies of table of contents so I wrote a simple `toctree-filt` directive that is able to filter entries based on a prefix to the entry. For example, given a `toctree-filt` directive like

.. toctree-filt::
   :maxdepth: 1

   user-manual
   :internal:supervisor-api
   :draft:new-feature
   :erik:erik-maths
   api

and setting the exclusion list to `['draft','erik']` will result in an effective toctree that looks like

.. toctree-filt::
   :maxdepth: 1

   user-manual
   supervisor-api
   api

Add the following lines to your `conf.py`:

sys.path.append(os.path.abspath('../sphinx-ext/'))
extensions = ['toctree_filter']
toc_filter_exclude = ['draft','erik']

Put the following code in `/sphinx_ext` next to your `/source` directory:

import re
from sphinx.directives.other import TocTree


def setup(app):
    app.add_config_value('toc_filter_exclude', [], 'html')
    app.add_directive('toctree-filt', TocTreeFilt)
    return {'version': '1.0.0'}

class TocTreeFilt(TocTree):
    """
    Directive to notify Sphinx about the hierarchical structure of the docs,
    and to include a table-of-contents like tree in the current document. This
    version filters the entries based on a list of prefixes. We simply filter
    the content of the directive and call the super's version of run. The
    list of exclusions is stored in the **toc_filter_exclusion** list. Any
    table of content entry prefixed by one of these strings will be excluded.
    If `toc_filter_exclusion=['secret','draft']` then all toc entries of the
    form `:secret:ultra-api` or `:draft:new-features` will be excuded from
    the final table of contents. Entries without a prefix are always included.
    """
    hasPat = re.compile('^\s*:(.+):(.+)$')

    # Remove any entries in the content that we dont want and strip
    # out any filter prefixes that we want but obviously don't want the
    # prefix to mess up the file name.
    def filter_entries(self, entries):
        excl = self.state.document.settings.env.config.toc_filter_exclude
        filtered = []
        for e in entries:
            m = self.hasPat.match(e)
            if m != None:
                if not m.groups()[0] in excl:
                    filtered.append(m.groups()[1])
            else:
                filtered.append(e)
        return filtered

    def run(self):
        # Remove all TOC entries that should not be on display
        self.content = self.filter_entries(self.content)
        return super().run()

Now just change your existing `toctree` directives to `toctree-filt` and you are good to roll. Note that Sphinx will post errors because it will find files that are not included in the document. Not sure how to fix that.

Problem

I want to do multiple versions of a documentation, which differ in the sections that are included. To achieve this I would usually use either the only directive or the ifconfig extension. However, I cannot use any of those in combination with the toctree directive. What I basically want is something like this: ``` .. toctree:: :maxdepth: 2 intro strings datatypes numeric .. only:: university complex ``` Is there a way to do that?

Original source

Related problems