英文:
Sphinx not documenting complex Enum classes
问题
在我的代码中,我有一些复杂的枚举类型类。例如:
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
当我使用Sphinx和autodoc创建我的文档时,这个类会被跳过。我尝试在我的conf.py文件中添加一个自定义的文档生成器,像这样:
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)
但这也没有起作用。
我该如何让Sphinx文档化这种类型的类?
英文:
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?
答案1
得分: 7
这是在Sphinx的autodoc
中出现的一个bug,与Enum的某些用法有关。
可以通过谨慎地编写.rst
文件来解决这个问题。
话虽如此,我认为这是为了以下目的而进行的:
相应的.rst
文件:
my_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__
我稍微修改了代码以更好地解释一些细节的解决方法:
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
你的conf.py
可以保持标准,我只添加了extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon']
以启用Google风格的注释。
特定的触发bug的条件组合已经确定,目前在@mzjn提供的链接和你的帖子中已经明确了:
- 使用@classmethod + IntEnum。
- 使用@classmethod + 多重继承,其中一个父类是Enum。
应该注意:使用简单的Enum与@classmethod不会触发bug。(在这种情况下,.. autoclass::
的行为如预期,几乎可以处理所有内容。)
这个bug影响了多个autodoc
指令和它们的选项,导致它们具有意外的行为。
在编写.rst文件时,必要的解决方法如下:
-
不要在Enum中使用
:undoc-members:
,否则会出现混乱。如果你这样做,@classmethod将始终包括在内,而不会获取描述符或文档字符串,并且使用:exclude-members:
排除它将没有效果。 -
接下来,
__init__
是最具问题的方面。有效的方法是使用:exclude-members:
将其排除在外,并明确使用.. automethod:: __init__
。 -
除了上述之外,你不能将 @classmethod 与
__init__
放在.rst中的:automethod:
旁边,否则整个 @classmethod 将被 "吸收" 作为__init__
文档字符串的一部分。 -
对我来说,最有效的方法是使用
:members:
和:exclude-members:
明确包含/排除Enum的所有部分。这可以保证autodoc
指令/选项的行为最一致。
两个最后的注意事项与使用Sphinx文档化Enum相关(与bug无直接关系):
- 在文档化Enum成员时,为了保持一致性,最好使用
#:
语法,而不是三引号'''
或内联#
。原因是后者经常会被Sphinx "混淆" 或甚至丢失。
- 即使在指令选项或配置中使用
..member-order: by source
,通常情况下也是如此。
- 最后,如果你希望Enum成员的值以它们在类声明语法中出现的方式显示在文档中。根据我的经验,最好的方法是使用
.rst
中显示的:annotation:
,否则Enum成员将显示在文档中,如下所示:
使用Python 3.8和Sphinx v2.2.2。
英文:
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.
<hr>
<hr>
The specific combination of conditions triggering the bug are identified, so far, in the link @mzjn contributed, and by your post, namely:
- Using @classmethod + IntEnum.
- Using @classmethod + multiple inheritance, one of the parents being Enum.
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.)
<hr>
<hr>
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 theautodoc
directives/options.
<hr>
<hr>
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.
- The above is usually so even if using
..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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论