Merge pull request #7686 from tk0miya/7685_autosummary_imported_members

Fix #7685: autosummary: imported members are listed unexpectedly
This commit is contained in:
Takeshi KOMIYA 2020-05-21 02:07:44 +09:00 committed by GitHub
commit 74351b2d43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 154 additions and 4 deletions

View File

@ -101,6 +101,8 @@ Bugs fixed
* #7551: autosummary: a nested class is indexed as non-nested class
* #7661: autosummary: autosummary directive emits warnings twices if failed to
import the target module
* #7685: autosummary: The template variable "members" contains imported members
even if :confval:`autossummary_imported_members` is False
* #7535: sphinx-autogen: crashes when custom template uses inheritance
* #7536: sphinx-autogen: crashes when template uses i18n feature
* #7653: sphinx-quickstart: Fix multiple directory creation for nested relpath

View File

@ -18,6 +18,7 @@
"""
import argparse
import inspect
import locale
import os
import pkgutil
@ -176,6 +177,56 @@ class AutosummaryRenderer:
# -- Generating output ---------------------------------------------------------
class ModuleScanner:
def __init__(self, app: Any, obj: Any) -> None:
self.app = app
self.object = obj
def get_object_type(self, name: str, value: Any) -> str:
return get_documenter(self.app, value, self.object).objtype
def is_skipped(self, name: str, value: Any, objtype: str) -> bool:
try:
return self.app.emit_firstresult('autodoc-skip-member', objtype,
name, value, False, {})
except Exception as exc:
logger.warning(__('autosummary: failed to determine %r to be documented, '
'the following exception was raised:\n%s'),
name, exc, type='autosummary')
return False
def scan(self, imported_members: bool) -> List[str]:
members = []
for name in dir(self.object):
try:
value = safe_getattr(self.object, name)
except AttributeError:
value = None
objtype = self.get_object_type(name, value)
if self.is_skipped(name, value, objtype):
continue
try:
if inspect.ismodule(value):
imported = True
elif safe_getattr(value, '__module__') != self.object.__name__:
imported = True
else:
imported = False
except AttributeError:
imported = False
if imported_members:
# list all members up
members.append(name)
elif imported is False:
# list not-imported members up
members.append(name)
return members
def generate_autosummary_content(name: str, obj: Any, parent: Any,
template: AutosummaryRenderer, template_name: str,
imported_members: bool, app: Any,
@ -246,7 +297,8 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
ns.update(context)
if doc.objtype == 'module':
ns['members'] = dir(obj)
scanner = ModuleScanner(app, obj)
ns['members'] = scanner.scan(imported_members)
ns['functions'], ns['all_functions'] = \
get_members(obj, {'function'}, imported=imported_members)
ns['classes'], ns['all_classes'] = \

View File

@ -1,4 +1,4 @@
from os import * # NOQA
from os import path # NOQA
from typing import Union
@ -17,7 +17,23 @@ class Foo:
pass
def bar(x: Union[int, str], y: int = 1):
class _Baz:
pass
def bar(x: Union[int, str], y: int = 1) -> None:
pass
def _quux():
pass
class Exc(Exception):
pass
class _Exc(Exception):
pass

View File

@ -19,7 +19,10 @@ from sphinx import addnodes
from sphinx.ext.autosummary import (
autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary
)
from sphinx.ext.autosummary.generate import AutosummaryEntry, generate_autosummary_docs, main as autogen_main
from sphinx.ext.autosummary.generate import (
AutosummaryEntry, generate_autosummary_content, generate_autosummary_docs,
main as autogen_main
)
from sphinx.testing.util import assert_node, etree_parse
from sphinx.util.docutils import new_document
from sphinx.util.osutil import cd
@ -189,6 +192,83 @@ def test_escaping(app, status, warning):
assert str_content(title) == 'underscore_module_'
@pytest.mark.sphinx(testroot='ext-autosummary')
def test_autosummary_generate_content_for_module(app):
import autosummary_dummy_module
template = Mock()
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
template, None, False, app, False, {})
assert template.render.call_args[0][0] == 'module'
context = template.render.call_args[0][1]
assert context['members'] == ['Exc', 'Foo', '_Baz', '_Exc', '__builtins__',
'__cached__', '__doc__', '__file__', '__name__',
'__package__', '_quux', 'bar', 'qux']
assert context['functions'] == ['bar']
assert context['all_functions'] == ['_quux', 'bar']
assert context['classes'] == ['Foo']
assert context['all_classes'] == ['Foo', '_Baz']
assert context['exceptions'] == ['Exc']
assert context['all_exceptions'] == ['Exc', '_Exc']
assert context['attributes'] == ['qux']
assert context['all_attributes'] == ['qux']
assert context['fullname'] == 'autosummary_dummy_module'
assert context['module'] == 'autosummary_dummy_module'
assert context['objname'] == ''
assert context['name'] == ''
assert context['objtype'] == 'module'
@pytest.mark.sphinx(testroot='ext-autosummary')
def test_autosummary_generate_content_for_module_skipped(app):
import autosummary_dummy_module
template = Mock()
def skip_member(app, what, name, obj, skip, options):
if name in ('Foo', 'bar', 'Exc'):
return True
app.connect('autodoc-skip-member', skip_member)
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
template, None, False, app, False, {})
context = template.render.call_args[0][1]
assert context['members'] == ['_Baz', '_Exc', '__builtins__', '__cached__', '__doc__',
'__file__', '__name__', '__package__', '_quux', 'qux']
assert context['functions'] == []
assert context['classes'] == []
assert context['exceptions'] == []
@pytest.mark.sphinx(testroot='ext-autosummary')
def test_autosummary_generate_content_for_module_imported_members(app):
import autosummary_dummy_module
template = Mock()
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
template, None, True, app, False, {})
assert template.render.call_args[0][0] == 'module'
context = template.render.call_args[0][1]
assert context['members'] == ['Exc', 'Foo', 'Union', '_Baz', '_Exc', '__builtins__',
'__cached__', '__doc__', '__file__', '__loader__',
'__name__', '__package__', '__spec__', '_quux',
'bar', 'path', 'qux']
assert context['functions'] == ['bar']
assert context['all_functions'] == ['_quux', 'bar']
assert context['classes'] == ['Foo']
assert context['all_classes'] == ['Foo', '_Baz']
assert context['exceptions'] == ['Exc']
assert context['all_exceptions'] == ['Exc', '_Exc']
assert context['attributes'] == ['qux']
assert context['all_attributes'] == ['qux']
assert context['fullname'] == 'autosummary_dummy_module'
assert context['module'] == 'autosummary_dummy_module'
assert context['objname'] == ''
assert context['name'] == ''
assert context['objtype'] == 'module'
@pytest.mark.sphinx('dummy', testroot='ext-autosummary')
def test_autosummary_generate(app, status, warning):
app.builder.build_all()