In my code I have some classes that are complex Enum types. For example:
class ComplexEnum(SomeOtherClass, Enum):
""" Some documentation """
MEMBER1 = SomeOtherClass(1)
MEMBER2 = SomeOtherClass(2)
def __init__(self, arg):
""" more doc """
pass
def somemethod(self):
""" more doc """
pass
@classmethod
def someclassmethod(cls, otherparam):
""" more doc """
pass
When I now create my documentation with Sphinx using autodoc this class is just skipped. I tried adding a custom documenter like this to my conf.py file:
from sphinx.ext.autodoc import ClassDocumenter
class MyClassDocumenter(ClassDocumenter):
objtype = 'ComplexEnum'
directivetype = 'class'
@classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, ComplexEnum)
def setup(app):
app.add_autodocumenter(MyClassDocumenter)
But this does not work either.
How can I make sphinx document those kind of classes?
This is a bug in Sphinx autodoc
occurring with some uses of Enum.
It can be solved by careful workarounds writing the .rst
files.
Having said that, I assume this is aimed for:
The corresponding .rst
:
my_module module
================
.. automodule:: my_module
:exclude-members: ComplexEnum
.. autoclass:: ComplexEnum
:members: some_method
:show-inheritance:
:exclude-members: MEMBER1, MEMBER2, __init__, some_classmethod
.. automethod:: some_classmethod
.. autoattribute:: MEMBER1
:annotation: = SomeOtherClass(1)
.. autoattribute:: MEMBER2
:annotation: = SomeOtherClass(2)
.. automethod:: __init__
.. autoclass:: SomeOtherClass
:special-members: __init__
I slightly altered the code to better explain some details of the workarounds:
from enum import Enum
class SomeOtherClass:
""" SomeOtherClass documentation """
def __init__(self, other_arg):
"""Example of docstring on the __init__ method.
Args:
other_arg (int): Description of `other_arg`.
"""
self.other_arg = other_arg
class ComplexEnum(SomeOtherClass, Enum):
"""ComplexEnum documentation."""
#: :py:mod:`~my_package.my_module.SomeOtherClass`: MEMBER1 docstring comment.
MEMBER1 = SomeOtherClass(1)
#: :py:mod:`~my_package.my_module.SomeOtherClass`: MEMBER2 docstring comment.
MEMBER2 = SomeOtherClass(2)
def __init__(self, complex_arg):
"""Example of docstring on the __init__ method.
Args:
complex_arg (int): Description of `complex_arg`.
"""
self.complex_arg = complex_arg
super().__init__(complex_arg)
def some_method(self):
"""The doc of some_method."""
pass
@classmethod
def some_classmethod(cls, some_arg):
"""The doc of some_classmethod.
Args:
some_arg (int): Description of `some_arg`.
"""
pass
Your conf.py
can be left standard, I only added extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
to enable google style comments.
The specific combination of conditions triggering the bug are identified, so far, in the link @mzjn contributed, and by your post, namely:
It should be noted: using a simple Enum with @classmethod does not trigger the bug. (In that case .. autoclass::
behaves as expected and takes care of almost everything.)
The bug affects several autodoc
directives and their options, causing them to have unexpected behavior.
The necessary workarounds in writing the .rst are as follows:
DO NOT use :undoc-members:
in the Enum, or else caos breaks out. If you do, the @classmethod with always be included without picking up the descriptor or the docstring, and excluding it with :exclude-members:
will have no effect.
Next __init__
is the most problematic aspect. What worked was excluding it with :exclude-members:
, together with explicitly using .. automethod:: __init__
.
Together with the above: you CAN NOT put the @classmethod next to __init__
using :automethod:
in the .rst
, or else the entire @classmethod gets "absorved" as part of the __init__
docstring.
What worked best, for me, is including/excluding all parts of the Enum explicitly with :members:
and :exclude-members:
. That guarantees the best consistency to the behavior of the autodoc
directives/options.
Two last notes are pertinent to documenting Enum's using Sphinx (not directly related to the bug).
When documenting Enum members, for best consistency use #:
syntax instead of triple-quotes '''
or inline #
. The reason is, because the later are frequently "mixed-up" or even lost by Sphinx.
..member-order: by source
as a directive option or in the configurations.Finally, in case you want the values of the Enum members to show in the documentation, as they appear in the class declaration syntax. The best way, in my experience, is using an :annotation:
as shown in the .rst
. Or else, the Enum members will show in documentation like this:
Using Python 3.8 with Sphinx v2.2.2.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With