222

I'm trying to use Sphinx to document a 5,000+ line project in Python. It has about 7 base modules. As far as I know, In order to use autodoc I need to write code like this for each file in my project:

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

This is way too tedious because I have many files. It would be much easier if I could just specify that I wanted the 'mods' package to be documented. Sphinx could then recursively go through the package and make a page for each submodule.

Is there a feature like this? If not I could write a script to make all the .rst files, but that would take up a lot of time.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Cory Walker
  • 4,809
  • 4
  • 28
  • 32

6 Answers6

161

You can check this script that I've made. I think it can help you.

This script parses a directory tree looking for python modules and packages and creates ReST files appropriately to create code documentation with Sphinx. It also creates a modules index.

UPDATE

This script is now part of Sphinx 1.1 as apidoc.

Etienne
  • 12,440
  • 5
  • 44
  • 50
  • 1
    Where are you supposed to output the files to? I tried outputting them to Sphinx's default _build folder, but running `sphinx-build -b html . ./_build` doesn't pick them up. – Cerin Jan 06 '11 at 18:13
  • 1
    You should put them in the `source directory` (. in your case). The _build directory is where the HTML files will be created. Check for more info: http://sphinx.pocoo.org/tutorial.html#running-the-build – Etienne Jan 06 '11 at 20:16
  • 1
    @Erienne: fantastic script! just what i was looking for. Wish it generated headings for individual classes (the regular sphinx look isn't nice to classes. they get lost in larger modules) – jbenet Dec 22 '11 at 14:40
  • 2
    Even sphinx-apidoc is pretty rudimentary. For a package with one or two modules, it works okay, but we've got modules nested deeply, and sphinx-apidoc produces some pretty unmanageable output. – slacy Apr 27 '12 at 20:35
  • @slacy It's true that sphinx-apidoc is relatively rudimentary but I'm curious to know more precisely what you mean by "unmanageable output"? I use it on a not so small code base without any problems (but modules were not nested too deeply). – Etienne May 06 '12 at 02:03
  • how to use the `shpinx-apidoc` output? I've got `mod.rst` and `modules.rst` on the same dir as `conf.py` and `index.rst`. You could put/ this info on the manual page @Etienne. – Ciro Santilli OurBigBook.com Apr 04 '13 at 12:56
  • 5
    self answering: add `.. include:: modules.rst` to your `index.rst` – Ciro Santilli OurBigBook.com Apr 04 '13 at 13:27
  • self answering again: if you are on a new project, use `apidoc -F -o desired-doc-location py-source-root`, and this generates the the doc templates with a working `index.rst`. The generated `Makefile` however does not regenerate module list as of current version (if you add a new file, you need to rerun `sphinx-apidoc`) – Ciro Santilli OurBigBook.com Apr 04 '13 at 14:08
  • The script link is broken. – lexalenka Sep 01 '21 at 17:36
  • 1
    Yes, as mention in the UPDATE, the script is now part of Sphinx as `apidoc`. I strikethrough the line to be more clear. – Etienne Sep 01 '21 at 18:26
157

From Sphinx version 3.1 (June 2020), sphinx.ext.autosummary (finally!) has automatic recursion.

So no need to hard code module names or rely on 3rd party libraries like Sphinx AutoAPI or Sphinx AutoPackageSummary for their automatic package detection any more.

Example Python 3.7 package to document (see code on Github and result on 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 (note new :recursive: option):

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

Some words.

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

   mypackage

This is sufficient to automatically summarise every module in the package, however deeply nested. For each module, it then summarises every attribute, function, class and exception in that module.

Oddly, though, the default sphinx.ext.autosummary templates don't go on to generate separate documentation pages for each attribute, function, class and exception, and link to them from the summary tables. It's possible to extend the templates to do this, as shown below, but I can't understand why this isn't the default behaviour - surely that's what most people would want..? I've raised it as a feature request.

I had to copy the default templates locally, and then add to them:

  • Copy site-packages/sphinx/ext/autosummary/templates/autosummary/module.rst to mytoolbox/doc/source/_templates/custom-module-template.rst
  • Copy site-packages/sphinx/ext/autosummary/templates/autosummary/class.rst to mytoolbox/doc/source/_templates/custom-class-template.rst

The hook into custom-module-template.rst is in index.rst above, using the :template: option. (Delete that line to see what happens using the default site-packages templates.)

custom-module-template.rst (additional lines noted on the right):

{{ 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 (additional lines noted on the right):

{{ 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
  • 2,064
  • 1
  • 10
  • 11
  • 5
    Thank you so much for this!!! I was almost about to give up and then finally found this. One question. I was using `sys.path.insert(0, os.path.abspath('../../mypackage'))` but you only use `'../..'`. Why does it not make a difference? – samlaf Nov 06 '20 at 22:48
  • 3
    No problem, glad it helped. Sphinx nearly drove me insane until I got it figured; now it all works beautifully. In `index.rst`, you need to explicitly name the package where you want `:recursive:` docstring extraction to start, so in my example `mypackage`. In `conf.py`, you just provide the route to that location. That's because in my example `mypackage` is a top-level folder in `mytoolbox`, but if it was (eg.) nested in a `source/mypackage` subfolder then `conf.py` would read `../../source`. – James Leedham Nov 08 '20 at 09:58
  • 1
    @jamleed This has been very useful!. Thanks for taking the time and writing Sphinx AutoPackageSummary. You saved me a lot of time. In that github project, the make html part (make.bat) was not available, so "make html" command did not work out of the box. You might have to add some instructions around that or upload that file as well. Otherwise, it worked as a charm. (Edited - included the missing file name) – SKG Nov 17 '20 at 15:08
  • Ok, thanks for pointing that out. A function of my not thinking about Windows users - sorry! Glad the project was of use... – James Leedham Nov 18 '20 at 11:16
  • 5
    This answer is gold, thanks a lot! Hope your feature request gets implemented. It should indeed be the default behaviour. – rickstaa Feb 24 '21 at 15:47
  • 3
    Just a small improvement to `custom-module-template.rst`, if you add `:members:` under the `.. automodule:: {{ fullname }}` (line 3), the documentation for that module will automatically be populated with members (classes, functions, etc.) in addition to the toctree. This gives users the option to quickly scan through the documentation in addition to pinpointing functions using the toctree. – FullmetalEngineer Mar 11 '21 at 16:57
  • How do I prevent sphinx from documenting 3rd party classes when using this in conjunction with `autosummary_imported_members = True`? I want this option to get documentation for imports in the `__init__.py` files. I already added `__all__` lists to all my modules but it still creates .rst's for 3rd party classes – Hyperplane Jul 15 '21 at 15:41
  • 1
    It didn't work for code structure given [here](https://github.com/sphinx-doc/sphinx/issues/7912#issuecomment-888241734); couldn't open docs for functions and classes in codebase – Vineet Jul 28 '21 at 12:32
  • @Vineet what's in your `__init__.py` files? If lots of `import` statements, then I've seen Sphinx fail to follow them properly, as per this bug: https://github.com/sphinx-doc/sphinx/issues/9069 (though I'm unsure what the exact problem is...) A – James Leedham Jul 28 '21 at 15:37
  • Also, what happens if you remove all the `transform` module code, and the `transform` statement in `index.rst`, and just run `make html` on the `extract` module? – James Leedham Jul 28 '21 at 15:43
  • @JamesLeedham my `__init__.py`s are empty. On removing `transform` module, similar behaviour was observed as one can see in `autosummary` - I am unable to view documentation of functions and classes on clicking on them. This does not happen when I specify individual files like - `extract.folder_1.a`. – Vineet Jul 28 '21 at 18:40
  • Thank you so much for you detailed answer. It really helped a lot :) – Karim Akhnoukh Sep 01 '21 at 15:40
  • Thank you so much for this answer. This literally fully automates the documentation :) Incredible what one recursive feature is able to do :) – dheinz Dec 16 '21 at 08:08
  • What commandline-scripts do you run to get this working? – Phil Feb 28 '22 at 21:14
  • 1
    @Phil, if you pull the repo and follow these instructions to build it locally, you should be able to unpick all the commands: https://github.com/JamesALeedham/Sphinx-Autosummary-Recursion/tree/master/docs – James Leedham Mar 02 '22 at 08:24
  • Special attention for the "Copy (...)/module.rst" and "class.rst" parts, as the files have changed recently and copying the whole code above might not work correctly. – Diego F Medina Mar 30 '22 at 17:23
  • 1
    If you're coming to this error from Jupyter-book, note that as of Oct 2022 you must use jupyter-book>0.13 and sphinx-book-theme>0.3 in order to supply a template path in the config (See the issue https://github.com/executablebooks/jupyter-book/issues/1660 and fix https://github.com/executablebooks/sphinx-book-theme/pull/566). Putting this here for lack of a better place. – raf Oct 28 '22 at 21:01
  • @JamesLeedham, in my case it seems that `autosummary` cannot go through files in a subfolder of a folder, but solely files in a folder (have a look at my post https://stackoverflow.com/q/76604082/4382986). Is there any workaround to this? – Élio Pereira Jul 04 '23 at 14:00
  • self-answering: I was able to get `autosummary` to include the content in subfolders in the summary by creating `__init__.py` files in these subfolders. – Élio Pereira Jul 05 '23 at 08:04
61

I do not know whether Sphinx had had autosummary extension at the time original question was asked, but for now it is quite possible to set up automatic generation of that kind without using sphinx-apidoc or similar script. Below there are settings which work for one of my projects.

  1. Enable autosummary extension (as well as autodoc) in conf.py file and set its autosummary_generate option to True. This may be enough if you're not using custom *.rst templates. Otherwise add your templates directory to exclude list, or autosummary will try to treat them as input files (which seems to be a bug).

    extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary']
    autosummary_generate = True
    templates_path = [ '_templates' ]
    exclude_patterns = ['_build', '_templates']
    
  2. Use autosummary:: in TOC tree in your index.rst file. In this example documentation for modules project.module1 and project.module2 will be generated automatically and placed into _autosummary directory.

    PROJECT
    =======
    
    .. toctree::
    
    .. autosummary::
       :toctree: _autosummary
    
       project.module1
       project.module2
    
  3. By default autosummary will generate only very short summaries for modules and their functions. To change that you can put a custom template file into _templates/autosummary/module.rst (which will be parsed with Jinja2):

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

In conclusion, there is no need to keep _autosummary directory under version control. Also, you may name it anything you want and place it anywhere in the source tree (putting it below _build will not work, though).

bad_coder
  • 11,289
  • 20
  • 44
  • 72
firegurafiku
  • 3,017
  • 1
  • 28
  • 37
  • 6
    This was a huge help. In point 2, where you have "project.module1" and "project.module2", is there a way to automagically generate that list for every module in a given package? To just put "project" and have it sniff out "module1" and "module2"? – Brown Jun 22 '17 at 21:24
  • 1
    Pretty surprised I can't find an answer to this anywhere, dod you ever work it out @Brown? – Alisdair Robertson Sep 09 '17 at 09:18
  • 3
    @AlisdairRobertson No, but the autosummary solution provided ended up being more than adequate for my needs. The only other thing I thought of doing was to write a script to generate the index.rst file and autodetect the module names. However, in practice, the list of modules doesn't change that often, so just editing one file once in a while isn't that unreasonable. I'm sure I already spent much more time looking for a solution than it takes to just edit that one file! – Brown Sep 13 '17 at 13:49
  • 1
    Are there no other steps to this? Updating to this is still only doing current directory and not recursive. I also see: WARNING: Error in "autosummary" directive: unknown option: "recursive". Current directory html is made only. – lexalenka Sep 01 '21 at 17:58
28

Sphinx AutoAPI does exactly this.

Vito
  • 1,580
  • 1
  • 17
  • 32
  • 5
    Oh my goodness! This works so much better than anything else. *Note that this is NOT "autodoc" or "apidoc", it's a completely different extension.* – ropeladder Feb 19 '20 at 00:25
  • 6
    Ditto. This puts the "auto" in "autodoc".... Here is all our project had to do to switch: [Switch to autoapi from autodoc by nealmcb · Pull Request \#7 · gwexploratoryaudits/r2b2](https://github.com/gwexploratoryaudits/r2b2/pull/7) – nealmcb Feb 27 '20 at 16:20
  • 3
    Links are not answers, please elaborate – endolith Jan 12 '22 at 22:20
14

In each package, the __init__.py file can have .. automodule:: package.module components for each part of the package.

Then you can .. automodule:: package and it mostly does what you want.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • do I just put that string in triple quotes in __init__.py? – Cory Walker Apr 23 '10 at 21:36
  • 8
    @Cory Walker: It's not "a" string. You can -- and **should** -- be putting triple-quoted docstrings in every single file. Every one. That includes the `__init__.py` files in your packages. The docstring can include ANY Sphinx documentation directives, including `.. automodule::` for modules within the package. – S.Lott Apr 24 '10 at 22:35
1

Maybe what you're looking for is Epydoc and this Sphinx extension.

Edward Dale
  • 29,597
  • 13
  • 90
  • 129