Merge branch '3.x' into 8437_safety_make_clean

This commit is contained in:
Takeshi KOMIYA
2020-11-21 01:56:07 +09:00
committed by GitHub
11 changed files with 161 additions and 55 deletions

View File

@@ -42,12 +42,10 @@ jobs:
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Set up Python ${{ matrix.python }} (deadsnakes) - name: Set up Python ${{ matrix.python }} (deadsnakes)
uses: deadsnakes/action@v1.0.0 uses: deadsnakes/action@v2.0.1
if: endsWith(matrix.python, '-dev') if: endsWith(matrix.python, '-dev')
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Check Python version - name: Check Python version
run: python --version run: python --version
- name: Install graphviz - name: Install graphviz

View File

@@ -14,6 +14,7 @@ Deprecated
---------- ----------
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()`` * The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
* ``sphinx.util.requests.is_ssl_error()`` * ``sphinx.util.requests.is_ssl_error()``
Features added Features added
@@ -42,6 +43,12 @@ Bugs fixed
is decorated is decorated
* #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables * #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables
and attributes 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
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous * #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
Testing Testing

View File

@@ -31,6 +31,11 @@ The following is a list of deprecated interfaces.
- 5.0 - 5.0
- N/A - N/A
* - ``sphinx.ext.autodoc.DataDeclarationDocumenter``
- 3.4
- 5.0
- ``sphinx.ext.autodoc.DataDocumenter``
* - ``sphinx.util.requests.is_ssl_error()`` * - ``sphinx.util.requests.is_ssl_error()``
- 3.4 - 3.4
- 5.0 - 5.0

View File

@@ -301,7 +301,6 @@ class StandaloneHTMLBuilder(Builder):
self.add_js_file('jquery.js') self.add_js_file('jquery.js')
self.add_js_file('underscore.js') self.add_js_file('underscore.js')
self.add_js_file('doctools.js') self.add_js_file('doctools.js')
self.add_js_file('language_data.js')
for filename, attrs in self.app.registry.js_files: for filename, attrs in self.app.registry.js_files:
self.add_js_file(filename, **attrs) self.add_js_file(filename, **attrs)

View File

