|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -import operator |
4 | 3 | from typing import TYPE_CHECKING, NewType, TypeVar |
5 | 4 |
|
6 | 5 | from docutils.statemachine import StringList |
|
16 | 15 | member_order_option, |
17 | 16 | members_option, |
18 | 17 | ) |
19 | | -from sphinx.ext.autodoc._member_finder import _filter_members, _get_members_to_document |
| 18 | +from sphinx.ext.autodoc._member_finder import _document_members |
20 | 19 | from sphinx.ext.autodoc._renderer import _add_content, _directive_header_lines |
21 | | -from sphinx.ext.autodoc._sentinels import ALL |
22 | 20 | from sphinx.ext.autodoc.importer import _load_object_by_name |
23 | 21 | from sphinx.ext.autodoc.mock import ismock |
24 | 22 | from sphinx.locale import _, __ |
25 | 23 | from sphinx.pycode import ModuleAnalyzer |
26 | 24 | from sphinx.util import inspect, logging |
27 | 25 | from sphinx.util.inspect import safe_getattr |
28 | | -from sphinx.util.typing import AnyTypeAliasType, restify, stringify_annotation |
| 26 | +from sphinx.util.typing import restify, stringify_annotation |
29 | 27 |
|
30 | 28 | if TYPE_CHECKING: |
31 | 29 | from collections.abc import Iterator |
@@ -373,46 +371,6 @@ def _assemble_more_content( |
373 | 371 |
|
374 | 372 | return more_content |
375 | 373 |
|
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 | | - |
416 | 374 | def generate( |
417 | 375 | self, |
418 | 376 | more_content: StringList | None = None, |
@@ -475,8 +433,8 @@ def _generate( |
475 | 433 | if self.real_modname != guess_modname: |
476 | 434 | # Add module to dependency list if target object is defined in other module. |
477 | 435 | try: |
478 | | - analyzer = ModuleAnalyzer.for_module(guess_modname) |
479 | | - self.directive.record_dependencies.add(analyzer.srcname) |
| 436 | + srcname = ModuleAnalyzer.for_module(guess_modname).srcname |
| 437 | + self.directive.record_dependencies.add(srcname) |
480 | 438 | except PycodeError: |
481 | 439 | pass |
482 | 440 |
|
@@ -512,121 +470,25 @@ def _generate( |
512 | 470 | self.add_content(more_content, indent=indent) |
513 | 471 |
|
514 | 472 | # document members, if possible |
515 | | - has_members = isinstance(self, ModuleDocumenter) or ( |
516 | | - isinstance(self, ClassDocumenter) and not self.props.doc_as_attr |
517 | | - ) |
518 | | - if has_members: |
519 | | - want_all = bool( |
520 | | - all_members |
521 | | - or self.options.inherited_members |
522 | | - or self.options.members is ALL |
523 | | - ) |
524 | | - member_documenters = self._gather_members(want_all=want_all, indent=indent) |
525 | | - # for implicit module members, check __module__ to avoid |
526 | | - # documenting imported objects |
527 | | - members_check_module = bool( |
528 | | - isinstance(self, ModuleDocumenter) |
529 | | - and want_all |
530 | | - and (self.options.ignore_module_all or self.props.all is None) |
531 | | - ) |
532 | | - _document_members( |
533 | | - member_documenters=member_documenters, |
534 | | - real_modname=self.real_modname, |
535 | | - members_check_module=members_check_module, |
536 | | - ) |
537 | | - |
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, |
| 473 | + analyzer = self.analyzer |
| 474 | + if analyzer is not None: |
| 475 | + analyzer.analyze() |
| 476 | + _document_members( |
| 477 | + all_members=all_members, |
| 478 | + analyzer_order=analyzer.tagorder if analyzer is not None else {}, |
| 479 | + attr_docs=analyzer.attr_docs if analyzer is not None else {}, |
| 480 | + config=self.config, |
| 481 | + current_document=self._current_document, |
| 482 | + directive=self.directive, |
| 483 | + events=self._events, |
581 | 484 | get_attr=self.get_attr, |
582 | | - inherit_docstrings=self.config.autodoc_inherit_docstrings, |
| 485 | + indent=indent, |
| 486 | + name=self.name, |
583 | 487 | 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, |
| 488 | + props=self.props, |
| 489 | + real_modname=self.real_modname, |
| 490 | + registry=self.env._registry, |
592 | 491 | ) |
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 | 492 |
|
631 | 493 |
|
632 | 494 | class ModuleDocumenter(Documenter): |
@@ -765,104 +627,3 @@ def autodoc_attrgetter( |
765 | 627 | return func(obj, name, *defargs) |
766 | 628 |
|
767 | 629 | return safe_getattr(obj, name, *defargs) |
768 | | - |
769 | | - |
770 | | -def _document_members( |
771 | | - *, |
772 | | - member_documenters: list[tuple[Documenter, bool]], |
773 | | - real_modname: str, |
774 | | - members_check_module: bool, |
775 | | -) -> None: |
776 | | - """Generate reST for member documentation. |
777 | | -
|
778 | | - If *all_members* is True, document all members, else those given by |
779 | | - *self.options.members*. |
780 | | - """ |
781 | | - for documenter, is_attr in member_documenters: |
782 | | - assert documenter.props.module_name |
783 | | - # We can directly call ._generate() since the documenters |
784 | | - # already called ``_load_object_by_name()`` before. |
785 | | - # |
786 | | - # Note that those two methods above do not emit events, so |
787 | | - # whatever objects we deduced should not have changed. |
788 | | - documenter._generate( |
789 | | - all_members=True, |
790 | | - real_modname=real_modname, |
791 | | - check_module=members_check_module and not is_attr, |
792 | | - ) |
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