Sphinx autodoc nie jest wystarczająco automatyczny

149

Próbuję użyć Sphinx do udokumentowania ponad 5000 liniowego projektu w Pythonie. Posiada około 7 modułów bazowych. O ile wiem, aby korzystać z autodoc, muszę napisać taki kod dla każdego pliku w moim projekcie:

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

Jest to zbyt uciążliwe, ponieważ mam wiele plików. Byłoby znacznie łatwiej, gdybym mógł po prostu określić, że chcę, aby pakiet „modów” był udokumentowany. Sphinx mógłby wówczas rekurencyjnie przejrzeć pakiet i utworzyć stronę dla każdego modułu podrzędnego.

Czy jest taka funkcja? Gdyby nie to, mógłbym napisać skrypt, który utworzyłby wszystkie pliki .rst, ale zajęłoby to dużo czasu.

Cory Walker
źródło
Co jest złego w pisaniu małego skryptu, który używa „os.walk” i zapisuje to wszystko? Przy okazji, mam ponad 40 000 liniowych projektów i nie wiem, o czym mówisz. Ile plików jest zaangażowanych? Jak trudne może być przekierowanie lsdo pliku i jego edycja?
S.Lott,
125
Nikt nie powiedział, że to trudne. OP powiedział, że to nudne , co jest. Biorąc pod uwagę, że inne systemy dokumentacyjne mogą to zrobić, nie jest to nierozsądne.
Gregg Lind
Po prostu użyj pdoc .
K3 --- rnc

Odpowiedzi:

143

Możesz sprawdzić ten skrypt , który zrobiłem. Myślę, że to może ci pomóc.

Ten skrypt analizuje drzewo katalogów w poszukiwaniu modułów i pakietów Pythona i tworzy pliki ReST odpowiednio do tworzenia dokumentacji kodu za pomocą Sphinx. Tworzy również indeks modułów.

AKTUALIZACJA

Ten skrypt jest teraz częścią Sphinx 1.1 jako apidoc .