@@ -961,7 +961,7 @@ class ModuleDocumenter(Documenter):
def __init__(self, *args: Any) -> None: def __init__(self, *args: Any) -> None:
super().__init__(*args) super().__init__(*args)
merge_members_option(self.options) merge_members_option(self.options)
self.__all__ = None self.__all__ = None # type: Optional[Sequence[str]]
@classmethod @classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
@@ -985,26 +985,16 @@ class ModuleDocumenter(Documenter):
return ret return ret
def import_object(self, raiseerror: bool = False) -> bool: 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) ret = super().import_object(raiseerror)
if not self.options.ignore_module_all: try:
__all__ = getattr(self.object, '__all__', None) if not self.options.ignore_module_all:
if is_valid_module_all(__all__): self.__all__ = inspect.getall(self.object)
# valid __all__ found. copy it to self.__all__ except ValueError as exc:
self.__all__ = __all__ # invalid __all__ found.
elif __all__: logger.warning(__('__all__ should be a list of strings, not %r '
# invalid __all__ found. '(in module %s) -- ignoring __all__') %
logger.warning(__('__all__ should be a list of strings, not %r ' (exc.args[0], self.fullname), type='autodoc')
'(in module %s) -- ignoring __all__') %
(__all__, self.fullname), type='autodoc')
return ret return ret
@@ -1697,6 +1687,28 @@ class DataDocumenter(ModuleLevelDocumenter):
) -> bool: ) -> bool:
return isinstance(parent, ModuleDocumenter) and isattr 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: def add_directive_header(self, sig: str) -> None:
super().add_directive_header(sig) super().add_directive_header(sig)
sourcename = self.get_sourcename() sourcename = self.get_sourcename()
@@ -1733,6 +1745,13 @@ class DataDocumenter(ModuleLevelDocumenter):
return self.get_attr(self.parent or self.object, '__module__', None) \ return self.get_attr(self.parent or self.object, '__module__', None) \
or self.modname 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): class DataDeclarationDocumenter(DataDocumenter):
""" """
@@ -1746,30 +1765,10 @@ class DataDeclarationDocumenter(DataDocumenter):
# must be higher than AttributeDocumenter # must be higher than AttributeDocumenter
priority = 11 priority = 11
@classmethod def __init__(self, *args: Any, **kwargs: Any) -> None:
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any warnings.warn("%s is deprecated." % self.__class__.__name__,
) -> bool: RemovedInSphinx50Warning, stacklevel=2)
"""This documents only INSTANCEATTR members.""" super().__init__(*args, **kwargs)
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)
class GenericAliasDocumenter(DataDocumenter): class GenericAliasDocumenter(DataDocumenter):
@@ -2036,6 +2035,22 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
def isinstanceattribute(self) -> bool: def isinstanceattribute(self) -> bool:
"""Check the subject is an instance attribute.""" """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: try:
analyzer = ModuleAnalyzer.for_module(self.modname) analyzer = ModuleAnalyzer.for_module(self.modname)
attr_docs = analyzer.find_attr_docs() attr_docs = analyzer.find_attr_docs()
@@ -2046,7 +2061,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
return False return False
except PycodeError: except PycodeError:
return False pass
return False
def import_object(self, raiseerror: bool = False) -> bool: def import_object(self, raiseerror: bool = False) -> bool:
try: try:
@@ -2282,7 +2299,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ClassDocumenter)
app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(ExceptionDocumenter)
app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(DataDocumenter)
app.add_autodocumenter(DataDeclarationDocumenter)
app.add_autodocumenter(GenericAliasDocumenter) app.add_autodocumenter(GenericAliasDocumenter)
app.add_autodocumenter(TypeVarDocumenter) app.add_autodocumenter(TypeVarDocumenter)
app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(FunctionDocumenter)

View File

@@ -85,8 +85,7 @@ AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
def setup_documenters(app: Any) -> None: def setup_documenters(app: Any) -> None:
from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, DataDocumenter,
DataDeclarationDocumenter, DataDocumenter,
DecoratorDocumenter, ExceptionDocumenter, DecoratorDocumenter, ExceptionDocumenter,
FunctionDocumenter, GenericAliasDocumenter, FunctionDocumenter, GenericAliasDocumenter,
InstanceAttributeDocumenter, MethodDocumenter, InstanceAttributeDocumenter, MethodDocumenter,
@@ -96,8 +95,7 @@ def setup_documenters(app: Any) -> None:
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter, FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter, SlotsAttributeDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter,
SingledispatchFunctionDocumenter,
] # type: List[Type[Documenter]] ] # type: List[Type[Documenter]]
for documenter in documenters: for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter) app.registry.add_documenter(documenter.objtype, documenter)

View File

@@ -182,7 +182,8 @@ class GraphvizSimple(SphinxDirective):
'alt': directives.unchanged, 'alt': directives.unchanged,
'align': align_spec, 'align': align_spec,
'caption': directives.unchanged, 'caption': directives.unchanged,
'graphviz_dot': directives.unchanged, 'layout': directives.unchanged,
'graphviz_dot': directives.unchanged, # an old alias of `layout` option
'name': directives.unchanged, 'name': directives.unchanged,
'class': directives.class_option, 'class': directives.class_option,
} }
@@ -194,6 +195,8 @@ class GraphvizSimple(SphinxDirective):
node['options'] = {'docname': self.env.docname} node['options'] = {'docname': self.env.docname}
if 'graphviz_dot' in self.options: if 'graphviz_dot' in self.options:
node['options']['graphviz_dot'] = self.options['graphviz_dot'] node['options']['graphviz_dot'] = self.options['graphviz_dot']
if 'layout' in self.options:
node['options']['graphviz_dot'] = self.options['layout']
if 'alt' in self.options: if 'alt' in self.options:
node['alt'] = self.options['alt'] node['alt'] = self.options['alt']
if 'align' in self.options: if 'align' in self.options:

View File

@@ -12,6 +12,7 @@
{%- block scripts %} {%- block scripts %}
{{ super() }} {{ super() }}
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script> <script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
<script src="{{ pathto('_static/language_data.js', 1) }}"></script>
{%- endblock %} {%- endblock %}
{% block extrahead %} {% block extrahead %}
<script src="{{ pathto('searchindex.js', 1) }}" defer></script> <script src="{{ pathto('searchindex.js', 1) }}" defer></script>

View File

@@ -20,7 +20,7 @@ import warnings
from functools import partial, partialmethod from functools import partial, partialmethod
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
from io import StringIO 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.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.pycode.ast import ast # for py35-37 from sphinx.pycode.ast import ast # for py35-37
@@ -137,6 +137,22 @@ def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
return obj 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]: def getslots(obj: Any) -> Optional[Dict]:
"""Get __slots__ attribute of the class as dict. """Get __slots__ attribute of the class as dict.

View File

@@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys
import pytest import pytest
from test_ext_autodoc import do_autodoc from test_ext_autodoc import do_autodoc
@@ -39,3 +41,31 @@ def test_autoattribute_novalue(app):
' should be documented -- süß', ' 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',
'',
]

View File

@@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details. :license: BSD, see LICENSE for details.
""" """
import sys
import pytest import pytest
from test_ext_autodoc import do_autodoc from test_ext_autodoc import do_autodoc
@@ -39,3 +41,34 @@ def test_autodata_novalue(app):
' documentation for the integer', ' 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',
'',
]