summaryrefslogtreecommitdiff
path: root/sphinx/ext/autodoc/__init__.py
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-21 01:01:36 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-11-21 01:01:36 +0900
commit3a4ae2092a6f236e2d71fdedb4b66f594e30d4e7 (patch)
treeafa4f774e0f8660d5ce247f01cace9deaa848d58 /sphinx/ext/autodoc/__init__.py
parentf3a6004f822b05e352372a77f449332ad230d21e (diff)
parent18b2707b2ac621f23f8ee6a7ca19bf1590be7826 (diff)
downloadsphinx-git-3a4ae2092a6f236e2d71fdedb4b66f594e30d4e7.tar.gz
Merge branch '3.x'
Diffstat (limited to 'sphinx/ext/autodoc/__init__.py')
-rw-r--r--sphinx/ext/autodoc/__init__.py144
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)