Sphinx不会记录复杂的Enum类。

huangapple go评论149阅读模式
英文:

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文件来解决这个问题。

话虽如此,我认为这是为了以下目的而进行的:

Sphinx不会记录复杂的Enum类。

相应的.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提供的链接和你的帖子中已经明确了:

  1. 使用@classmethod + IntEnum。
  2. 使用@classmethod + 多重继承,其中一个父类是Enum。

应该注意:使用简单的Enum与@classmethod不会触发bug。(在这种情况下,.. autoclass::的行为如预期,几乎可以处理所有内容。)

这个bug影响了多个autodoc指令和它们的选项,导致它们具有意外的行为。

在编写.rst文件时,必要的解决方法如下:

  1. 不要在Enum中使用 :undoc-members:,否则会出现混乱。如果你这样做,@classmethod将始终包括在内,而不会获取描述符或文档字符串,并且使用 :exclude-members: 排除它将没有效果。

  2. 接下来,__init__ 是最具问题的方面。有效的方法是使用 :exclude-members: 将其排除在外,并明确使用 .. automethod:: __init__

  3. 除了上述之外,你不能将 @classmethod 与 __init__ 放在.rst中的 :automethod: 旁边,否则整个 @classmethod 将被 "吸收" 作为 __init__ 文档字符串的一部分。

  4. 对我来说,最有效的方法是使用 :members::exclude-members: 明确包含/排除Enum的所有部分。这可以保证 autodoc 指令/选项的行为最一致。

两个最后的注意事项与使用Sphinx文档化Enum相关(与bug无直接关系):

  1. 在文档化Enum成员时,为了保持一致性,最好使用 #: 语法,而不是三引号 ''' 或内联 #。原因是后者经常会被Sphinx "混淆" 或甚至丢失。
  • 即使在指令选项或配置中使用 ..member-order: by source,通常情况下也是如此。
  1. 最后,如果你希望Enum成员的值以它们在类声明语法中出现的方式显示在文档中。根据我的经验,最好的方法是使用 .rst 中显示的 :annotation:,否则Enum成员将显示在文档中,如下所示:

Sphinx不会记录复杂的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:

Sphinx不会记录复杂的Enum类。

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:

  1. Using @classmethod + IntEnum.
  2. 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:

  1. 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.

  2. Next __init__ is the most problematic aspect. What worked was excluding it with :exclude-members:, together with explicitly using .. automethod:: __init__.

  3. 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.

  4. 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.

<hr>
<hr>

Two last notes are pertinent to documenting Enum's using Sphinx (not directly related to the bug).

  1. When documenting Enum members, for best consistency use #: syntax instead of triple-quotes &#39;&#39;&#39; 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.
  1. 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:

Sphinx不会记录复杂的Enum类。

Using Python 3.8 with Sphinx v2.2.2.

huangapple
  • 本文由 发表于 2020年1月6日 19:13:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/59611074.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定