Sphinx autodoc non è abbastanza automatico


149

Sto cercando di usare Sphinx per documentare un progetto di oltre 5.000 righe in Python. Ha circa 7 moduli di base. Per quanto ne so, per usare l'autodoc ho bisogno di scrivere codice come questo per ogni file nel mio progetto:

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

Questo è troppo noioso perché ho molti file. Sarebbe molto più semplice se potessi semplicemente specificare che volevo documentare il pacchetto 'mods'. Sphinx potrebbe quindi ricorsivamente ricorrere al pacchetto e creare una pagina per ciascun sottomodulo.

C'è una caratteristica come questa? Altrimenti potrei scrivere uno script per creare tutti i file .rst, ma ciò richiederebbe molto tempo.


Cosa c'è di sbagliato nello scrivere un piccolo script che usa "os.walk" e scrive tutto questo? A proposito, ho un progetto di oltre 40.000 linee e non sono chiaro di cosa tu stia parlando. Quanti file sono coinvolti? Quanto può essere difficile instradare lsverso un file e modificarlo?
S.Lott

125
Nessuno ha detto che era difficile. OP ha detto che era noioso , e lo è. Dato che altri sistemi doc possono farlo, non è irragionevole.
Gregg Lind,

Usa solo pdoc .
K3 --- rnc,

Risposte:


143

Puoi controllare questo script che ho creato. Penso che possa aiutarti.

Questo script analizza un albero di directory alla ricerca di moduli e pacchetti python e crea i file ReST in modo appropriato per creare la documentazione del codice con Sphinx. Crea anche un indice dei moduli.

AGGIORNARE

Questo script fa ora parte di Sphinx 1.1 come apidoc .


Dove dovresti produrre i file? Ho provato a inviarli alla cartella _build predefinita di Sphinx, ma l'esecuzione sphinx-build -b html . ./_buildnon li raccoglie.
Cerin,

Dovresti metterli nel source directory(. Nel tuo caso). La directory _build è dove verranno creati i file HTML. Per maggiori informazioni: sphinx.pocoo.org/tutorial.html#running-the-build
Etienne

