Developer Guide

Setup Development Environment

  1. Install pip and tox:

    sudo apt-get install python-pip
    sudo pip install tox
    
  2. Configure git pre-commit hook:

    sudo pip install flake8 pep8-naming
    flake8 --install-hook
    git config flake8.strict true
    

Building Documentation

tox -e doc

Output will be available at .tox/doc/tmp/html. It is recommended to install the webdev package:

sudo pip install webdev

So a development web server can serve any location like this:

$ webdev .tox/doc/tmp/html

Running Test Suite

tox -e py27,py34

autoapi

Module that provides the module tree node APINode.

This class will load the module identified by name and recursively build a tree with all it’s submodules and subpackages. In the process, each node analyze and fetch the public API of that module.

name can be any node, like the root package, or any subpackage or submodule and a tree will be built from there. name must follow the standard “dot notation” for importing a module.

This class will not assume any special naming, or perform any complex analysis to determine what must be in the public interface. This is because it is not only a difficult problem, but it involves analyzing deeply the namespace of the module which can be quite expensive.

In general it is very difficult to determine in a module namespace what elements are private or public declared locally, private or public but declared in another module and brought into the module local namespace (from x import y), third party library, Python standard library, etc. At the end, any algorithm that tries to determine this will eventually fail to meet the requirements or expectations of the developer, leaving false positives or removing elements expected to be present in the public API.

For example, a common scenario is that some modules, specially package entry points __init__.py, can be setup to expose the public API of their sibling modules, possible causing several objects to be identified as part of the public API of both modules.

Because of this the approach taken by this module follows the rule in PEP20 “Explicit is better than implicit”. In consequence, the node will consider elements as public if they are explicitly listed in the __api__ or __all__ variables. It is up to the developer to list the elements that must be published in the public API.

__api__ is a special variable introduced by this module, and it exists for situation were for whatever reason the developer don’t want to list in the __all__ variable an element that needs to be published in the public API.

This class will extract all elements identified in ONE of those listings (not the union), with __api__ having the precedence. If none of those variables exists in the module then it will be assumed that no public API exists for that module and no futher actions will be taken.

If any of those variables exists this class will iterate all elements listed in them and will catalog them in four categories:

  • Functions.
  • Exceptions.
  • Classes.
  • Variables.

Being Variables the default if it cannot be determined that an element belongs to any of other categories.

Submodules

autoapi.sphinx

Glue for Sphinx API.

Functions

  • builder_inited(): autoapi Sphinx extension hook for the builder-inited event.
  • setup(): autoapi Sphinx extension setup.
autoapi.sphinx.builder_inited(app)

autoapi Sphinx extension hook for the builder-inited event.

This hook will read the configuration value autoapi_modules and render the modules described in it.

See http://sphinx-doc.org/extdev/appapi.html#event-builder-inited

autoapi.sphinx.setup(app)

autoapi Sphinx extension setup.

See http://sphinx-doc.org/extdev/tutorial.html#the-setup-function

Classes

  • APINode: Tree node class for module instrospection.
class autoapi.APINode(name, directory=None)

Tree node class for module instrospection.

Parameters:
  • name (str) – Name of the module to build the tree from. It must follow the “dot notation” of the import mechanism.
  • directory (dict) – Directory to store the index of all the modules. If None, the default, the root node will create one a pass it to the subnodes.

Attributes:

Variables:
  • name – Name of the current module.
  • subname – Last part of the name of this module. For example if name is my.module.another the subname will be another.
  • directory – Directory of the tree. This is a OrderedDict that will register all modules name with it’s associated node APINode. All nodes of a tree share this index and thus the whole tree can be queried from any node.
  • module – The loaded module.
  • subnodes – A list of APINode with all child submodules and subpackages.
  • subnodes_failed – A list of submodules and subpackages names that failed to import.

Public API categories:

Variables:
  • functions – A OrderedDict of all functions found in the public API of the module.
  • classes – A OrderedDict of all classes found in the public API of the module.
  • exceptions – A OrderedDict of all exceptions found in the public API of the module.
  • variables – A OrderedDict of all other elements found in the public API of the module.

In all categories the order on which the elements are listed is preserved.

Inheritance

Inheritance diagram of APINode
depth()

Get the depth of the current node in the tree.

Return type:int
Returns:The depth of the node. For example, for node my.add.foo the depth is 3.
get_module(name)

Get a module node by it’s name.

This is just a helper that does lookup on the directory index.

Return type:APINode or None
Returns:The module node identified by name in the tree. None if the name doesn’t exists.
has_public_api()

Check if this node has a public API.

Return type:bool
Returns:True if any category has at least one element.
is_leaf()

Check if the current node is a leaf in the tree.

A leaf node not necessarily is a module, it can be a package without modules (just the entry point __init__.py).

