自动摘要生成的文档缺少除 `__init__` 之外的所有 dunder 方法

autosummary generated documentation missing all dunder methods except for `__init__`

我正在使用 Sphinx 的自动摘要自动为我的模块的每个成员生成单独的 rst 文件。文档按预期创建,除了生成的第一个文件缺少除 __init__.

之外的所有 dunder 方法

在我的 conf.py 中,我有以下几行:

extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.autosummary',
]

autosummary_generate = True
autosummary_imported_members = True

考虑下面的虚拟 class,它包含 dunder 和常规 public 方法:

class MyClassA:

    def __init__(self):
        r'__init__ docstring'
        pass

    def __call__(self):
        r'__call__ docstring'
        pass

    def __len__(self):
        r'__len__ docstring'
        pass

    def public_method_1(self):
        r'public_method_1 docstring'
        pass

    def public_method_2(self):
        r'public_method_2 docstring'
        pass

在我的第一个主文件中,我按如下方式设置了自动摘要:

.. autosummary::
    :toctree: my_module_members

    my_module.MyClassA
    my_module.MyClassB

正如预期的那样,自动摘要创建了一个名为 /my_module_members 的子目录,其中包含模块的每个成员的单独的 rst 文件。但是在这些自动生成的第一个文件的方法部分中只列出了 __init__。例如:

my_module.MyClassA
==================

.. currentmodule:: my_module

.. autoclass:: MyClassA




   .. rubric:: Methods

   .. autosummary::

      ~MyClassA.__init__
      ~MyClassA.public_method_1
      ~MyClassA.public_method_2

因此,在生成的html文档中,方法table中只列出了这三个方法,__call____len__没有:

所以我的问题是如何在以这种方式使用自动摘要时包含所有特殊方法?

问题出在自动摘要用于 classes 的默认模板上。这是文档中的relevant page,不过直接看默认模板更有帮助:

# In the sphinx v3.0.4 source code:
# sphinx/ext/autosummary/templates/autosummary/class.rst
{{ fullname | escape | underline}}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}

   {% block methods %}
   .. automethod:: __init__

   {% if methods %}
   .. rubric:: Methods

   .. autosummary::
   {% for item in methods %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block attributes %}
   {% if attributes %}
   .. rubric:: Attributes

   .. autosummary::
   {% for item in attributes %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}

您可以看到此模板如何对应于为您的项目生成的存根文件(尽管我不确定为什么您的模板缺少 .. automethod:: __init__ 行;也许我们有不同版本的 sphinx)。重要的部分是 {% for item in methods %} 循环。上面链接的文档简要提到 methods 仅包含 "public" 方法,这意味着不以下划线开头的方法。根据 sphinx/ext/autosummary/generate.py 的第 242 行,__init__() 也被视为 public,尽管这似乎没有在任何地方记录。希望这能解释您所看到的行为。

考虑到这一点,我可以想到三种方法将所有特殊方法包含在文档中:

  1. 提供使用 members 而不是 methods 的自定义模板。这应该记录所有内容,但会消除方法、属性、继承成员、内部 classes 等之间的区别

  2. 这没有记录,我也没有尝试过,看起来你可以用 all_methods 替换 methods 以包含自动摘要中的所有方法(再次参见第 242 行)。不过,public 和私有方法之间没有任何区别。

  3. 尝试使用 autoclasstoc。全面披露:这是我编写的一个包,用于更好地总结 sphinx 文档中的 class 方法。它比 autosummary 更可配置,并且默认包含每个方法。它还折叠了继承的方法,这对于大型 classes 来说非常好,尽管这可能与您无关。

    This page 描述了如何将 autoclasstoc 与自动摘要结合使用,但其要点是您需要一个看起来像这样的自定义模板:

    {{ fullname | escape | underline}}
    
    .. currentmodule:: {{ module }}
    
    .. autoclass:: {{ objname }}
       :members:
       :special-members:
    
        .. autoclasstoc::
    

我最终通过使用自定义 class 模板解决了这个问题,该模板查找 all_methods,然后过滤掉所有私人的以及那些 不是 在要包含的 dunder 方法列表中。

{{ fullname | escape | underline}}

.. currentmodule:: {{ module }}

.. autoclass:: {{ objname }}

   {% block methods %}

   {% if methods %}
   .. rubric:: {{ ('Methods') }}

   .. autosummary::
   {% for item in all_methods %}
      {%- if not item.startswith('_') or item in ['__init__',
                                                  '__repr__',
                                                  '__len__',
                                                  '__call__',
                                                  '__next__',
                                                  '__iter__',
                                                  '__getitem__',
                                                  '__setitem__',
                                                  '__delitem__',
                                                  ] %}
      ~{{ name }}.{{ item }}
      {%- endif -%}
   {%- endfor %}
   {% endif %}
   {% endblock %}

   {% block attributes %}
   {% if attributes %}
   .. rubric:: {{ ('Attributes') }}

   .. autosummary::
   {% for item in attributes %}
      ~{{ name }}.{{ item }}
   {%- endfor %}
   {% endif %}
   {% endblock %}