11from __future__ import annotations
22
3- import operator
43from typing import TYPE_CHECKING , NewType , TypeVar
54
65from docutils .statemachine import StringList
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
2019from sphinx .ext .autodoc ._renderer import _add_content , _directive_header_lines
2120from sphinx .ext .autodoc ._sentinels import ALL
2221from sphinx .ext .autodoc .importer import _load_object_by_name
2524from sphinx .pycode import ModuleAnalyzer
2625from sphinx .util import inspect , logging
2726from 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
3029if 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
632512class 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