0047 — Условия в Sphinx

Содержимое страницы

Постановка задачи

Для создания документации используется Sphinx.

Требования:

  • нужно собирать документацию для публичного и служебного пользования;
  • документация для служебного пользования должна содержать дополнительные разделы и части контента, которых не должно быть видно обычным пользователям.

Решения

ifconfig

Самое первое, что приходит на ум — использовать директиву .. ifconfig::.

  1. Добавляем в conf.py код, позволяющий получить значение переменной окружения IS_INTERNAL (её значение задаётся в настройках используемой системы сборки):

    from os import environ
    from typing import Final
    
    extensions = [
        "sphinx.ext.ifconfig",
        # ...
    ]
    
    def setup (app):
        IS_INTERNAL: Final[bool] = bool(environ.get('IS_INTERNAL', False))
        app.add_config_value('IS_INTERNAL', IS_INTERNAL, 'env')
    
  2. Добавляем код в макет:

    .. ifconfig:: IS_INTERNAL
    
       Список пользователей и паролей см. на `этой странице <https://internal.example.com/passwords/>`.
    
    .. ifconfig:: not IS_INTERNAL
    
       Для получения списка пользователей и паролей обратитесь к системному администратору.
    

Неплохо. Даже работает так, как и ожидается.

Теперь давайте расширим постановку задачи:

  • нужно собирать документацию для публичного и служебного пользования;
  • документация для служебного пользования должна содержать дополнительные разделы и части контента, которых не должно быть видно обычным пользователям;
  • из документации для публичного пользования нужно убрать некоторые разделы.

Попробуем убрать раздел описанным выше способом:

.. ifconfig:: IS_INTERNAL

   .. _users_passwords:

   Пароли служебных пользователей
   ==============================

   ...

Ой! А почему на рендере у нас заголовок второго уровня (<h2>) вдруг стал третьим (<h3>)? Может, баг какой-то? Нет, ребята, это нормальное поведение .. ifconfig::. Читаем вместе:

This directive is designed to control only content of document. It could not control sections, labels and so on.

То есть работать это не будет.

only

К счастью, у нас есть ещё теги и директива .. only::.

Однако, для её использования нужна небольшая подготовка:

  1. Правим conf.py:

    from os import environ
    from typing import Final
    
    # ...
    
    def setup (app):
        IS_INTERNAL: Final[bool] = bool(environ.get('IS_INTERNAL', False))
        if IS_INTERNAL:
            app.tags.add('is_internal')
    
  2. Правим вёрстку:

    .. only:: is_internal
    
       .. _users_passwords:
    
       Пароли служебных пользователей
       ==============================
    
  3. Собираем:

    export IS_INTERNAL=true; make dirhtml
    

    Или так (даже проще чем возня с переменными окружения):

    sphinx-build dirhtml --tag is_internal source/ build/
    

Вот сейчас всё рендерится как надо: целый раздел спрятан от анонимусов, зато доступен на внутреннем сервере компании.

Скрытие страниц

А если мы хотим спрятать целую страницу или раздел?

  1. Добавьте в начало скрытых страниц макрос .. orphan::. Теперь Sphinx не будет падать с ошибкой «Страница не включена ни в одно оглавление».

  2. В conf.py добавьте код, управляющий исключениями:

    from os import environ
    from typing import Final
    
    # ...
    
    IS_INTERNAL: Final[bool] = bool(environ.get('IS_INTERNAL', False))
    
    if IS_INTERNAL:
        exclude_patterns = [
            'secret/*',
            'images/internal'
        ]
    

    В публичную документацию не попадут все страницы в каталоге secret/ и страница images/internal.rst.

  3. В оглавлении нужных разделов вместо конкретных имён файлов используйте wildcard:

    .. toctree::
    
       secret/*
       images/*
    

    Документация, собранная таким образом, будет содержать все нужные страницы, кроме перечисленных в параметре exclude_patterns.

    Если важен порядок следования разделов — измените имена файлов таким образом, чтобы они шли в нужном порядке, например:

    01-index.rst
    02-ssh.rst
    03-users.rst
    04-files.rst