Developing autodoc extension for IntEnum

The objective of this tutorial is to create an extension that adds support for new type for autodoc. This autodoc extension will format the IntEnum class from Python standard library. (module enum)

概要

We want the extension that will create auto-documentation for IntEnum. IntEnum is the integer enum class from standard library enum module.

Currently this class has no special auto documentation behavior.

We want to add following to autodoc:

  • A new autointenum directive that will document the IntEnum class.

  • The generated documentation will have all the enum possible values with names.

  • The autointenum directive will have an option :hex: which will cause the integers be printed in hexadecimal form.

前提条件

We need the same setup as in the previous extensions. This time, we will be putting out extension in a file called autodoc_intenum.py. The my_enums.py will contain the sample enums we will document.

フォルダ構造として次のようなものが得られるでしょう:

└── source
    ├── _ext
    │   └── autodoc_intenum.py
    ├── conf.py
    ├── index.rst
    └── my_enums.py

拡張機能の書き方

Start with setup function for the extension.

1def setup(app: Sphinx) -> None:
2    app.setup_extension('sphinx.ext.autodoc')  # Require autodoc extension
3    app.add_autodocumenter(IntEnumDocumenter)

The setup_extension() method will pull the autodoc extension because our new extension depends on autodoc. add_autodocumenter() is the method that registers our new auto documenter class.

We want to import certain objects from the autodoc extension:

1from enum import IntEnum
2from typing import Any, Optional
3
4from docutils.statemachine import StringList
5
6from sphinx.application import Sphinx
7from sphinx.ext.autodoc import ClassDocumenter, bool_option

There are several different documenter classes such as MethodDocumenter or AttributeDocumenter available in the autodoc extension but our new class is the subclass of ClassDocumenter which a documenter class used by autodoc to document classes.

This is the definition of our new the auto-documenter class:

 1class IntEnumDocumenter(ClassDocumenter):
 2    objtype = 'intenum'
 3    directivetype = 'class'
 4    priority = 10 + ClassDocumenter.priority
 5    option_spec = dict(ClassDocumenter.option_spec)
 6    option_spec['hex'] = bool_option
 7
 8    @classmethod
 9    def can_document_member(cls,
10                            member: Any, membername: str,
11                            isattr: bool, parent: Any) -> bool:
12        return isinstance(member, IntEnum)
13
14    def add_directive_header(self, sig: str) -> None:
15        super().add_directive_header(sig)
16        self.add_line('   :final:', self.get_sourcename())
17
18    def add_content(self,
19                    more_content: Optional[StringList],
20                    no_docstring: bool = False
21                    ) -> None:
22
23        super().add_content(more_content, no_docstring)
24
25        source_name = self.get_sourcename()
26        enum_object: IntEnum = self.object
27        use_hex = self.options.hex
28        self.add_line('', source_name)
29
30        for enum_value in enum_object:
31            the_value_name = enum_value.name
32            the_value_value = enum_value.value
33            if use_hex:
34                the_value_value = hex(the_value_value)
35
36            self.add_line(
37                f"**{the_value_name}**: {the_value_value}", source_name)
38            self.add_line('', source_name)

Important attributes of the new class:

objtype

This attribute determines the auto directive name. In this case the auto directive will be autointenum.

directivetype

This attribute sets the generated directive name. In this example the generated directive will be .. :py:class::.

priority

the larger the number the higher is the priority. We want our documenter be higher priority than the parent.

option_spec

option specifications. We copy the parent class options and add a new option hex.

Overridden members:

can_document_member

This member is important to override. It should return True when the passed object can be documented by this class.

add_directive_header

This method generates the directive header. We add :final: directive option. Remember to call super or no directive will be generated.

add_content

This method generates the body of the class documentation. After calling the super method we generate lines for enum description.

Using the extension

You can now use the new autodoc directive to document any IntEnum.

For example, you have the following IntEnum:

my_enums.py
class Colors(IntEnum):
    """Colors enumerator"""
    NONE = 0
    RED = 1
    GREEN = 2
    BLUE = 3

This will be the documentation file with auto-documentation directive:

index.rst
.. autointenum:: my_enums.Colors