mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
autodoc: fix ordering of class and static methods for groupwise
order (#13201)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
parent
f4a802cce7
commit
5b9fb9e060
@ -78,6 +78,11 @@ Bugs fixed
|
||||
* #1810: Always copy static files when building, regardless of whether
|
||||
any documents have changed since the previous build.
|
||||
Patch by Adam Turner.
|
||||
* #13201: autodoc: fix ordering of members when using ``groupwise``
|
||||
for :confval:`autodoc_member_order`. Class methods are now rendered
|
||||
before static methods, which themselves are rendered before regular
|
||||
methods and attributes.
|
||||
Patch by Bénédikt Tran.
|
||||
|
||||
Testing
|
||||
-------
|
||||
|
@ -986,10 +986,12 @@ There are also config values that you can set:
|
||||
|
||||
* ``'alphabetical'``:
|
||||
Use alphabetical order.
|
||||
|
||||
* ``'groupwise'``: order by member type. The order is:
|
||||
|
||||
* for modules, exceptions, classes, functions, data
|
||||
* for classes: methods, then properties and attributes
|
||||
* for classes: class methods, static methods, methods,
|
||||
and properties/attributes
|
||||
|
||||
Members are ordered alphabetically within groups.
|
||||
|
||||
|
@ -907,7 +907,7 @@ class Documenter:
|
||||
members_check_module, members = self.get_object_members(want_all)
|
||||
|
||||
# document non-skipped members
|
||||
memberdocumenters: list[tuple[Documenter, bool]] = []
|
||||
member_documenters: list[tuple[Documenter, bool]] = []
|
||||
for mname, member, isattr in self.filter_members(members, want_all):
|
||||
classes = [
|
||||
cls
|
||||
@ -923,13 +923,27 @@ class Documenter:
|
||||
# of inner classes can be documented
|
||||
full_mname = f'{self.modname}::' + '.'.join((*self.objpath, mname))
|
||||
documenter = classes[-1](self.directive, full_mname, self.indent)
|
||||
memberdocumenters.append((documenter, isattr))
|
||||
member_documenters.append((documenter, isattr))
|
||||
|
||||
member_order = self.options.member_order or self.config.autodoc_member_order
|
||||
memberdocumenters = self.sort_members(memberdocumenters, member_order)
|
||||
# We now try to import all objects before ordering them. This is to
|
||||
# avoid possible circular imports if we were to import objects after
|
||||
# their associated documenters have been sorted.
|
||||
member_documenters = [
|
||||
(documenter, isattr)
|
||||
for documenter, isattr in member_documenters
|
||||
if documenter.parse_name() and documenter.import_object()
|
||||
]
|
||||
member_documenters = self.sort_members(member_documenters, member_order)
|
||||
|
||||
for documenter, isattr in memberdocumenters:
|
||||
documenter.generate(
|
||||
for documenter, isattr in member_documenters:
|
||||
assert documenter.modname
|
||||
# We can directly call ._generate() since the documenters
|
||||
# already called parse_name() and import_object() before.
|
||||
#
|
||||
# Note that those two methods above do not emit events, so
|
||||
# whatever objects we deduced should not have changed.
|
||||
documenter._generate(
|
||||
all_members=True,
|
||||
real_modname=self.real_modname,
|
||||
check_module=members_check_module and not isattr,
|
||||
@ -995,6 +1009,15 @@ class Documenter:
|
||||
if not self.import_object():
|
||||
return
|
||||
|
||||
self._generate(more_content, real_modname, check_module, all_members)
|
||||
|
||||
def _generate(
|
||||
self,
|
||||
more_content: StringList | None = None,
|
||||
real_modname: str | None = None,
|
||||
check_module: bool = False,
|
||||
all_members: bool = False,
|
||||
) -> None:
|
||||
# If there is no real module defined, figure out which to use.
|
||||
# The real module is used in the module analyzer to look up the module
|
||||
# where the attribute documentation would actually be found in.
|
||||
@ -2358,17 +2381,14 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
return ret
|
||||
|
||||
# to distinguish classmethod/staticmethod
|
||||
obj = self.parent.__dict__.get(self.object_name)
|
||||
if obj is None:
|
||||
obj = self.object
|
||||
|
||||
obj_is_staticmethod = inspect.isstaticmethod(
|
||||
obj, cls=self.parent, name=self.object_name
|
||||
)
|
||||
if inspect.isclassmethod(obj) or obj_is_staticmethod:
|
||||
# document class and static members before ordinary ones
|
||||
self.member_order = self.member_order - 1
|
||||
|
||||
obj = self.parent.__dict__.get(self.object_name, self.object)
|
||||
if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name):
|
||||
# document static members before regular methods
|
||||
self.member_order -= 1
|
||||
elif inspect.isclassmethod(obj):
|
||||
# document class methods before static methods as
|
||||
# they usually behave as alternative constructors
|
||||
self.member_order -= 2
|
||||
return ret
|
||||
|
||||
def format_args(self, **kwargs: Any) -> str:
|
||||
|
@ -72,6 +72,14 @@ class Class:
|
||||
'moore', 9, 8, 7, docstring='moore(a, e, f) -> happiness'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def b_staticmeth():
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def a_staticmeth():
|
||||
pass
|
||||
|
||||
def __init__(self, arg):
|
||||
self.inst_attr_inline = None #: an inline documented instance attr
|
||||
#: a documented instance attribute
|
||||
|
@ -728,7 +728,9 @@ def test_autodoc_undoc_members(app):
|
||||
actual = do_autodoc(app, 'class', 'target.Class', options)
|
||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||
'.. py:class:: Class(arg)',
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.inst_attr_comment',
|
||||
@ -750,7 +752,9 @@ def test_autodoc_undoc_members(app):
|
||||
actual = do_autodoc(app, 'class', 'target.Class', options)
|
||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||
'.. py:class:: Class(arg)',
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.inst_attr_comment',
|
||||
@ -921,7 +925,9 @@ def test_autodoc_special_members(app):
|
||||
' .. py:method:: Class.__special1__()',
|
||||
' .. py:method:: Class.__special2__()',
|
||||
' .. py:attribute:: Class.__weakref__',
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.inst_attr_comment',
|
||||
@ -1200,6 +1206,8 @@ def test_autodoc_member_order(app):
|
||||
' .. py:attribute:: Class.mdocattr',
|
||||
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:attribute:: Class.inst_attr_inline',
|
||||
' .. py:attribute:: Class.inst_attr_comment',
|
||||
' .. py:attribute:: Class.inst_attr_string',
|
||||
@ -1216,10 +1224,15 @@ def test_autodoc_member_order(app):
|
||||
actual = do_autodoc(app, 'class', 'target.Class', options)
|
||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||
'.. py:class:: Class(arg)',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:method:: Class.meth()',
|
||||
# class methods
|
||||
' .. py:method:: Class.moore(a, e, f) -> happiness',
|
||||
' .. py:method:: Class.roger(a, *, b=2, c=3, d=4, e=5, f=6)',
|
||||
# static methods
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
# regular methods
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:method:: Class.meth()',
|
||||
' .. py:method:: Class.skipmeth()',
|
||||
' .. py:method:: Class.undocmeth()',
|
||||
' .. py:attribute:: Class._private_inst_attr',
|
||||
@ -1243,7 +1256,9 @@ def test_autodoc_member_order(app):
|
||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||
'.. py:class:: Class(arg)',
|
||||
' .. py:attribute:: Class._private_inst_attr',
|
||||
' .. py:method:: Class.a_staticmeth()',
|
||||
' .. py:attribute:: Class.attr',
|
||||
' .. py:method:: Class.b_staticmeth()',
|
||||
' .. py:attribute:: Class.docattr',
|
||||
' .. py:method:: Class.excludemeth()',
|
||||
' .. py:attribute:: Class.inst_attr_comment',
|
||||
|
Loading…
Reference in New Issue
Block a user