Записать индекс модуля в файл с pydoc без сервера - PullRequest
0 голосов
/ 10 октября 2019

В настоящее время я пишу документацию для своей библиотеки Python, используя следующую команду:

python -m pydoc -w "\\myserver.com\my_library"

Это прекрасно работает, и я нахожу в my_library файлах HTML документацию, полученную из строк документации класса / метода / функции. Это даже документирует файлы Python, найденные в подпапках.

Теперь я хотел бы создать и сохранить index , который дает доступ ко всем этим файлам.

Документация pydocговорит, что это возможно, если вы запустите сервер:

pydoc -b запустит сервер и дополнительно откроет веб-браузер для индексной страницы модуля . Каждая обслуживаемая страница имеет вверху панель навигации, где вы можете получить справку по отдельному элементу, выполнить поиск по всем модулям с ключевым словом в строке синопсиса и перейти на страницы Индекс модуля , Темы и ключевые слова.

Однако я собираюсь написать страницу индекса модулей, включая относительные ссылки на документацию с одним файлом, без серверного решения. Затем я могу сохранить Индекс + отдельные файлы [по одному для каждого файла py] в каталоге, доступном для пользователей.

Возможно ли это, или есть лучший способ решения этой проблемы?

Я посмотрел на Сфинкса, но это кажется излишним для моих требований.

1 Ответ

1 голос
/ 16 октября 2019

Этого можно достичь, запустив небольшой скрипт:

  • , который импортирует подлежащие документированию модули,

  • записывает документацию вhtml файлы, а затем

  • записывает выходные данные внутренней функции, которая динамически генерирует index.html, в файл index.html.

Itэто не очень хорошее решение, потому что оно опирается на внутренние компоненты модуля pydoc, но достаточно компактно:

import pydoc
import importlib

module_list = ['sys']
for m in module_list:
    importlib.import_module(m)
    pydoc.writedoc(m)

#the monkey patching optionally goes here

with open('index.html','w') as out_file:
    out_file.write(pydoc._url_handler('index.html'))

Есть еще один недостаток в том, что он также создает ссылки на все встроенныемодули и т. д., для которых мы не (и, я думаю, не хотели) создавать документацию для.

Можем ли мы скопировать функцию из pydoc.py, которая создает файл index.html, и изменить ее только длядобавить ссылки на наши нужные модули? К сожалению, это не так просто, потому что функция использует некоторую нелокальную область видимости для достижения некоторой своей логики.

Следующим лучшим решением будет обезьяна-исправление метода index_html(), который генерирует эту страницу дляперечислите только наши модули.

К сожалению pydoc._url_handler использует локальную функцию для реализации этого, а не метод класса. Таким образом, это становится немного сложнее.

Существует решение для обезьяньего патча, но это немного хак:

перед вызовом _url_handler нам нужно:

  • определяет исправленную версию, которая генерирует ссылки только для элементов в нашем module_list (обходной путь через __placeholder__ используется, потому что наш module_list не определен в области, в которой выполняется функция, поэтому нам нужносделать что-то, что соответствует жесткому кодированию в функции, вроде того.)

  • исправьте источник модуля pydoc, чтобы использовать эту локальную функцию вместо первоначально определенного

Это достигается следующим образом:

import inspect, ast

__placeholder__ = None

#our patched version, needs to have same name and signature as original
def html_index():
    """Module Index page."""
    names= __placeholder__

    def bltinlink(name):
        return '<a href="%s.html">%s</a>' % (name, name)

    heading = html.heading(
        '<big><big><strong>Index of Modules</strong></big></big>',
        '#ffffff', '#7799ee')
    contents = html.multicolumn(names, bltinlink)
    contents = [heading, '<p>' + html.bigsection(
        'Module List', '#ffffff', '#ee77aa', contents)]

    contents.append(
        '<p align=right><font color="#909090" face="helvetica,'
        'arial"><strong>pydoc</strong> by Ka-Ping Yee'
        '&lt;ping@lfw.org&gt;</font>')
    return 'Index of Modules', ''.join(contents)

#get source and replace __placeholder__ with our module_list
s=inspect.getsource(html_index).replace('__placeholder__', str(module_list))

#create abstract syntax tree, and store the actual function definition in l_index
l_index=ast.parse(s).body[0]
#ast.dump(l_index) #check if you want

#now obtain source from unpatched pydoc, generate ast patch it and recompile:
s= inspect.getsource(pydoc)
m = ast.parse(s)

def find_named_el_ind(body, name):
    '''find named element in ast body'''
    found=False
    for i,e in enumerate(body):
        if hasattr(e,'name') and e.name == name:
            found=True
            break
    if not found: raise ValueError('not found!')
    return i

#find and replace html_index with our patched html_index
i_url_handler = find_named_el_ind(m.body, '_url_handler')
i_html_index = find_named_el_ind(m.body[i_url_handler].body, 'html_index')
m.body[i_url_handler].body[i_html_index] = l_index

#compile and replace module in memory
co = compile(m, '<string>', 'exec')
exec(co, pydoc.__dict__)

#ast.dump(m.body[i_url_handler]) #check ast if you will
...