Return type:bool
Returns:True if no other subnodes exists for this node.
is_relevant()

Check if this branch of the tree is relevant.

A branch is relevant if the current node has a public API or if any of its subnodes is relevant (in order to reach relevant nodes).

Relevancy is determined at initialization by the root node.

Return type:bool
Returns:True if the current node is relevant.
is_root()

Check if the current node is the root node.

Return type:bool
Returns:True if the current node is the root node.
tree(level=0, fullname=True)

Pretty print the subtree at the current node.

For example, for the module confspec:

confspec
    confspec.manager [c]
    confspec.options [c]
    confspec.providers [c, v]
        confspec.providers.dict [c]
        confspec.providers.ini [c]
        confspec.providers.json [c]
    confspec.utils [f]
    confspec.validation [f]

The tags at the right of the name shows what kind of elements are present in the public interfaces of those modules.

Parameters:
  • level (int) – Indentation level.
  • fullname (bool) – Plot the full name of the module or just it’s subname.
walk()

Traverse the tree top-down.

Returns:This method will yield tuples (node, [leaves]) for each node in the tree.

documented

This is my module brief line.

This is a more complete paragraph documenting my module.

  • A list item.
  • Another list item.

This section can use any reST syntax.

Functions

  • a_function(): This is the brief description of my function.
documented.a_function(my_arg, another)

This is the brief description of my function.

This is a more complete example of my function. It can include doctest, code blocks or any other reST structure.

>>> a_function(10, [MyClass('a'), MyClass('b')])
20
Parameters:
  • my_arg (int) – The first argument of the function. Just a number.
  • another (A list of MyClass) – The other argument of the important function.
Returns:

The length of the second argument times the first argument.

Return type:

int

Classes

class documented.MyClass(param1, param2=None)

This is the brief of my main class.

A more general description of what the class does.

Parameters:
  • param1 (int) – The first parameter of my class.
  • param2 (int or float) – The second one.
Variables:

my_attribute – Just an instance attribute.

Raises:

TypeError – if param2 is not None.

Inheritance

Inheritance diagram of MyClass
class_attribute = 625

This is a class attribute.

my_method(param1, param2)

The brief of this method.

This method does many many important things.

Parameters:
  • param1 (int) – A parameter.
  • param2 (list) – Another parameter.
Returns:

A list of the first parameter as long a the length of the second parameter.

Return type:

list of int

class documented.AnotherClass(param1, param2=None)

This another class.

Check the nice inheritance diagram. See MyClass.

Inheritance

Inheritance diagram of AnotherClass

Exceptions

exception documented.MyException

This is my custom exception.

This is a more complete description of what my exception does. Again, you can be as verbose as you want here.

Inheritance

Inheritance diagram of MyException

Variables

documented.A_CONSTANT

This is an important constant.

1000
documented.YET_ANOTHER

Yet another public constant variable

{'jam': 'eggs',
 'this': 'that',
 'yet': {'things': [1, 2, 3, 'a'], 'tuples': (1000, 4)}}

AutoAPI

_images/logo.png

Automatic Python API reference documentation generator for Sphinx, inspired by Doxygen.

AutoAPI is a Sphinx extension that allows to automatically generate API reference documentation for Python packages (example), recursively, without any intervention from the developer. It will discover all the package modules and their public objects and document them.

Usage

  1. Install:

    pip install autoapi
    

    Or if using Virtualenv or Tox (and you should) add it to your requirements.txt (or requirements.dev.txt if you want to separate your package requirements from the development requirements).

  2. Add AutoAPI to your extensions:

    In your Sphinx conf.py configuration file add autoapi.sphinx:

    extensions = [
        'sphinx.ext.autodoc',
        'sphinx.ext.inheritance_diagram',
        'autoapi.sphinx'
    ]
    

    Make sure to have autodoc and inheritance_diagram too because the default generation template will use them.

  3. Configure AutoAPI with the root modules you want to generate documentation for:

    In your project conf.py file define the autoapi_modules dictionary variable with the module names as keys:

    autoapi_modules = {'mymodule': None}
    

    This dictionary maps the module names with the options for generation for that module:

    prune:

    bool [False] The package to document is modeled as a tree, and each submodule is a node of that tree. A node is considered relevant if it has a public interface that needs to be documented or it is required to reach a node that has a public interface. If prune is set to True, all branches that aren’t relevant will be silently ignored. If False, all branches will be generated, even for those modules that do not possess a public interface.

    override:

    bool [True] Regenerate the reference pages even if they exists. AutoAPI by default is automatic, but if you prefer to use it like autosummary_generate and create stub pages for later manual edition set the override flag to False and commit the generated pages to your version control. On the other hand, if fully automatic reference documentation generation is preferred put the output folder of this module in your version control ignore file.

    template:

    str ['module'] Template name to use. This option can be changed to use different templates for different modules. See the section Using different templates.

    output:

    str [module] Output folder to generate the documentation for this module relative to the folder where your conf.py is located. By default the output folder is the same as the module key. For example, for mymodule the following could be generated:

    .
    |-- conf.py
    `-- mymodule
        |-- mymodule.rst
        |-- mymodule.submodule.rst
        `-- mymodule.another.rst
    

    For example, a custom configuration could be:

    autoapi_modules = {
       'mymodule': {
          'override': False,
          'output': 'auto'
       }
    }
    
  4. Reference your documentation in your Sphinx project:

    Add in one of your reST source files a reference to the documentation.

    .. toctree::
       :hidden:
    
       mymodule/mymodule
    

    And, optionally, you can link to it like this:

    See the :doc:`reference documentation <mymodule/mymodule>`.
    
  5. Prepare your codebase:

    Now in your modules, put all the elements you want to document in the __all__ or the __api__ listings. See the section Documenting the code.

