Skip to content

Commit

Permalink
Merge pull request #8444 from tk0miya/8443_autodata_for_pep526
Browse files Browse the repository at this point in the history
Fix #8443: autodata directive does not support PEP-526 ivars
  • Loading branch information
tk0miya committed Nov 20, 2020
2 parents 90dad94 + dcd8ff5 commit 18b2707
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 30 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Deprecated
----------

* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
* ``sphinx.util.requests.is_ssl_error()``

Features added
Expand Down Expand Up @@ -42,6 +43,10 @@ Bugs fixed
is decorated
* #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables
and attributes
* #8443: autodoc: autodata directive can't create document for PEP-526 based
type annotated variables
* #8443: autodoc: autoattribute directive can't create document for PEP-526
based uninitalized variables
* #8419: html search: Do not load ``language_data.js`` in non-search pages

Testing
Expand Down
5 changes: 5 additions & 0 deletions doc/extdev/deprecated.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ The following is a list of deprecated interfaces.
- 5.0
- N/A

* - ``sphinx.ext.autodoc.DataDeclarationDocumenter``
- 3.4
- 5.0
- ``sphinx.ext.autodoc.DataDocumenter``

* - ``sphinx.util.requests.is_ssl_error()``
- 3.4
- 5.0
Expand Down
78 changes: 52 additions & 26 deletions sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,28 @@ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent:
) -> 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()
Expand Down Expand Up @@ -1723,6 +1745,13 @@ def get_real_modname(self) -> str:
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):
"""
Expand All @@ -1736,30 +1765,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):
Expand Down Expand Up @@ -2026,6 +2035,22 @@ def document_members(self, all_members: bool = False) -> None:

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()
Expand All @@ -2036,7 +2061,9 @@ def isinstanceattribute(self) -> bool:

return False
except PycodeError:
return False
pass

return False

def import_object(self, raiseerror: bool = False) -> bool:
try:
Expand Down Expand Up @@ -2272,7 +2299,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)
Expand Down
6 changes: 2 additions & 4 deletions sphinx/ext/autosummary/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ def emit_firstresult(self, *args: Any) -> None:


def setup_documenters(app: Any) -> None:
from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter,
DataDeclarationDocumenter, DataDocumenter,
from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, DataDocumenter,
DecoratorDocumenter, ExceptionDocumenter,
FunctionDocumenter, GenericAliasDocumenter,
InstanceAttributeDocumenter, MethodDocumenter,
Expand All @@ -96,8 +95,7 @@ def setup_documenters(app: Any) -> None:
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter,
SingledispatchFunctionDocumenter,
SlotsAttributeDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter,
] # type: List[Type[Documenter]]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
Expand Down
30 changes: 30 additions & 0 deletions tests/test_ext_autodoc_autoattribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details.
"""

import sys

import pytest
from test_ext_autodoc import do_autodoc

Expand Down Expand Up @@ -39,3 +41,31 @@ def test_autoattribute_novalue(app):
' should be documented -- süß',
'',
]


@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoattribute_typed_variable(app):
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Class.attr2')
assert list(actual) == [
'',
'.. py:attribute:: Class.attr2',
' :module: target.typed_vars',
' :type: int',
'',
]


@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoattribute_instance_variable(app):
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Class.attr4')
assert list(actual) == [
'',
'.. py:attribute:: Class.attr4',
' :module: target.typed_vars',
' :type: int',
'',
' attr4',
'',
]
33 changes: 33 additions & 0 deletions tests/test_ext_autodoc_autodata.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details.
"""

import sys

import pytest
from test_ext_autodoc import do_autodoc

Expand Down Expand Up @@ -39,3 +41,34 @@ def test_autodata_novalue(app):
' documentation for the integer',
'',
]


@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodata_typed_variable(app):
actual = do_autodoc(app, 'data', 'target.typed_vars.attr2')
assert list(actual) == [
'',
'.. py:data:: attr2',
' :module: target.typed_vars',
' :type: str',
'',
' attr2',
'',
]


@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodata_type_comment(app):
actual = do_autodoc(app, 'data', 'target.typed_vars.attr3')
assert list(actual) == [
'',
'.. py:data:: attr3',
' :module: target.typed_vars',
' :type: str',
" :value: ''",
'',
' attr3',
'',
]

0 comments on commit 18b2707

Please sign in to comment.