O Sphinx autodoc não é automático o suficiente

149

Estou tentando usar o Sphinx para documentar um projeto de mais de 5.000 linhas em Python. Possui cerca de 7 módulos básicos. Tanto quanto eu sei, para usar o autodoc eu preciso escrever um código como este para cada arquivo no meu projeto:

.. automodule:: mods.set.tests
    :members:
    :show-inheritance:

Isso é muito entediante, porque eu tenho muitos arquivos. Seria muito mais fácil se eu pudesse especificar que queria que o pacote 'mods' fosse documentado. O Sphinx poderia recursivamente percorrer o pacote e criar uma página para cada submódulo.

Existe um recurso como este? Caso contrário, eu poderia escrever um script para criar todos os arquivos .rst, mas isso levaria muito tempo.

Cory Walker
fonte
O que há de errado em escrever um pequeno script que use "os.walk" e grave tudo isso? BTW, eu tenho um projeto de mais de 40.000 linhas e não sei ao certo o que você está falando. Quantos arquivos estão envolvidos? Quão difícil pode ser rotear lspara um arquivo e editá-lo?
S.Lott
125
Ninguém disse que era difícil. OP disse que era tedioso , o que é. Dado que outros sistemas de documentos podem fazer isso, não é irracional.
Gregg Lind
Basta usar o pdoc .
K3 --- rnc 15/04

Respostas:

143

Você pode verificar este script que eu fiz. Eu acho que isso pode ajudá-lo.

Esse script analisa uma árvore de diretórios procurando módulos e pacotes python e cria arquivos ReST adequadamente para criar documentação de código com o Sphinx. Ele também cria um índice de módulos.

ATUALIZAR

Este script agora faz parte do Sphinx 1.1 como apidoc .

Etienne
fonte
Para onde você deve enviar os arquivos? Tentei imprimi-los na pasta _build padrão do Sphinx, mas a execução sphinx-build -b html . ./_buildnão os captura .
Cerin
Você deve colocá-los no source directory(. No seu caso). O diretório _build é onde os arquivos HTML serão criados. Verifique para obter mais informações: sphinx.pocoo.org/tutorial.html#running-the-build
Etienne
1
@ Erienne: roteiro fantástico! É mesmo o que eu procurava. Gostaria que cabeçalhos gerados para aulas individuais (o olhar esfinge regular não é bom para as classes eles se perdem em módulos maiores.)
jbenet
1
Até o sphinx-apidoc é bastante rudimentar. Para um pacote com um ou dois módulos, ele funciona bem, mas temos módulos aninhados profundamente, e o sphinx-apidoc produz uma saída bastante incontrolável.
slacy
4
resposta automática: adicione .. include:: modules.rstao seuindex.rst
Ciro Santilli #: 04413
40

Não sei se o Sphinx teve autosummaryextensão no momento em que a pergunta original foi feita, mas, por enquanto, é bem possível configurar a geração automática desse tipo sem o uso de sphinx-apidocum script semelhante. Abaixo, há configurações que funcionam para um dos meus projetos.

  1. Ative a autosummaryextensão (e também autodoc) no conf.pyarquivo e defina sua autosummary_generateopção como True. Isso pode ser suficiente se você não estiver usando *.rstmodelos personalizados . Caso contrário, adicione o diretório de modelos para excluir a lista ou autosummarytentará tratá-los como arquivos de entrada (o que parece ser um bug).

    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary']
    autosummary_generate = True
    templates_path = [ '_templates' ]
    exclude_patterns = ['_build', '_templates']
  2. Use autosummary::na árvore do sumário em seu index.rstarquivo. Neste exemplo documentação para módulos project.module1e project.module2será gerada automaticamente e colocada no _autosummarydiretório.

    PROJECT
    =======
    
    .. toctree::
    
    .. autosummary::
       :toctree: _autosummary
    
       project.module1
       project.module2
  3. Por padrão, autosummaryirá gerar apenas resumos muito curtos para os módulos e suas funções. Para alterar o que você pode colocar em um arquivo de modelo personalizado _templates/autosummary/module.rst(que será analisado com o Jinja2 ):

    {{ fullname }}
    {{ underline }}
    
    .. automodule:: {{ fullname }}
        :members:

Em conclusão, não há necessidade de manter o _autosummarydiretório sob controle de versão. Além disso, você pode dar o nome que desejar e colocá-lo em qualquer lugar da árvore de origem (no entanto _build, não será possível colocá-lo abaixo ).

firegurafiku
fonte
4
Esta foi uma grande ajuda. No ponto 2, onde você tem "project.module1" e "project.module2", existe uma maneira de gerar automaticamente essa lista para cada módulo em um determinado pacote? Para colocar "project" e farejar "module1" e "module2"?
Brown
Muito surpreso por não encontrar uma resposta para isso em nenhum lugar, você já trabalhou nisso @Brown?
Alisdair Robertson
3
@AlisdairRobertson Não, mas a solução automática fornecida acabou sendo mais do que adequada para minhas necessidades. A única outra coisa que pensei em fazer foi escrever um script para gerar o arquivo index.rst e detectar automaticamente os nomes dos módulos. No entanto, na prática, a lista de módulos não muda com tanta frequência; portanto, apenas editar um arquivo de vez em quando não é irracional. Tenho certeza de que já gastei muito mais tempo procurando uma solução do que a necessária para editar esse arquivo!
Brown
12