1
@Erienne: sceneggiatura fantastica! Proprio quello che stavo cercando. Vorrei che generasse intestazioni per singole classi (l'aspetto normale della sfinge non è carino per le classi. Si perdono in moduli più grandi)
jbenet

1
Anche la sfinge-apidoc è piuttosto rudimentale. Per un pacchetto con uno o due moduli, funziona bene, ma i moduli sono nidificati in profondità e sphinx-apidoc produce un output piuttosto ingestibile.
slacy,

4
risposta automatica: aggiungi .. include:: modules.rstal tuoindex.rst
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

40

Non so se Sphinx avesse avuto l' autosummaryestensione al momento in cui era stata posta la domanda originale, ma per ora è del tutto possibile impostare una generazione automatica di quel tipo senza usare sphinx-apidocscript simili. Di seguito ci sono le impostazioni che funzionano per uno dei miei progetti.

  1. Abilita l' autosummaryestensione (oltre che autodoc) nel conf.pyfile e imposta la sua autosummary_generateopzione su True. Questo potrebbe essere sufficiente se non stai utilizzando *.rstmodelli personalizzati . Altrimenti aggiungi la tua directory dei modelli per escludere l'elenco, o autosummaryproverà a trattarli come file di input (che sembra essere un bug).

    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary']
    autosummary_generate = True
    templates_path = [ '_templates' ]
    exclude_patterns = ['_build', '_templates']
  2. Utilizzare autosummary::nell'albero TOC nel index.rstfile. In questo esempio la documentazione per i moduli project.module1e project.module2verrà generata automaticamente e inserita nella _autosummarydirectory.

    PROJECT
    =======
    
    .. toctree::
    
    .. autosummary::
       :toctree: _autosummary
    
       project.module1
       project.module2
  3. Per impostazione predefinita autosummarygenererà solo riepiloghi molto brevi per i moduli e le loro funzioni. Per cambiare è possibile inserire un file modello personalizzato _templates/autosummary/module.rst(che verrà analizzato con Jinja2 ):

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

In conclusione, non è necessario mantenere la _autosummarydirectory sotto il controllo della versione. Inoltre, puoi nominarlo come vuoi e posizionarlo ovunque nell'albero dei sorgenti (metterlo sotto _buildnon funzionerà, però).


4
Questo è stato di grande aiuto. Al punto 2, dove hai "project.module1" e "project.module2", c'è un modo per generare automagicamente quell'elenco per ogni modulo in un determinato pacchetto? Per mettere semplicemente "project" e farlo annusare "module1" e "module2"?
Brown,

Abbastanza sorpreso di non riuscire a trovare una risposta a questo da nessuna parte, dod hai mai capito @Brown?
Alisdair Robertson,

3
@AlisdairRobertson No, ma la soluzione di sintesi automatica fornita è risultata più che adeguata alle mie esigenze. L'unica altra cosa che ho pensato di fare è stata scrivere uno script per generare il file index.rst e rilevare automaticamente i nomi dei moduli. Tuttavia, in pratica, l'elenco dei moduli non cambia così spesso, quindi modificare un file ogni tanto non è così irragionevole. Sono sicuro di aver già trascorso molto più tempo a cercare una soluzione di quella necessaria per modificare quel solo file!
Brown,

12

In ogni pacchetto, il __init__.pyfile può avere .. automodule:: package.modulecomponenti per ogni parte del pacchetto.

Quindi puoi .. automodule:: packagee fa principalmente quello che vuoi.


ho appena messo quella stringa tra virgolette triple in init .py?
Cory Walker,

5
@Cory Walker: non è una "stringa". Puoi - e dovresti - inserire docstringhe a tre virgolette in ogni singolo file. Tutti. Ciò include i __init__.pyfile nei pacchetti. La documentazione può includere QUALSIASI direttiva sulla documentazione di Sphinx, anche .. automodule::per i moduli all'interno del pacchetto.
S.Lott

2
autodocè un errore di battitura, dovrebbe essere automodule. ma grazie mille per il suggerimento!
mariotomo,

9

Da Sphinx versione 3.1 (giugno 2020), sphinx.ext.autosummary(finalmente!) Ha una ricorsione.

Quindi non è necessario codificare i nomi dei moduli o fare affidamento su librerie di terze parti come Sphinx AutoAPI o Sphinx AutoPackageSummary per il rilevamento automatico dei pacchetti.

Esempio di pacchetto Python 3.7 da documentare ( vedi codice su Github e risultato su 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 la nuova :recursive:opzione):

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

Some words.

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

   mypackage

Questo è sufficiente per riepilogare automaticamente tutti i moduli del pacchetto, per quanto profondamente nidificati. Per ogni modulo, quindi riassume ogni attributo, funzione, classe ed eccezione in quel modulo.

Stranamente, tuttavia, i sphinx.ext.autosummarymodelli predefiniti non continuano a generare pagine di documentazione separate per ogni attributo, funzione, classe ed eccezione, e collegamenti ad esse dalle tabelle di riepilogo. È possibile estendere i modelli per farlo, come mostrato di seguito, ma non riesco a capire perché questo non sia il comportamento predefinito - sicuramente è quello che la maggior parte delle persone vorrebbe ..? L'ho sollevato come richiesta di funzionalità .

Ho dovuto copiare i modelli predefiniti localmente e quindi aggiungerli:

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

L'hook in custom-module-template.rstè index.rstsopra, usando l' :template:opzione. (Elimina quella riga per vedere cosa succede usando i modelli predefiniti di pacchetti di siti.)

custom-module-template.rst (righe aggiuntive indicate a destra):

{{ 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 (righe aggiuntive indicate a destra):

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


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.