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:
python-version: ${{ matrix.python }}
- name: Set up Python ${{ matrix.python }} (deadsnakes)
uses: deadsnakes/action@v1.0.0
uses: deadsnakes/action@v2.0.1
if: endsWith(matrix.python, '-dev')
with:
python-version: ${{ matrix.python }}
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Check Python version
run: python --version
- name: Install graphviz

View File

@@ -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
@@ -42,6 +43,12 @@ 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
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
Testing

View File

@@ -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

View File

@@ -301,7 +301,6 @@ class StandaloneHTMLBuilder(Builder):
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)

View File

@@ -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
@@ -985,26 +985,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
@@ -1697,6 +1687,28 @@ class DataDocumenter(ModuleLevelDocumenter):
) -> 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()
@@ -1733,6 +1745,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):
"""
@@ -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):
@@ -2036,6 +2035,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()
@@ -2046,7 +2061,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
return False
except PycodeError:
return False
pass
return False
def import_object(self, raiseerror: bool = False) -> bool:
try:
@@ -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)

View File

@@ -85,8 +85,7 @@ AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
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,
@@ -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)

View File

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

View File

@@ -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>

View File

@@ -20,7 +20,7 @@ import warnings
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
@@ -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.

View File

@@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details.
"""
import sys
import pytest
from test_ext_autodoc import do_autodoc
@@ -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',
'',
]

View File

@@ -9,6 +9,8 @@
:license: BSD, see LICENSE for details.
"""
import sys
import pytest
from test_ext_autodoc import do_autodoc
@@ -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',
'',
]