Skip to content

Sphinx apidoc extension #4101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions doc/ext/apidoc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
.. highlight:: rest

:mod:`sphinx.ext.apidoc` -- Generate autodoc stub pages
=======================================================

.. module:: sphinx.ext.apidoc
:synopsis: Generate autodoc stub pages

.. versionadded:: 1.8

The functionality of this extension was previously available as part of the
:program:`sphinx-apidoc` tool. This ability to run this automatically by way
of a Sphinx extension was added in 1.8.

This extension generates function, method and attribute stub documentation
pages, similar to that generated by API doc tools like Doxygen or Javadoc.

.. warning::

The apidoc extension generates source files that use
:mod:`sphinx.ext.autodoc` to document all found modules. If any modules
have side effects on import, these will be executed when building
documentation.

If you document scripts (as opposed to library modules), make sure their
main routine is protected by a ``if __name__ == '__main__'`` condition.

Configuration
-------------

The apidoc extension uses the following configuration values:

.. confval:: apidoc_module_dir

The path to the module to document. This must be a path to a Python package.
This path can be a path relative to the documentation source directory or an
absolute path.

.. confval:: apidoc_output_dir

The output directory. If it does not exist, it is created. This path is
relative to the documentation source directory.

Defaults to ``api``.

.. confval:: apidoc_excluded_modules

An optional list of modules to exclude. These should be paths relative to
``api_module_dir``. fnmatch-style wildcarding is supported.

Defaults to ``[]``.
1 change: 1 addition & 0 deletions doc/ext/builtins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ These extensions are built in and can be activated by respective entries in the

.. toctree::

apidoc
autodoc
autosectionlabel
autosummary
Expand Down
48 changes: 45 additions & 3 deletions sphinx/ext/apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@
from sphinx import __display_version__, package_dir
from sphinx.cmd.quickstart import EXTENSIONS
from sphinx.locale import __
from sphinx.util import rst
from sphinx.util import logging, rst
from sphinx.util.osutil import FileAvoidWrite, ensuredir, walk

if False:
# For type annotation
from typing import Any, List, Tuple # NOQA
from typing import Any, Dict, List, Tuple # NOQA
from sphinx.application import Sphinx # NOQA

logger = logging.getLogger(__name__)

# automodule options
if 'SPHINX_APIDOC_OPTIONS' in os.environ:
Expand Down Expand Up @@ -459,6 +462,45 @@ def main(argv=sys.argv[1:]):
return 0


# So program can be started with "python -m sphinx.apidoc ..."
def builder_inited(app):
# type: (Sphinx) -> None
module_dir = app.config.apidoc_module_dir
output_dir = path.join(app.srcdir, app.config.apidoc_output_dir)
excludes = app.config.apidoc_excluded_modules

if not module_dir:
logger.warning("No 'apidoc_module_dir' specified; skipping API doc "
"generation")
return

# if the path is relative, make it relative to the 'conf.py' directory
if not path.isabs(module_dir):
module_dir = path.abspath(path.join(app.srcdir, module_dir))

excludes = [path.abspath(path.join(module_dir, exc)) for exc in excludes]

# refactor this module so that we can call 'recurse_tree' like a sane
# person - at present there is way too much passing around of the
# 'optparse.Value' instance returned by 'optparse.parse_args'
cmd = ['--force', '-o', output_dir, module_dir]
if excludes:
cmd += excludes

main(cmd)


def setup(app):
# type: (Sphinx) -> Dict[unicode, Any]
app.setup_extension('sphinx.ext.autodoc') # We need autodoc to function

app.connect('builder-inited', builder_inited)
app.add_config_value('apidoc_module_dir', None, 'env', [str])
app.add_config_value('apidoc_output_dir', 'api', 'env', [str])
app.add_config_value('apidoc_excluded_modules', [], 'env',
[[str]])

return {'version': __display_version__, 'parallel_read_safe': True}


if __name__ == "__main__":
main()
9 changes: 9 additions & 0 deletions tests/roots/test-ext-apidoc/apidoc_dummy_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from os import *


class Foo:
def __init__(self):
pass

def bar(self):
pass
12 changes: 12 additions & 0 deletions tests/roots/test-ext-apidoc/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-

import os
import sys

sys.path.insert(0, os.path.abspath('./'))

extensions = ['sphinx.ext.apidoc']
master_doc = 'index'

apidoc_module_dir = '.'
apidoc_excluded_modules = ['conf.py']
6 changes: 6 additions & 0 deletions tests/roots/test-ext-apidoc/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apidoc
======

.. toctree::

api/modules
11 changes: 9 additions & 2 deletions tests/test_ext_apidoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,6 @@ def extract_toc(path):
coderoot='test-apidoc-subpackage-in-toc',
options=['--separate']
)


def test_subpackage_in_toc(make_app, apidoc):
"""Make sure that empty subpackages with non-empty subpackages in them
are not skipped (issue #4520)
Expand All @@ -404,3 +402,12 @@ def test_subpackage_in_toc(make_app, apidoc):
assert 'parent.child.foo' in parent_child

assert (outdir / 'parent.child.foo.rst').isfile()


@pytest.mark.sphinx('html', testroot='ext-apidoc')
def test_apidoc_extension(app, status, warning):
app.builder.build_all()
assert (app.outdir / 'api').isdir()
assert (app.outdir / 'api' / 'modules.html').exists()
assert (app.outdir / 'api' / 'apidoc_dummy_module.html').exists()
assert not (app.outdir / 'api' / 'conf.html').exists()