From 18973f6c1b985200dbf3f8c7b27c2416892cee5b Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Thu, 17 Dec 2020 02:55:05 +0900 Subject: [PATCH] refactor: autodoc: Add get_class_members() To enhance ClassDocumenter, Add a new helper function get_class_members(). At this moment, it is a copy of get_object_members(). It will be changed to return more detailed information of class members. --- sphinx/ext/autodoc/__init__.py | 5 +-- sphinx/ext/autodoc/importer.py | 64 ++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 9ae115ef4..99aff05cb 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -26,7 +26,8 @@ from sphinx.config import ENUM, Config from sphinx.deprecation import (RemovedInSphinx40Warning, RemovedInSphinx50Warning, RemovedInSphinx60Warning) from sphinx.environment import BuildEnvironment -from sphinx.ext.autodoc.importer import get_module_members, get_object_members, import_object +from sphinx.ext.autodoc.importer import (get_class_members, get_module_members, + get_object_members, import_object) from sphinx.ext.autodoc.mock import mock from sphinx.locale import _, __ from sphinx.pycode import ModuleAnalyzer, PycodeError @@ -1576,7 +1577,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename) def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: - members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer) + members = get_class_members(self.object, self.objpath, self.get_attr, self.analyzer) if not want_all: if not self.options.members: return False, [] # type: ignore diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index ec05a8a3d..b9858f049 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -241,6 +241,70 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, return members +def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable, + analyzer: ModuleAnalyzer = None) -> Dict[str, Attribute]: + """Get members and attributes of target class.""" + from sphinx.ext.autodoc import INSTANCEATTR + + # the members directly defined in the class + obj_dict = attrgetter(subject, '__dict__', {}) + + members = {} # type: Dict[str, Attribute] + + # enum members + if isenumclass(subject): + for name, value in subject.__members__.items(): + if name not in members: + members[name] = Attribute(name, True, value) + + superclass = subject.__mro__[1] + for name in obj_dict: + if name not in superclass.__dict__: + value = safe_getattr(subject, name) + members[name] = Attribute(name, True, value) + + # members in __slots__ + try: + __slots__ = getslots(subject) + if __slots__: + from sphinx.ext.autodoc import SLOTSATTR + + for name in __slots__: + members[name] = Attribute(name, True, SLOTSATTR) + except (AttributeError, TypeError, ValueError): + pass + + # other members + for name in dir(subject): + try: + value = attrgetter(subject, name) + directly_defined = name in obj_dict + name = unmangle(subject, name) + if name and name not in members: + members[name] = Attribute(name, directly_defined, value) + except AttributeError: + continue + + # annotation only member (ex. attr: int) + for i, cls in enumerate(getmro(subject)): + try: + for name in getannotations(cls): + name = unmangle(cls, name) + if name and name not in members: + members[name] = Attribute(name, i == 0, INSTANCEATTR) + except AttributeError: + pass + + if analyzer: + # append instance attributes (cf. self.attr1) if analyzer knows + namespace = '.'.join(objpath) + for (ns, name) in analyzer.find_attr_docs(): + if namespace == ns and name not in members: + members[name] = Attribute(name, True, INSTANCEATTR) + + return members + + from sphinx.ext.autodoc.mock import (MockFinder, MockLoader, _MockModule, _MockObject, # NOQA mock)