Developing autodoc extensions¶
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)
Overview¶
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
autointenumdirective that will document theIntEnumclass.The generated documentation will have all the enum possible values with names.
The
autointenumdirective will have an option:hex:which will cause the integers be printed in hexadecimal form.
Prerequisites¶
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.
Here is an example of the folder structure you might obtain:
└── source ├── _ext │ └── autodoc_intenum.py ├── conf.py ├── index.rst └── my_enums.py
Writing the extension¶
Start with setup function for the extension.
1defsetup(app: Sphinx) -> ExtensionMetadata: 2 app.setup_extension('sphinx.ext.autodoc') # Require autodoc extension 3 app.add_autodocumenter(IntEnumDocumenter) 4 return { 5 'version': '1', 6 'parallel_read_safe': True, 7 }
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__future__import annotations 2 3fromenumimport IntEnum 4fromtypingimport TYPE_CHECKING 5 6fromsphinx.ext.autodocimport ClassDocumenter, bool_option 7fromsphinx.ext.autodoc._generateimport _docstring_source_name
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:
1classIntEnumDocumenter(ClassDocumenter): 2 objtype = 'intenum' 3 directivetype = ClassDocumenter.objtype 4 priority = 25 5 option_spec = dict(ClassDocumenter.option_spec) 6 option_spec['hex'] = bool_option 7 8 @classmethod 9 defcan_document_member( 10 cls, member: Any, membername: str, isattr: bool, parent: Documenter 11 ) -> bool: 12 try: 13 return issubclass(member, IntEnum) 14 except TypeError: 15 return False 16 17 defadd_line(self, line: str, source: str = '', *lineno: int, indent: str) -> None: 18"""Append one line of generated reST to the output.""" 19 analyzer_source = '' if self.analyzer is None else self.analyzer.srcname 20 source_name = _docstring_source_name(props=self.props, source=analyzer_source) 21 if line.strip(): # not a blank line 22 self.result.append(indent + line, source_name, *lineno) 23 else: 24 self.result.append('', source_name, *lineno) 25 26 defadd_directive_header(self, *, indent: str) -> None: 27 super().add_directive_header(indent=indent) 28 self.add_line(' :final:', indent=indent) 29 30 defadd_content(self, more_content: StringList | None, *, indent: str) -> None: 31 super().add_content(more_content, indent=indent) 32 33 enum_object: IntEnum = self.props._obj 34 use_hex = self.options.hex 35 self.add_line('', indent=indent) 36 37 for the_member_name, enum_member in enum_object.__members__.items(): # type: ignore[attr-defined] 38 the_member_value = enum_member.value 39 if use_hex: 40 the_member_value = hex(the_member_value) 41 42 self.add_line(f'**{the_member_name}**: {the_member_value}', indent=indent) 43 self.add_line('', indent=indent)
Important attributes of the new class:
- objtype
This attribute determines the
autodirective name. In this case the auto directive will beautointenum.- 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:
classColors(IntEnum): """Colors enumerator""" NONE = 0 RED = 1 GREEN = 2 BLUE = 3
This will be the documentation file with auto-documentation directive:
.. autointenum:: my_enums.Colors
Further reading¶
If you wish to share your extension across multiple projects or with others, check out the Third-party extensions section.