Em cada pacote, o __init__.pyarquivo pode ter .. automodule:: package.modulecomponentes para cada parte do pacote.

Então você pode .. automodule:: packagee principalmente faz o que você quer.

S.Lott
fonte
apenas coloco essa string entre aspas triplas no init .py?
Cory Walker
5
@ Walker Walker: Não é "uma" string. Você pode - e deve - estar colocando documentos com aspas triplas em cada arquivo. Todos. Isso inclui os __init__.pyarquivos em seus pacotes. A documentação pode incluir QUALQUER diretiva de documentação do Sphinx, inclusive .. automodule::para módulos dentro do pacote.
S.Lott
2
autodocé um erro de digitação, deveria ser automodule. mas muito obrigado pela dica!
Mariotomo #
9

Da versão 3.1 do Sphinx (junho de 2020), sphinx.ext.autosummary(finalmente!), A recursão.

Portanto, não é necessário codificar os nomes dos módulos ou confiar em bibliotecas de terceiros como Sphinx AutoAPI ou Sphinx AutoPackageSummary para a detecção automática de pacotes.

Exemplo de pacote Python 3.7 a ser documentado ( consulte o código no Github e o resultado no ReadTheDocs ):

mytoolbox
|-- mypackage
|   |-- __init__.py
|   |-- foo.py
|   |-- mysubpackage
|       |-- __init__.py
|       |-- bar.py
|-- doc
|   |-- source
|       |--index.rst
|       |--conf.py
|       |-- _templates
|           |-- custom-module-template.rst
|           |-- custom-class-template.rst

conf.py:

import os
import sys
sys.path.insert(0, os.path.abspath('../..'))  # Source code dir relative to this file

extensions = [
    'sphinx.ext.autodoc',  # Core library for html generation from docstrings
    'sphinx.ext.autosummary',  # Create neat summary tables
]
autosummary_generate = True  # Turn on sphinx.ext.autosummary

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

index.rst(observe nova :recursive:opção):

Welcome to My Toolbox
=====================

Some words.

.. autosummary::
   :toctree: _autosummary
   :template: custom-module-template.rst
   :recursive:

   mypackage

Isso é suficiente para resumir automaticamente todos os módulos do pacote, mesmo que profundamente aninhados. Para cada módulo, ele resume cada atributo, função, classe e exceção nesse módulo.

Estranhamente, porém, os sphinx.ext.autosummarymodelos padrão não geram páginas de documentação separadas para cada atributo, função, classe e exceção, e vinculam a elas nas tabelas de resumo. É possível estender os modelos para fazer isso, como mostrado abaixo, mas não consigo entender por que esse não é o comportamento padrão - certamente é isso que a maioria das pessoas deseja ..? Eu o levantei como uma solicitação de recurso .

Eu tive que copiar os modelos padrão localmente e adicioná-los:

  • Copiar site-packages/sphinx/ext/autosummary/templates/autosummary/module.rstparamytoolbox/doc/source/_templates/custom-module-template.rst
  • Copiar site-packages/sphinx/ext/autosummary/templates/autosummary/class.rstparamytoolbox/doc/source/_templates/custom-class-template.rst

O gancho custom-module-template.rstestá index.rstacima, usando a :template:opção (Exclua essa linha para ver o que acontece usando os modelos de pacotes de sites padrão.)

custom-module-template.rst (linhas adicionais anotadas à direita):

{{ fullname | escape | underline}}

.. automodule:: {{ fullname }}
  
   {% block attributes %}
   {% if attributes %}
   .. rubric:: Module Attributes

   .. autosummary::
      :toctree:                                          <-- add this line
   {% for item in attributes %}
      {{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block functions %}
   {% if functions %}
   .. rubric:: {{ _('Functions') }}

   .. autosummary::
      :toctree:                                          <-- add this line
   {% for item in functions %}
      {{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block classes %}
   {% if classes %}
   .. rubric:: {{ _('Classes') }}

   .. autosummary::
      :toctree:                                          <-- add this line
      :template: custom-class-template.rst               <-- add this line
   {% for item in classes %}
      {{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block exceptions %}
   {% if exceptions %}
   .. rubric:: {{ _('Exceptions') }}

   .. autosummary::
      :toctree:                                          <-- add this line
   {% for item in exceptions %}
      {{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

{% block modules %}
{% if modules %}
.. rubric:: Modules

.. autosummary::
   :toctree:
   :template: custom-module-template.rst                 <-- add this line
   :recursive:
{% for item in modules %}
   {{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

custom-class-template.rst (linhas adicionais anotadas à direita):

{{ fullname | escape | underline}}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}
   :members:                                    <-- add at least this line
   :show-inheritance:                           <-- plus I want to show inheritance...
   :inherited-members:                          <-- ...and inherited members too

   {% block methods %}
   .. automethod:: __init__

   {% if methods %}
   .. rubric:: {{ _('Methods') }}

   .. autosummary::
   {% for item in methods %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block attributes %}
   {% if attributes %}
   .. rubric:: {{ _('Attributes') }}

   .. autosummary::
   {% for item in attributes %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}
James Leedham
fonte
1

Talvez o que você está procurando seja o Epydoc e esta extensão do Sphinx .

Edward Dale
fonte