Skip to content

Commit

Permalink
Merge branch '3.x' into 8454_graphviz_layout_option
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Nov 20, 2020
2 parents 0071409 + 18b2707 commit e8b7fd2
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 51 deletions.
6 changes: 6 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,11 @@ 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
* #8454: graphviz: The layout option for graph and digraph directives don't work

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
1 change: 0 additions & 1 deletion sphinx/builders/html/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,6 @@ def init_js_files(self) -> None:
self.add_js_file('jquery.js')
self.add_js_file('underscore.js')
self.add_js_file('doctools.js')
self.add_js_file('language_data.js')

for filename, attrs in self.app.registry.js_files:
self.add_js_file(filename, **attrs)
Expand Down
106 changes: 61 additions & 45 deletions sphinx/ext/autodoc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -961,7 +961,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
Expand All @@ -985,26 +985,16 @@ def parse_name(self) -> bool:
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

Expand Down Expand Up @@ -1697,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 @@ -1733,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 @@ -1746,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 @@ -2036,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 @@ -2046,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 @@ -2282,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
1 change: 1 addition & 0 deletions sphinx/themes/basic/search.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
{%- block scripts %}
{{ super() }}
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
<script src="{{ pathto('_static/language_data.js', 1) }}"></script>
{%- endblock %}
{% block extrahead %}
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
Expand Down
18 changes: 17 additions & 1 deletion sphinx/util/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from functools import partial, partialmethod
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
from io import StringIO
from typing import Any, Callable, Dict, List, Mapping, Optional, Tuple, cast
from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, cast

from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.pycode.ast import ast # for py35-37
Expand Down Expand Up @@ -137,6 +137,22 @@ def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
return obj


def getall(obj: Any) -> Optional[Sequence[str]]:
"""Get __all__ attribute of the module as dict.
Return None if given *obj* does not have __all__.
Raises ValueError if given *obj* have invalid __all__.
"""
__all__ = safe_getattr(obj, '__all__', None)
if __all__ is None:
return None
else:
if (isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__)):
return __all__
else:
raise ValueError(__all__)


def getslots(obj: Any) -> Optional[Dict]:
"""Get __slots__ attribute of the class as dict.
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 e8b7fd2

Please sign in to comment.