Etienne
źródło
Gdzie masz wyprowadzić pliki? Próbowałem wyprowadzić je do domyślnego folderu _build Sphinx, ale uruchomienie sphinx-build -b html . ./_buildich nie podnosi.
Cerin
Powinieneś je umieścić w source directory(. W twoim przypadku). Katalog _build to miejsce, w którym zostaną utworzone pliki HTML. Sprawdź więcej informacji: sphinx.pocoo.org/tutorial.html#running-the-build
Etienne
1
@Erienne: fantastyczny scenariusz! właśnie tego szukałem. Wish it wygenerował nagłówki dla poszczególnych klas (zwykły wygląd sfinksa nie jest przyjemny dla klas.
Gubią
1
Nawet sfinks-apidok jest dość prymitywny. W przypadku pakietu z jednym lub dwoma modułami działa dobrze, ale mamy głęboko zagnieżdżone moduły, a sphinx-apidoc generuje dość niemożliwe do zarządzania wyjście.
slacy
4
samodzielne odpowiadanie: dodaj .. include:: modules.rstdo swojegoindex.rst
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
40

Nie wiem, czy Sphinx miał autosummaryrozszerzenie w momencie zadawania pierwotnego pytania, ale na razie jest całkiem możliwe, aby ustawić automatyczne generowanie tego rodzaju bez użycia sphinx-apidoclub podobnego skryptu. Poniżej znajdują się ustawienia, które działają dla jednego z moich projektów.

  1. Włącz autosummaryrozszerzenie (a także autodoc) w conf.pypliku i ustaw jego autosummary_generateopcję na True. To może wystarczyć, jeśli nie używasz niestandardowych *.rstszablonów. W przeciwnym razie dodaj katalog szablonów do listy wykluczeń, lub autosummaryspróbuje traktować je jako pliki wejściowe (co wydaje się być błędem).

    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary']
    autosummary_generate = True
    templates_path = [ '_templates' ]
    exclude_patterns = ['_build', '_templates']
  2. Użyj autosummary::w drzewie spisu treści w index.rstpliku. W tym przykładzie dokumentacja modułów project.module1i project.module2zostanie wygenerowana automatycznie i umieszczona w _autosummarykatalogu.

    PROJECT
    =======
    
    .. toctree::
    
    .. autosummary::
       :toctree: _autosummary
    
       project.module1
       project.module2
  3. Domyślnie autosummarygeneruje tylko bardzo krótkie podsumowania modułów i ich funkcji. Aby to zmienić, możesz umieścić plik szablonu niestandardowego w _templates/autosummary/module.rst(który zostanie przeanalizowany za pomocą Jinja2 ):

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

Podsumowując, nie ma potrzeby utrzymywania _autosummarykatalogu pod kontrolą wersji. Możesz także nazwać go dowolnie i umieścić w dowolnym miejscu w drzewie źródłowym (umieszczenie go poniżej _buildnie zadziała).

firegurafiku
źródło
4
To była ogromna pomoc. W punkcie 2, gdzie masz "project.module1" i "project.module2", czy istnieje sposób na automagiczne wygenerowanie tej listy dla każdego modułu w danym pakiecie? Aby po prostu wstawić „projekt” i wyłowić „moduł1” i „moduł2”?
Brown
Całkiem zaskoczony, że nie mogę nigdzie znaleźć odpowiedzi na to pytanie, czy kiedykolwiek rozwiązałeś to @Brown?
Alisdair Robertson
3
@AlisdairRobertson Nie, ale dostarczone rozwiązanie autopodsumowania okazało się bardziej niż wystarczające dla moich potrzeb. Jedyną inną rzeczą, o której myślałem, było napisanie skryptu do generowania pliku index.rst i automatycznego wykrywania nazw modułów. Jednak w praktyce lista modułów nie zmienia się tak często, więc edycja jednego pliku od czasu do czasu nie jest aż tak nierozsądna. Jestem pewien, że spędziłem już znacznie więcej czasu na poszukiwaniu rozwiązania niż po prostu edytować ten jeden plik!
Brown
12

W każdym pakiecie __init__.pyplik może zawierać .. automodule:: package.modulekomponenty dla każdej części pakietu.

Wtedy możesz .. automodule:: packagei robi to, co chcesz.

S.Lott
źródło
czy po prostu umieścić ten ciąg w potrójnych cudzysłowach w init .py?
Cory Walker
5
@Cory Walker: To nie jest „ciąg”. Możesz - i powinieneś - umieszczać ciągi dokumentów w potrójnych cudzysłowach w każdym pliku. Wszyscy. Obejmuje to __init__.pypliki w twoich pakietach. Dokumentacja może zawierać DOWOLNE dyrektywy dokumentacji Sphinx, w tym .. automodule::dla modułów w pakiecie.
S.Lott
2
autodocto literówka, a powinno automodule. ale wielkie dzięki za podpowiedź!
mariotomo
9

Od wersji 3.1 Sphinx (czerwiec 2020) sphinx.ext.autosummary(w końcu!) Ma rekursję.

Nie trzeba więc już na stałe kodować nazw modułów ani polegać na bibliotekach innych firm, takich jak Sphinx AutoAPI lub Sphinx AutoPackageSummary do automatycznego wykrywania pakietów.

Przykładowy pakiet Pythona 3.7 do udokumentowania ( zobacz kod na Github i wynik w 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(uwaga na nową :recursive:opcję):

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

Some words.

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

   mypackage

To wystarczy, aby automatycznie podsumować każdy moduł w pakiecie, nawet jeśli jest głęboko zagnieżdżony. Dla każdego modułu podsumowuje następnie każdy atrybut, funkcję, klasę i wyjątek w tym module.

Co dziwne, domyślne sphinx.ext.autosummaryszablony nie generują oddzielnych stron dokumentacji dla każdego atrybutu, funkcji, klasy i wyjątku oraz nie zawierają linków do nich z tabel podsumowań. Możliwe jest rozszerzenie szablonów, aby to zrobić, jak pokazano poniżej, ale nie mogę zrozumieć, dlaczego nie jest to domyślne zachowanie - z pewnością tego chciałaby większość ludzi ...? Podniosłem to jako prośbę o funkcję .

Musiałem lokalnie skopiować domyślne szablony, a następnie dodać do nich:

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

Hak do custom-module-template.rstznajduje się index.rstpowyżej, korzystając z :template:opcji. (Usuń ten wiersz, aby zobaczyć, co się dzieje przy użyciu domyślnych szablonów pakietów witryn).

custom-module-template.rst (dodatkowe linie zaznaczone po prawej stronie):

{{ 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 (dodatkowe linie zaznaczone po prawej stronie):

{{ 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
źródło