Sphinx autodoc no es lo suficientemente automático

149

Estoy tratando de usar Sphinx para documentar un proyecto de más de 5,000 líneas en Python. Tiene unos 7 módulos base. Hasta donde sé, para usar autodoc necesito escribir un código como este para cada archivo en mi proyecto:

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

Esto es demasiado tedioso porque tengo muchos archivos. Sería mucho más fácil si pudiera especificar que quería que se documentara el paquete 'mods'. Luego, Sphinx podría revisar el paquete de forma recursiva y crear una página para cada submódulo.

¿Hay una característica como esta? Si no, podría escribir un script para hacer todos los archivos .rst, pero eso tomaría mucho tiempo.

Cory Walker
fuente
¿Qué tiene de malo escribir un pequeño script que use "os.walk" y escriba todo esto? Por cierto, tengo un proyecto de más de 40,000 líneas y no tengo claro de qué estás hablando. ¿Cuántos archivos están involucrados? ¿Qué tan difícil puede ser enrutar lsa un archivo y editarlo?
S.Lott
125
Nadie dijo que fuera difícil. OP dijo que era tedioso , lo cual es. Dado que otros sistemas de documentación pueden hacer esto, no es irrazonable.
Gregg Lind
Solo usa pdoc .
K3 --- rnc

Respuestas:

143

Puedes consultar este script que hice. Creo que te puede ayudar.

Este script analiza un árbol de directorios en busca de módulos y paquetes de Python y crea archivos ReST de manera apropiada para crear documentación de código con Sphinx. También crea un índice de módulos.

ACTUALIZAR

Este script ahora es parte de Sphinx 1.1 como apidoc .

Etienne
fuente
¿A dónde se supone que debes enviar los archivos? Intenté enviarlos a la carpeta _build predeterminada de Sphinx, pero la ejecución sphinx-build -b html . ./_buildno los recoge.
Cerin
Debe ponerlos en el source directory(. En su caso). El directorio _build es donde se crearán los archivos HTML. Consulte para obtener más información: sphinx.pocoo.org/tutorial.html#running-the-build
Etienne
1
@Erienne: ¡guión fantástico! justo lo que estaba buscando. Me hubiera gustado que las partidas generadas para las clases individuales (el aspecto regular de la esfinge no es agradable a las clases que se pierden en los módulos más grandes.)
jbenet
1
Incluso la esfinge-apidoc es bastante rudimentaria. Para un paquete con uno o dos módulos, funciona bien, pero tenemos módulos anidados profundamente, y sphinx-apidoc produce una salida bastante inmanejable.
Slacy
44
auto respuesta: agregue .. include:: modules.rsta suindex.rst
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
40

No sé si Sphinx tenía autosummaryextensión en el momento en que se hizo la pregunta original, pero por ahora es bastante posible configurar una generación automática de ese tipo sin usar sphinx-apidocun script similar. A continuación hay configuraciones que funcionan para uno de mis proyectos.

  1. Habilite la autosummaryextensión (así como autodoc) en el conf.pyarchivo y configure su autosummary_generateopción en True. Esto puede ser suficiente si no está utilizando *.rstplantillas personalizadas . De lo contrario, agregue su directorio de plantillas para excluir la lista, o autosummaryintente tratarlos como archivos de entrada (lo que parece ser un error).

    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary']
    autosummary_generate = True
    templates_path = [ '_templates' ]
    exclude_patterns = ['_build', '_templates']
    
  2. Úselo autosummary::en el árbol de TOC en su index.rstarchivo. En este ejemplo, la documentación de los módulos project.module1y project.module2se generará automáticamente y se coloca en _autosummaryel directorio.

    PROJECT
    =======
    
    .. toctree::
    
    .. autosummary::
       :toctree: _autosummary
    
       project.module1
       project.module2
    
  3. Por defecto autosummarygenerará solo resúmenes muy cortos para los módulos y sus funciones. Para cambiar eso, puede colocar un archivo de plantilla personalizado _templates/autosummary/module.rst(que se analizará con Jinja2 ):

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

En conclusión, no hay necesidad de mantener el _autosummarydirectorio bajo control de versiones. Además, puede nombrarlo como desee y colocarlo en cualquier lugar del árbol fuente (aunque ponerlo a continuación _buildno funcionará).

firegurafiku
fuente
44
Esto fue de gran ayuda. En el punto 2, donde tiene "project.module1" y "project.module2", ¿hay alguna forma de generar automáticamente esa lista para cada módulo en un paquete dado? ¿Simplemente poner "proyecto" y hacer que huela "módulo1" y "módulo2"?
Marrón
Bastante sorprendido de que no pueda encontrar una respuesta a esto en ningún lado, ¿alguna vez lo resolvió @Brown?
Alisdair Robertson
3
@AlisdairRobertson No, pero la solución autosummary proporcionada terminó siendo más que adecuada para mis necesidades. Lo único que pensé hacer fue escribir un script para generar el archivo index.rst y autodetectar los nombres de los módulos. Sin embargo, en la práctica, la lista de módulos no cambia tan a menudo, por lo que editar un archivo de vez en cuando no es tan irracional. ¡Estoy seguro de que ya pasé mucho más tiempo buscando una solución de la que se necesita para editar ese único archivo!
Marrón
12

En cada paquete, el __init__.pyarchivo puede tener .. automodule:: package.modulecomponentes para cada parte del paquete.

Entonces puedes .. automodule:: packagey en su mayoría hace lo que quieres.

S.Lott
fuente
¿acabo de poner esa cadena entre comillas triples en init .py?
Cory Walker
55
@Cory Walker: No es una cadena "a". Puede, y debe , colocar cadenas de documentos entre comillas triples en cada archivo. Todo el mundo. Eso incluye los __init__.pyarchivos en sus paquetes. La cadena de documentación puede incluir CUALQUIER directiva de documentación de Sphinx, incluidos los .. automodule::módulos del paquete.
S.Lott
2
autodoces un error tipográfico, debería serlo automodule. pero muchas gracias por la pista!
mariotomo
9

Desde la versión 3.1 de Sphinx (junio de 2020), sphinx.ext.autosummary(¡finalmente!) Tiene recursividad.

Por lo tanto, no es necesario codificar los nombres de los módulos ni confiar en bibliotecas de terceros como Sphinx AutoAPI o Sphinx AutoPackageSummary para su detección automática de paquetes.

Ejemplo de paquete de Python 3.7 para documentar ( ver código en Github y resultado en 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(nota nueva :recursive:opción):

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

Some words.

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

   mypackage

Esto es suficiente para resumir automáticamente cada módulo del paquete, por muy anidado que esté. Para cada módulo, resume cada atributo, función, clase y excepción en ese módulo.

Sin embargo, curiosamente, las sphinx.ext.autosummaryplantillas predeterminadas no generan páginas de documentación separadas para cada atributo, función, clase y excepción, y se vinculan a ellas desde las tablas de resumen. Es posible extender las plantillas para hacer esto, como se muestra a continuación, pero no puedo entender por qué este no es el comportamiento predeterminado, seguramente eso es lo que la mayoría de la gente querría ... Lo he planteado como una solicitud de función .

Tuve que copiar las plantillas predeterminadas localmente y luego agregarlas:

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

El gancho en custom-module-template.rstestá index.rstarriba, usando la :template:opción. (Elimine esa línea para ver qué sucede usando las plantillas predeterminadas de paquetes de sitio).

custom-module-template.rst (líneas adicionales anotadas a la derecha):

{{ 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 (líneas adicionales anotadas a la derecha):

{{ 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
fuente