Documenting the code

The strict minimum:

  • List all public objects in your __all__ (preferred) or __api__.
  • Put at least a docstring with a brief in your module, in all your public classes, methods, functions and variables.

Even better:

  • Use the autodoc syntax in your docstrings (or use other that autodoc supports).

Use the following example as a guide. Check corresponding documentation produced by AutoAPI.

# -*- coding: utf-8 -*-
"""
This is my module brief line.

This is a more complete paragraph documenting my module.

- A list item.
- Another list item.

This section can use any reST syntax.
"""

A_CONSTANT = 1000
"""This is an important constant."""

YET_ANOTHER = {
    'this': 'that',
    'jam': 'eggs',
    'yet': {
        'things': [1, 2, 3, 'a'],
        'tuples': (A_CONSTANT, 4)
    }
}
"""Yet another public constant variable"""


def a_function(my_arg, another):
    """
    This is the brief description of my function.

    This is a more complete example of my function. It can include doctest,
    code blocks or any other reST structure.

    >>> a_function(10, [MyClass('a'), MyClass('b')])
    20

    :param int my_arg: The first argument of the function. Just a number.
    :param another: The other argument of the important function.
    :type another: A list of :class:`MyClass`

    :return: The length of the second argument times the first argument.
    :rtype: int
    """
    return my_arg * len(another)


class MyClass(object):
    """
    This is the brief of my main class.

    A more general description of what the class does.

    :param int param1: The first parameter of my class.
    :param param2: The second one.
    :type param2: int or float

    :var my_attribute: Just an instance attribute.

    :raises TypeError: if param2 is not None.
    """

    class_attribute = 625
    """This is a class attribute."""

    def __init__(self, param1, param2=None):
        self.param1 = param1
        if param2 is not None:
            raise TypeError()
        self.param2 = param2
        self.my_attribute = 100

    def my_method(self, param1, param2):
        """
        The brief of this method.

        This method does many many important things.

        :param int param1: A parameter.
        :param list param2: Another parameter.

        :return: A list of the first parameter as long a the length of the
         second parameter.
        :rtype: list of int
        """
        return [param1] * len(param2)


class AnotherClass(MyClass):
    """
    This another class.

    Check the nice inheritance diagram. See :class:`MyClass`.
    """


class MyException(Exception):
    """
    This is my custom exception.

    This is a more complete description of what my exception does. Again, you
    can be as verbose as you want here.
    """


__all__ = [
    'A_CONSTANT',
    'YET_ANOTHER',
    'a_function',
    'MyClass',
    'AnotherClass',
    'MyException'
]

Customizing

AutoAPI provides several ways to customize the output generated by the tool. AutoAPI uses the Jinja2 template engine for page generation.

Overriding the default template

The default template used by autoapi can be customized to meet your needs. To do so, copy the default template and put it in your templates_path (usually _templates) folder under:

<templates_path>/autoapi/module.rst

The next run Sphinx will use it for all your modules documentation generation.

Using different templates

Instead of providing a single template for all your modules you can also setup different templates for different modules. For example, for you module mymod you choose to use the template mymodtemplate. In your conf.py the following is setup:

# autoapi configuration
autoapi_modules = {
    'mymod': {'template': 'mymodtemplate'}
}

Now you need to place your template in:

<templates_path>/autoapi/mymodtemplate.rst

And AutoAPI will use it for documenting mymod only.

Improvements

This is a list of possible improvements, its doesn’t mean they are good idea or that they should be implemented.

  • Automatically hook the auto-generated files to the index document toctree.
  • Extend the __api__ key to support a dictionary so each element that requires documentation can provide attributes individually (like show special members).
  • Generate an optional module index page with a different template (called index.rst at the module generation folder).

License

Copyright (C) 2015-2018 KuraLabs S.R.L

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.