Использование Jinja2 с автосводкой Sphinx

Я пытаюсь использовать sphinx.ext.autosummary документировать пакет Python. Поскольку «автосводка» требует, чтобы мы перечислили все элементы, которые должны быть включены, я хотел указать их с помощью Jinja2.

Мой conf.py выглядит следующим образом (показаны соответствующие части):

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
    'sphinx.ext.doctest',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon',
    'sphinx_automodapi.automodapi'
]

autodoc_default_options = {
    'imported-members':True
}
add_module_names = False
autosummary_generate = True
numpydoc_show_class_members = False

def rstjinja(app, docname, source):
    """
    Render our pages as a jinja template for fancy templating goodness.
    """
    # Make sure we're outputting HTML
    if app.builder.format != 'html':
        return
    src = source[0]
    rendered = app.builder.templates.render_string(
        src, app.config.html_context
    )
    source[0] = rendered

def setup(app):
    app.connect("source-read", rstjinja)

# in actual usage, `entities` is determined at docs generation time via some code
html_context = {
    'entities' : ["classA", "classB", "classD"]
}

Методы rstjinja() и setup() были заимствованы из здесь . В нем четко указано, что:

Шаблоны Jinja будут обработаны до обработки RST.

Мой .rst файл выглядит следующим образом:

#####
Title
#####

.. currentmodule:: Package.SubModule

.. autosummary::
    :nosignatures:
    :toctree:

    {% for item in entities %}
        {{ item }}
    {% endfor %}

Вывод правильно показывает мне сводную таблицу, состоящую из 3 записей (по одной для каждого из трех указанных мною классов: «classA», «classB», «classD»). В первом столбце отображается имя класса, а во втором столбце - однострочное описание (из его строки документации). Данные во втором столбце ясно показывают, что Sphinx может идентифицировать соответствующие классы и извлекать их строки документации.

Моя проблема в том, что «автосуммари» не создает заглушек для этих классов, и поэтому эти записи в таблице нельзя щелкнуть. На терминале я вижу следующее предупреждение для каждого из классов с отсутствующими заглушками:

ВНИМАНИЕ: autosummary: файл-заглушка «Package.SubModule.classA» не найден. Проверьте настройку autosummary_generate.

Как видно из моего файла conf.py, этот параметр уже True.

Если я изменю (ради изучения) файл .rst на следующее:

#####
Title
#####

.. currentmodule:: Package.SubModule

.. autosummary::
    :nosignatures:
    :toctree:

    {% for item in entities %}
        {{ item }}
    {% endfor %}
    classA

Затем я получаю таблицу, аналогичную предыдущему случаю, но с дополнительной строкой в ​​конце, соответствующей «classA». И что интересно, обе записи для «classA» (первая сгенерирована с помощью Jinja, вторая с помощью явного указания) теперь гиперссылки на заглушку, созданную для «classA».

Почему это так? Почему не создаются заглушки, когда одна и та же информация указывается только через Jinja (хотя sphinx действительно отображает строки документации для них в таблице)?

Как я могу решить эту проблему? Для меня важно иметь возможность предоставить список сущностей, которые нужно документировать с помощью Jinja (поскольку я определяю их с помощью некоторого кода Python в conf.py).

Дополнительная информация: в приведенном выше примере классы можно импортировать через

from Package.SubModule import classA, classB, classD


person Shailesh Appukuttan    schedule 19.12.2019    source источник
comment
Поскольку «автосуммирование» требует, чтобы мы перечислили все элементы, которые должны быть включены, я хотел указать их с помощью Jinja2.. Но вы все равно перечисляете предметы. Теперь вы делаете это в html_context. Что вы получаете, используя Jinja?   -  person mzjn    schedule 20.12.2019
comment
Для этого есть две причины: 1) Список записей может изменяться в процессе разработки этого пакета, и намерение состоит в том, чтобы автоматически обновлять документы, не требуя ручного вмешательства. В приведенном выше упрощенном примере у меня жестко закодированы записи в html_context, но при фактическом использовании они определяются с помощью некоторого кода. 2) У нас есть набор похожих пакетов, и мы хотим иметь общий сценарий, который будет обрабатывать документацию для каждого из этих модулей. Имена записей, подлежащих документированию, могут различаться в этих пакетах, но мы бы хотели избежать ручного редактирования.   -  person Shailesh Appukuttan    schedule 20.12.2019


Ответы (1)


Я нашел обходной путь с помощью расширения sphinx_automodapi.automodapi.

Соответствующие части моего conf.py:

import sphinx_automodapi

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.doctest',
    'sphinx.ext.todo',
    'sphinx.ext.coverage',
    'sphinx.ext.viewcode',
    'sphinx.ext.napoleon',
    'sphinx_automodapi.automodapi'
]

add_module_names = False
autosummary_generate = True
numpydoc_show_class_members = False

def rstjinja(app, docname, source):
    """
    Render our pages as a jinja template for fancy templating goodness.
    """
    # Make sure we're outputting HTML
    if app.builder.format != 'html':
        return
    src = source[0]
    rendered = app.builder.templates.render_string(
        src, app.config.html_context
    )
    source[0] = rendered

def setup(app):
    app.connect("source-read", rstjinja)


html_context = {
    'entities'       : ["classC", "classE"] # NOTE: specify classes NOT to be included/documented; items specified here will be skipped in doc generation
}

ПРИМЕЧАНИЕ: список классов, переданных через html_context, - это классы, которые должны быть исключены из документации. Было бы неплохо, если бы расширение позволяло напрямую указывать требуемые классы. Я открыл тикет (здесь: https://github.com/astropy/sphinx-automodapi/issues/92) за то же самое (в настоящее время не решено).

При фактическом использовании список классов может быть определен динамически. Например:

import inspect, importlib, sciunit
package_import_name = "package_name"

submodule = "{}.submodule_name".format(package_import_name)
module = importlib.import_module(submodule)
exlcude_classes = [x[0] for x in inspect.getmembers(module,
                    lambda member: inspect.isclass(member)
                                    and not(<<specify condition>>))]

html_context = {
    'entities'       : exlcude_classes
}

Пример моего файла .rst:

##########
Submodules
##########

.. automodapi:: package_name.submodule_name
    :nosignatures:
    :no-main-docstr:
    :skip: {{ entities|join(', ') }}
person Shailesh Appukuttan    schedule 10.02.2020