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.


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 .


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 ).


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.


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 %}


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.