Fix #9883: autodoc: doccomment for the alias to mocked object was ignored

This commit is contained in:
Takeshi KOMIYA 2021-11-26 01:35:40 +09:00
parent f780210b6a
commit f88ac53e51
6 changed files with 34 additions and 10 deletions

View File

@ -23,7 +23,8 @@ Features added
Bugs fixed Bugs fixed
---------- ----------
* #9866: autodoc: doccoment for the imported class was ignored * #9866: autodoc: doccomment for the imported class was ignored
* #9883: autodoc: doccomment for the alias to mocked object was ignored
* #9878: mathjax: MathJax configuration is placed after loading MathJax itself * #9878: mathjax: MathJax configuration is placed after loading MathJax itself
* #9857: Generated RFC links use outdated base url * #9857: Generated RFC links use outdated base url

View File

@ -751,7 +751,7 @@ class Documenter:
isprivate = membername.startswith('_') isprivate = membername.startswith('_')
keep = False keep = False
if ismock(member): if ismock(member) and (namespace, membername) not in attr_docs:
# mocked module or object # mocked module or object
pass pass
elif self.options.exclude_members and membername in self.options.exclude_members: elif self.options.exclude_members and membername in self.options.exclude_members:
@ -2009,7 +2009,8 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
self.add_line(' :type: ' + objrepr, sourcename) self.add_line(' :type: ' + objrepr, sourcename)
try: try:
if self.options.no_value or self.should_suppress_value_header(): if (self.options.no_value or self.should_suppress_value_header() or
ismock(self.object)):
pass pass
else: else:
objrepr = object_description(self.object) objrepr = object_description(self.object)
@ -2528,11 +2529,11 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
@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
) -> bool: ) -> bool:
if inspect.isattributedescriptor(member): if isinstance(parent, ModuleDocumenter):
return False
elif inspect.isattributedescriptor(member):
return True return True
elif (not isinstance(parent, ModuleDocumenter) and elif not inspect.isroutine(member) and not isinstance(member, type):
not inspect.isroutine(member) and
not isinstance(member, type)):
return True return True
else: else:
return False return False
@ -2625,7 +2626,8 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
self.add_line(' :type: ' + objrepr, sourcename) self.add_line(' :type: ' + objrepr, sourcename)
try: try:
if self.options.no_value or self.should_suppress_value_header(): if (self.options.no_value or self.should_suppress_value_header() or
ismock(self.object)):
pass pass
else: else:
objrepr = object_description(self.object) objrepr = object_description(self.object)

View File

@ -170,7 +170,8 @@ def ismock(subject: Any) -> bool:
try: try:
# check the object is mocked object # check the object is mocked object
__mro__ = safe_getattr(type(subject), '__mro__', []) __mro__ = safe_getattr(type(subject), '__mro__', [])
if len(__mro__) > 2 and __mro__[1] is _MockObject: if len(__mro__) > 2 and __mro__[-2] is _MockObject:
# A mocked object has a MRO that ends with (..., _MockObject, object).
return True return True
except AttributeError: except AttributeError:
pass pass

View File

@ -22,6 +22,10 @@ def func(arg: missing_module.Class):
class TestAutodoc(object): class TestAutodoc(object):
"""TestAutodoc docstring.""" """TestAutodoc docstring."""
#: docstring
Alias = missing_module2.Class
@missing_name @missing_name
def decoratedMethod(self): def decoratedMethod(self):
"""TestAutodoc::decoratedMethod docstring""" """TestAutodoc::decoratedMethod docstring"""
@ -34,3 +38,6 @@ class Inherited(missing_module.Class):
sphinx.missing_module4.missing_function(len(missing_name2)) sphinx.missing_module4.missing_function(len(missing_name2))
#: docstring
Alias = missing_module2.Class

View File

@ -536,7 +536,7 @@ def test_mocked_module_imports(app, warning):
sys.modules.pop('target', None) # unload target module to clear the module cache sys.modules.pop('target', None) # unload target module to clear the module cache
# no autodoc_mock_imports # no autodoc_mock_imports
options = {"members": 'TestAutodoc,decoratedFunction,func'} options = {"members": 'TestAutodoc,decoratedFunction,func,Alias'}
actual = do_autodoc(app, 'module', 'target.need_mocks', options) actual = do_autodoc(app, 'module', 'target.need_mocks', options)
assert list(actual) == [] assert list(actual) == []
assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue()
@ -557,12 +557,24 @@ def test_mocked_module_imports(app, warning):
'.. py:module:: target.need_mocks', '.. py:module:: target.need_mocks',
'', '',
'', '',
'.. py:data:: Alias',
' :module: target.need_mocks',
'',
' docstring',
'',
'',
'.. py:class:: TestAutodoc()', '.. py:class:: TestAutodoc()',
' :module: target.need_mocks', ' :module: target.need_mocks',
'', '',
' TestAutodoc docstring.', ' TestAutodoc docstring.',
'', '',
'', '',
' .. py:attribute:: TestAutodoc.Alias',
' :module: target.need_mocks',
'',
' docstring',
'',
'',
' .. py:method:: TestAutodoc.decoratedMethod()', ' .. py:method:: TestAutodoc.decoratedMethod()',
' :module: target.need_mocks', ' :module: target.need_mocks',
'', '',

View File

@ -146,6 +146,7 @@ def test_ismock():
assert ismock(mod1) is True assert ismock(mod1) is True
assert ismock(mod1.Class) is True assert ismock(mod1.Class) is True
assert ismock(mod1.submod.Class) is True
assert ismock(Inherited) is False assert ismock(Inherited) is False
assert ismock(mod2) is False assert ismock(mod2) is False