Настроить шаблоны для `sphinx-apidoc`

Недавно я попробовал использовать sphinx-apidoc из Sphinx, чтобы помочь сгенерировать специфический для Sphinx reStructuredText из API проекта Python.

Однако я получаю результат:

Default look of <code>sphinx-api</code> result

Кто-нибудь знает, могу ли я настроить шаблон sphinx-api для его вывода? В частности, я хотел бы:

  • Избавьтесь от всех заголовков "Submodules", "Subpackages" и "Module contents" и
  • Получают ли результаты из docstring в моих файлах __init__.py непосредственно под пакетами, поэтому, если я нажму имя пакета, первое, что я вижу, это документация пакета. На данный момент эта документация помещается под слегка странным заголовком "Module contents" в самом конце каждого раздела пакета.

Заголовки "Submodules" и "Subpackages" являются избыточными, я думаю, поскольку обычные заголовки для пакетов/модулей "xxx.yyy package" и "xxx.yyy.zzz module".

Структура, которая мне нужна для приведенного выше небольшого примера, -

  • orexplore.components package
    • orexplore.components.mbg120
  • пакет orexplore.simulators
    • пакет orexplore.simulators.test
      • Модуль
      • orexplore.simulators.test.mbg120
      Модуль
    • orexplore.simulators.mbg120

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

Или, может быть, даже просто

  • orexplore.components
    • orexplore.components.mbg120
  • orexplore.simulators
    • orexplore.simulators.test
      • orexplore.simulators.test.mbg120
  • orexplore.simulators.mbg120

если есть какой-то способ визуально различать пакеты/модули (цвет? emblem?) вместо довольно многословного "пакета" и "модуля".

Ответ 1

sphinx-apidoc script использует модуль apidoc.py. Я не могу предоставить подробные инструкции, но чтобы удалить заголовки или иным образом настроить выход, вам придется написать собственную версию этого модуля. Нет другого "шаблона".

Обратите внимание, что если структура API и модуля стабильна, нет необходимости повторно запускать sphinx-apidoc. Вы можете постовать обработанные первые файлы по своему вкусу один раз, а затем поместить их под контроль версий. См. Также fooobar.com/questions/544914/....

Ответ 2

Я реализовал better-apidoc, исправленную версию скрипта sphinx-apidoc, которая добавляет полную поддержку шаблонов.

Он добавляет опцию -t/--template, позволяющую передать каталог шаблона, который должен содержать файлы шаблонов package.rst и module.rst. Видеть package.rst а также module.rst для примера. Это рендеринг, например, http://qnet.readthedocs.io/en/latest/API/qnet.algebra.operator_algebra.html.

Ответ 3

FWIW, здесь полный взлом script, чтобы внести нужные изменения, которые также были моими желаемыми изменениями, в файле "filename.rst.new" рядом с каждым "filename.rst":

#!/usr/bin/env python

'''
Rearrange content in sphinx-apidoc generated .rst files.

* Move "Module Contents" section to the top.
* Remove headers for "Module Contents", "Submodules" and "Subpackages",
  including their underlines and the following blank line.
'''


import argparse
import glob
import os


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def argument_parser():
    '''
    Define command line arguments.
    '''

    parser = argparse.ArgumentParser(
        description='''
        Rearrange content in sphinx-apidoc generated .rst files.
        '''
        )

    parser.add_argument(
        '-v', '--verbose',
        dest='verbose',
        default=False,
        action='store_true',
        help="""
            show more output.
            """
        )

    parser.add_argument(
        'input_file',
        metavar="INPUT_FILE",
        nargs='+',
        help="""
            file.
            """
        )

    return parser


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
def main():
    '''
    Main program entry point.
    '''

    global args
    parser = argument_parser()
    args = parser.parse_args()

    filenames = [glob.glob(x) for x in args.input_file]
    if len(filenames) > 0:
        filenames = reduce(lambda x, y: x + y, filenames)

    for filename in set(filenames):

        # line_num was going to be for some consistency checks, never
        # implemented but left in place.
        found = {
            'Subpackages': {'contents': False, 'line_num': None},
            'Submodules': {'contents': False, 'line_num': None},
            'Module contents': {'contents': True, 'line_num': None},
            }

        in_module_contents = False
        line_num = 0
        reordered = []
        module_contents = []

        new_filename = '.'.join([filename, 'new'])

        with open(filename, 'r') as fptr:

            for line in fptr:
                line = line.rstrip()
                discard = False

                line_num += 1

                if (
                        in_module_contents
                        and len(line) > 0
                        and line[0] not in ['.', '-', ' ']
                        ):  # pylint: disable=bad-continuation
                    in_module_contents = False

                for sought in found:

                    if line.find(sought) == 0:

                        found[sought]['line_num'] = line_num
                        if found[sought]['contents']:
                            in_module_contents = True

                        discard = True
                        # discard the underlines and a blank line too
                        _ = fptr.next()
                        _ = fptr.next()

                if in_module_contents and not discard:
                    module_contents.append(line)

                elif not discard:
                    reordered.append(line)

                # print '{:<6}|{}'.format(len(line), line)

        with open(new_filename, 'w') as fptr:
            fptr.write('\n'.join(reordered[:3]))
            fptr.write('\n')
            if module_contents:
                fptr.write('\n'.join(module_contents))
                fptr.write('\n')
                if len(module_contents[-1]) > 0:
                    fptr.write('\n')
            if reordered[3:]:
                fptr.write('\n'.join(reordered[3:]))
                fptr.write('\n')


if __name__ == "__main__":
    main()