Skip to content

Commit 0a0250d

Browse files
committed
Extract _gather_members()
1 parent 3dec862 commit 0a0250d

File tree

3 files changed

+258
-219
lines changed

3 files changed

+258
-219
lines changed

sphinx/ext/autodoc/_documenters.py

Lines changed: 17 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import operator
43
from typing import TYPE_CHECKING, NewType, TypeVar
54

65
from docutils.statemachine import StringList
@@ -16,7 +15,7 @@
1615
member_order_option,
1716
members_option,
1817
)
19-
from sphinx.ext.autodoc._member_finder import _filter_members, _get_members_to_document
18+
from sphinx.ext.autodoc._member_finder import _gather_members
2019
from sphinx.ext.autodoc._renderer import _add_content, _directive_header_lines
2120
from sphinx.ext.autodoc._sentinels import ALL
2221
from sphinx.ext.autodoc.importer import _load_object_by_name
@@ -25,7 +24,7 @@
2524
from sphinx.pycode import ModuleAnalyzer
2625
from sphinx.util import inspect, logging
2726
from sphinx.util.inspect import safe_getattr
28-
from sphinx.util.typing import AnyTypeAliasType, restify, stringify_annotation
27+
from sphinx.util.typing import restify, stringify_annotation
2928

3029
if TYPE_CHECKING:
3130
from collections.abc import Iterator
@@ -373,46 +372,6 @@ def _assemble_more_content(
373372

374373
return more_content
375374

376-
def sort_members(
377-
self, documenters: list[tuple[Documenter, bool]], order: str
378-
) -> list[tuple[Documenter, bool]]:
379-
"""Sort the given member list."""
380-
if order == 'groupwise':
381-
# sort by group; alphabetically within groups
382-
documenters.sort(key=lambda e: (e[0].props._groupwise_order_key, e[0].name))
383-
elif order == 'bysource':
384-
if (
385-
isinstance(self, ModuleDocumenter)
386-
and not self.options.ignore_module_all
387-
and (module_all := self.props.all)
388-
):
389-
# Sort by __all__
390-
module_all_idx = {name: idx for idx, name in enumerate(module_all)}
391-
module_all_len = len(module_all)
392-
393-
def key_func(entry: tuple[Documenter, bool]) -> int:
394-
fullname = entry[0].name.split('::')[1]
395-
return module_all_idx.get(fullname, module_all_len)
396-
397-
documenters.sort(key=key_func)
398-
399-
# By default, member discovery order matches source order,
400-
# as dicts are insertion-ordered from Python 3.7.
401-
elif self.analyzer is not None:
402-
# sort by source order, by virtue of the module analyzer
403-
tagorder = self.analyzer.tagorder
404-
tagorder_len = len(tagorder)
405-
406-
def key_func(entry: tuple[Documenter, bool]) -> int:
407-
fullname = entry[0].name.split('::')[1]
408-
return tagorder.get(fullname, tagorder_len)
409-
410-
documenters.sort(key=key_func)
411-
else: # alphabetical
412-
documenters.sort(key=lambda e: e[0].name)
413-
414-
return documenters
415-
416375
def generate(
417376
self,
418377
more_content: StringList | None = None,
@@ -521,7 +480,21 @@ def _generate(
521480
or self.options.inherited_members
522481
or self.options.members is ALL
523482
)
524-
member_documenters = self._gather_members(want_all=want_all, indent=indent)
483+
member_documenters = _gather_members(
484+
want_all=want_all,
485+
indent=indent,
486+
analyzer=self.analyzer,
487+
config=self.config,
488+
current_document=self._current_document,
489+
directive=self.directive,
490+
events=self._events,
491+
get_attr=self.get_attr,
492+
name=self.name,
493+
options=self.options,
494+
props=self.props,
495+
registry=self.env._registry,
496+
)
497+
525498
# for implicit module members, check __module__ to avoid
526499
# documenting imported objects
527500
members_check_module = bool(
@@ -535,99 +508,6 @@ def _generate(
535508
members_check_module=members_check_module,
536509
)
537510

538-
def _gather_members(
539-
self, *, want_all: bool, indent: str
540-
) -> list[tuple[Documenter, bool]]:
541-
"""Generate reST for member documentation.
542-
543-
If *want_all* is True, document all members, else those given by
544-
*self.options.members*.
545-
"""
546-
if not isinstance(self, (ModuleDocumenter, ClassDocumenter)):
547-
msg = 'must be implemented in subclasses'
548-
raise NotImplementedError(msg)
549-
550-
current_document = self._current_document
551-
events = self._events
552-
registry = self.env._registry
553-
props = self.props
554-
indent += ' ' * (props.obj_type != 'module')
555-
556-
# set current namespace for finding members
557-
current_document.autodoc_module = props.module_name
558-
if props.parts:
559-
current_document.autodoc_class = props.parts[0]
560-
561-
inherited_members = frozenset(self.options.inherited_members or ())
562-
if self.analyzer:
563-
self.analyzer.analyze()
564-
attr_docs = self.analyzer.attr_docs
565-
else:
566-
attr_docs = {}
567-
found_members = _get_members_to_document(
568-
want_all=want_all,
569-
get_attr=self.get_attr,
570-
inherit_docstrings=self.config.autodoc_inherit_docstrings,
571-
props=props,
572-
opt_members=self.options.members or (),
573-
inherited_members=inherited_members,
574-
ignore_module_all=bool(self.options.ignore_module_all),
575-
attr_docs=attr_docs,
576-
)
577-
filtered_members = _filter_members(
578-
found_members,
579-
want_all=want_all,
580-
events=events,
581-
get_attr=self.get_attr,
582-
inherit_docstrings=self.config.autodoc_inherit_docstrings,
583-
options=self.options,
584-
orig_name=self.name,
585-
props=props,
586-
inherited_members=inherited_members,
587-
exclude_members=self.options.exclude_members,
588-
special_members=self.options.special_members,
589-
private_members=self.options.private_members,
590-
undoc_members=self.options.undoc_members,
591-
attr_docs=attr_docs,
592-
)
593-
# document non-skipped members
594-
member_documenters: list[tuple[Documenter, bool]] = []
595-
for member_name, member, is_attr in filtered_members:
596-
# prefer the documenter with the highest priority
597-
obj_type = _best_object_type_for_member(
598-
member=member,
599-
member_name=member_name,
600-
is_attr=is_attr,
601-
parent_obj_type=self.objtype,
602-
parent_props=self.props,
603-
)
604-
if not obj_type:
605-
# don't know how to document this member
606-
continue
607-
doccls = registry.documenters[obj_type]
608-
# give explicitly separated module name, so that members
609-
# of inner classes can be documented
610-
module_prefix = f'{props.module_name}::'
611-
full_mname = module_prefix + '.'.join((*props.parts, member_name))
612-
documenter = doccls(self.directive, full_mname, indent)
613-
614-
# We now try to import all objects before ordering them. This is to
615-
# avoid possible circular imports if we were to import objects after
616-
# their associated documenters have been sorted.
617-
if documenter._load_object_by_name() is None:
618-
continue
619-
620-
member_documenters.append((documenter, is_attr))
621-
622-
member_order = self.options.member_order or self.config.autodoc_member_order
623-
member_documenters = self.sort_members(member_documenters, member_order)
624-
625-
# reset current objects
626-
current_document.autodoc_module = ''
627-
current_document.autodoc_class = ''
628-
629-
return member_documenters
630-
631511

632512
class ModuleDocumenter(Documenter):
633513
"""Specialized Documenter subclass for modules."""
@@ -790,79 +670,3 @@ def _document_members(
790670
real_modname=real_modname,
791671
check_module=members_check_module and not is_attr,
792672
)
793-
794-
795-
def _best_object_type_for_member(
796-
member: Any,
797-
member_name: str,
798-
is_attr: bool,
799-
*,
800-
parent_obj_type: str,
801-
parent_props: _ItemProperties | None,
802-
) -> str | None:
803-
"""Return the best object type that supports documenting *member*."""
804-
filtered = []
805-
806-
# Don't document submodules automatically: 'module' is never returned.
807-
808-
try:
809-
if isinstance(member, type) and issubclass(member, BaseException):
810-
# priority must be higher than 'class'
811-
filtered.append((20, 'exception'))
812-
except TypeError as exc:
813-
# It's possible for a member to be considered a type, but fail
814-
# issubclass checks due to not being a class. For example:
815-
# https://github.com/sphinx-doc/sphinx/issues/11654#issuecomment-1696790436
816-
msg = f'Failed to discern if member {member} is a BaseException subclass.'
817-
raise ValueError(msg) from exc
818-
819-
if isinstance(member, type) or (is_attr and isinstance(member, (NewType, TypeVar))):
820-
# priority must be higher than 'function', 'class', and 'attribute'
821-
# as NewType can be an attribute and is a class after Python 3.10.
822-
filtered.append((15, 'class'))
823-
824-
if parent_obj_type in {'class', 'exception'}:
825-
if inspect.isproperty(member):
826-
# priority must be higher than 'attribute'
827-
filtered.append((11, 'property'))
828-
829-
# See _get_documenter() in autosummary, parent_props might be None.
830-
elif parent_props is not None:
831-
# Support for class properties. Note: these only work on Python 3.9.
832-
__dict__ = safe_getattr(parent_props._obj, '__dict__', {})
833-
obj = __dict__.get(member_name)
834-
if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__):
835-
# priority must be higher than 'attribute'
836-
filtered.append((11, 'property'))
837-
838-
if parent_obj_type != 'module':
839-
if inspect.isattributedescriptor(member) or not (
840-
inspect.isroutine(member) or isinstance(member, type)
841-
):
842-
# priority must be higher than 'method', else it will recognise
843-
# some non-data descriptors as methods
844-
filtered.append((10, 'attribute'))
845-
846-
if inspect.isroutine(member) and parent_obj_type != 'module':
847-
# priority must be higher than 'function'
848-
filtered.append((1, 'method'))
849-
850-
if (
851-
inspect.isfunction(member)
852-
or inspect.isbuiltin(member)
853-
or (inspect.isroutine(member) and parent_obj_type == 'module')
854-
):
855-
# supports functions, builtins and bound methods exported
856-
# at the module level
857-
filtered.extend(((0, 'function'), (-1, 'decorator')))
858-
859-
if isinstance(member, AnyTypeAliasType):
860-
filtered.append((0, 'type'))
861-
862-
if parent_obj_type == 'module' and is_attr:
863-
filtered.append((-10, 'data'))
864-
865-
if filtered:
866-
# return the highest priority object type
867-
return max(filtered, key=operator.itemgetter(0))[1]
868-
return None

0 commit comments

Comments
 (0)