diff options
Diffstat (limited to 'sphinx/ext/autodoc/__init__.py')
-rw-r--r-- | sphinx/ext/autodoc/__init__.py | 144 |
1 files changed, 71 insertions, 73 deletions
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py index 73d3c75ef..a6cd850dc 100644 --- a/sphinx/ext/autodoc/__init__.py +++ b/sphinx/ext/autodoc/__init__.py @@ -16,7 +16,7 @@ import warnings from inspect import Parameter, Signature from types import ModuleType from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Sequence, - Set, Tuple, Type, TypeVar, Union, get_type_hints) + Set, Tuple, Type, TypeVar, Union) from docutils.statemachine import StringList @@ -33,7 +33,7 @@ from sphinx.util import inspect, logging from sphinx.util.docstrings import extract_metadata, prepare_docstring from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr, stringify_signature) -from sphinx.util.typing import restify +from sphinx.util.typing import get_type_hints, restify from sphinx.util.typing import stringify as stringify_typehint if TYPE_CHECKING: @@ -954,7 +954,7 @@ class ModuleDocumenter(Documenter): def __init__(self, *args: Any) -> None: super().__init__(*args) merge_members_option(self.options) - self.__all__ = None + self.__all__ = None # type: Optional[Sequence[str]] @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any @@ -978,26 +978,16 @@ class ModuleDocumenter(Documenter): return ret def import_object(self, raiseerror: bool = False) -> bool: - def is_valid_module_all(__all__: Any) -> bool: - """Check the given *__all__* is valid for a module.""" - if (isinstance(__all__, (list, tuple)) and - all(isinstance(e, str) for e in __all__)): - return True - else: - return False - ret = super().import_object(raiseerror) - if not self.options.ignore_module_all: - __all__ = getattr(self.object, '__all__', None) - if is_valid_module_all(__all__): - # valid __all__ found. copy it to self.__all__ - self.__all__ = __all__ - elif __all__: - # invalid __all__ found. - logger.warning(__('__all__ should be a list of strings, not %r ' - '(in module %s) -- ignoring __all__') % - (__all__, self.fullname), type='autodoc') + try: + if not self.options.ignore_module_all: + self.__all__ = inspect.getall(self.object) + except ValueError as exc: + # invalid __all__ found. + logger.warning(__('__all__ should be a list of strings, not %r ' + '(in module %s) -- ignoring __all__') % + (exc.args[0], self.fullname), type='autodoc') return ret @@ -1670,28 +1660,41 @@ class DataDocumenter(ModuleLevelDocumenter): priority = -10 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option @classmethod def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any ) -> bool: return isinstance(parent, ModuleDocumenter) and isattr + def import_object(self, raiseerror: bool = False) -> bool: + try: + return super().import_object(raiseerror=True) + except ImportError as exc: + # annotation only instance variable (PEP-526) + try: + self.parent = importlib.import_module(self.modname) + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + self.object = UNINITIALIZED_ATTR + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + def add_directive_header(self, sig: str) -> None: super().add_directive_header(sig) sourcename = self.get_sourcename() if not self.options.annotation: # obtain annotation for this data - try: - annotations = get_type_hints(self.parent) - except NameError: - # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) - annotations = safe_getattr(self.parent, '__annotations__', {}) - except TypeError: - annotations = {} - except KeyError: - # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) - annotations = {} - + annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases) if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) @@ -1702,7 +1705,7 @@ class DataDocumenter(ModuleLevelDocumenter): sourcename) try: - if self.object is UNINITIALIZED_ATTR: + if self.object is UNINITIALIZED_ATTR or self.options.no_value: pass else: objrepr = object_description(self.object) @@ -1722,6 +1725,13 @@ class DataDocumenter(ModuleLevelDocumenter): return self.get_attr(self.parent or self.object, '__module__', None) \ or self.modname + def add_content(self, more_content: Any, no_docstring: bool = False) -> None: + if self.object is UNINITIALIZED_ATTR: + # suppress docstring of the value + super().add_content(more_content, no_docstring=True) + else: + super().add_content(more_content, no_docstring=no_docstring) + class DataDeclarationDocumenter(DataDocumenter): """ @@ -1735,30 +1745,10 @@ class DataDeclarationDocumenter(DataDocumenter): # must be higher than AttributeDocumenter priority = 11 - @classmethod - def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any - ) -> bool: - """This documents only INSTANCEATTR members.""" - return (isinstance(parent, ModuleDocumenter) and - isattr and - member is INSTANCEATTR) - - def import_object(self, raiseerror: bool = False) -> bool: - """Never import anything.""" - # disguise as a data - self.objtype = 'data' - self.object = UNINITIALIZED_ATTR - try: - # import module to obtain type annotation - self.parent = importlib.import_module(self.modname) - except ImportError: - pass - - return True - - def add_content(self, more_content: Any, no_docstring: bool = False) -> None: - """Never try to get a docstring from the object.""" - super().add_content(more_content, no_docstring=True) + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn("%s is deprecated." % self.__class__.__name__, + RemovedInSphinx50Warning, stacklevel=2) + super().__init__(*args, **kwargs) class GenericAliasDocumenter(DataDocumenter): @@ -1998,6 +1988,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): member_order = 60 option_spec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option # must be higher than the MethodDocumenter, else it will recognize # some non-data descriptors as methods @@ -2024,6 +2015,22 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): def isinstanceattribute(self) -> bool: """Check the subject is an instance attribute.""" + # uninitialized instance variable (PEP-526) + with mock(self.config.autodoc_mock_imports): + try: + ret = import_object(self.modname, self.objpath[:-1], 'class', + attrgetter=self.get_attr, + warningiserror=self.config.autodoc_warningiserror) + self.parent = ret[3] + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + self.object = UNINITIALIZED_ATTR + return True + except ImportError: + pass + + # An instance variable defined inside __init__(). try: analyzer = ModuleAnalyzer.for_module(self.modname) attr_docs = analyzer.find_attr_docs() @@ -2034,7 +2041,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): return False except PycodeError: - return False + pass + + return False def import_object(self, raiseerror: bool = False) -> bool: try: @@ -2069,17 +2078,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): sourcename = self.get_sourcename() if not self.options.annotation: # obtain type annotation for this attribute - try: - annotations = get_type_hints(self.parent) - except NameError: - # Failed to evaluate ForwardRef (maybe TYPE_CHECKING) - annotations = safe_getattr(self.parent, '__annotations__', {}) - except TypeError: - annotations = {} - except KeyError: - # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084) - annotations = {} - + annotations = get_type_hints(self.parent, None, self.config.autodoc_type_aliases) if self.objpath[-1] in annotations: objrepr = stringify_typehint(annotations.get(self.objpath[-1])) self.add_line(' :type: ' + objrepr, sourcename) @@ -2092,7 +2091,7 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # data descriptors do not have useful values if not self._datadescriptor: try: - if self.object is INSTANCEATTR: + if self.object is INSTANCEATTR or self.options.no_value: pass else: objrepr = object_description(self.object) @@ -2244,8 +2243,8 @@ class SlotsAttributeDocumenter(AttributeDocumenter): % self.__class__.__name__, RemovedInSphinx50Warning, stacklevel=2) name = self.objpath[-1] - __slots__ = safe_getattr(self.parent, '__slots__', []) - if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str): + __slots__ = inspect.getslots(self.parent) + if __slots__ and isinstance(__slots__.get(name, None), str): docstring = prepare_docstring(__slots__[name]) return [docstring] else: @@ -2280,7 +2279,6 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) - app.add_autodocumenter(DataDeclarationDocumenter) app.add_autodocumenter(GenericAliasDocumenter) app.add_autodocumenter(TypeVarDocumenter) app.add_autodocumenter(FunctionDocumenter) |