From 61f9005b73273cbda0ee4633a79b9ffbf3a17d33 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 15:14:01 +0300 Subject: [PATCH 1/5] _MockModule and _MockObject now display meaningful names in type annotations and superclass names --- sphinx/ext/autodoc/importer.py | 37 ++++++++++++++++++++++-------- tests/test_ext_autodoc_importer.py | 18 ++++++++++++++- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 4b92a7b59..6af8125f9 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -31,13 +31,18 @@ logger = logging.getLogger(__name__) class _MockObject(object): """Used by autodoc_mock_imports.""" + __display_name__ = '_MockObject' + def __new__(cls, *args, **kwargs): # type: (Any, Any) -> Any - if len(args) == 3 and isinstance(args[1], tuple) and args[1][-1].__class__ is cls: - # subclassing MockObject - return type(args[0], (_MockObject,), args[2], **kwargs) # type: ignore - else: - return super(_MockObject, cls).__new__(cls) + if len(args) == 3 and isinstance(args[1], tuple): + superclass = args[1][-1].__class__ + if superclass is cls: + # subclassing MockObject + return _make_subclass(args[0], superclass.__display_name__, + superclass=superclass, attributes=args[2]) + + return super(_MockObject, cls).__new__(cls) def __init__(self, *args, **kwargs): # type: (Any, Any) -> None @@ -61,11 +66,11 @@ class _MockObject(object): def __getitem__(self, key): # type: (str) -> _MockObject - return self + return _make_subclass(key, self.__display_name__, self.__class__)() def __getattr__(self, key): # type: (str) -> _MockObject - return self + return _make_subclass(key, self.__display_name__, self.__class__)() def __call__(self, *args, **kw): # type: (Any, Any) -> Any @@ -74,6 +79,17 @@ class _MockObject(object): return args[0] return self + def __repr__(self): + return self.__display_name__ + + +def _make_subclass(name, module, superclass=_MockObject, attributes=None): + # type: (str, str, Any, dict) -> _MockObject + attrs = {'__module__': module, '__display_name__': module + '.' + name} + attrs.update(attributes or {}) + + return type(name, (superclass,), attrs) + class _MockModule(ModuleType): """Used by autodoc_mock_imports.""" @@ -88,9 +104,10 @@ class _MockModule(ModuleType): def __getattr__(self, name): # type: (str) -> _MockObject - o = _MockObject() - o.__module__ = self.__name__ - return o + return _make_subclass(name, self.__name__)() + + def __repr__(self): + return self.__name__ class _MockImporter(object): diff --git a/tests/test_ext_autodoc_importer.py b/tests/test_ext_autodoc_importer.py index fe0c9f2bc..108d06d7c 100644 --- a/tests/test_ext_autodoc_importer.py +++ b/tests/test_ext_autodoc_importer.py @@ -9,7 +9,7 @@ :license: BSD, see LICENSE for details. """ -from sphinx.ext.autodoc.importer import _MockObject +from sphinx.ext.autodoc.importer import _MockObject, _MockModule def test_MockObject(): @@ -21,6 +21,7 @@ def test_MockObject(): class SubClass(mock.SomeClass): """docstring of SubClass""" + def method(self): return "string" @@ -29,3 +30,18 @@ def test_MockObject(): assert isinstance(obj, SubClass) assert obj.method() == "string" assert isinstance(obj.other_method(), SubClass) + + +def test_MockModule(): + mock = _MockModule('mocked_module', None) + assert isinstance(mock.some_attr, _MockObject) + assert isinstance(mock.some_method, _MockObject) + assert isinstance(mock.attr1.attr2, _MockObject) + assert isinstance(mock.attr1.attr2.meth(), _MockObject) + + assert repr(mock.some_attr) == 'mocked_module.some_attr' + assert repr(mock.some_method) == 'mocked_module.some_method' + assert repr(mock.attr1.attr2) == 'mocked_module.attr1.attr2' + assert repr(mock.attr1.attr2.meth) == 'mocked_module.attr1.attr2.meth' + + assert repr(mock) == 'mocked_module' From c0b5f4712d8051f3542fadaa47a22723f9aba201 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 15:45:23 +0300 Subject: [PATCH 2/5] fix returned type --- sphinx/ext/autodoc/importer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 6af8125f9..f70d3e1ad 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -84,11 +84,11 @@ class _MockObject(object): def _make_subclass(name, module, superclass=_MockObject, attributes=None): - # type: (str, str, Any, dict) -> _MockObject + # type: (str, str, Any, dict) -> Any attrs = {'__module__': module, '__display_name__': module + '.' + name} attrs.update(attributes or {}) - return type(name, (superclass,), attrs) + return type(name, (superclass,), attrs) # type: ignore class _MockModule(ModuleType): From 4d94c3c97b39c7566d6d2eb5dafc58b67f09febc Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 7 Sep 2018 19:10:28 +0300 Subject: [PATCH 3/5] removed redundant "type: ignore" --- sphinx/ext/autodoc/importer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index f70d3e1ad..39bd1c0dd 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -80,6 +80,7 @@ class _MockObject(object): return self def __repr__(self): + # type: () -> str return self.__display_name__ @@ -88,7 +89,7 @@ def _make_subclass(name, module, superclass=_MockObject, attributes=None): attrs = {'__module__': module, '__display_name__': module + '.' + name} attrs.update(attributes or {}) - return type(name, (superclass,), attrs) # type: ignore + return type(name, (superclass,), attrs) class _MockModule(ModuleType): @@ -107,6 +108,7 @@ class _MockModule(ModuleType): return _make_subclass(name, self.__name__)() def __repr__(self): + # type: () -> str return self.__name__ From a6ef8190cef63d74c3e1cef5546348ebd67035e6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 01:46:38 +0900 Subject: [PATCH 4/5] Add testcase for mocked objects in autodoc --- tests/roots/test-ext-autodoc/target/need_mocks.py | 5 +++++ tests/test_autodoc.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/roots/test-ext-autodoc/target/need_mocks.py b/tests/roots/test-ext-autodoc/target/need_mocks.py index 4f83579a4..b8a661581 100644 --- a/tests/roots/test-ext-autodoc/target/need_mocks.py +++ b/tests/roots/test-ext-autodoc/target/need_mocks.py @@ -15,6 +15,11 @@ def decoratedFunction(): return None +def func(arg: missing_module.Class): + """a function takes mocked object as an argument""" + pass + + class TestAutodoc(object): """TestAutodoc docstring.""" @missing_name diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index a25a13639..a56226b42 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -1345,7 +1345,7 @@ def test_autofunction_for_callable(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_mocked_module_imports(app, warning): # no autodoc_mock_imports - options = {"members": 'TestAutodoc,decoratedFunction'} + options = {"members": 'TestAutodoc,decoratedFunction,func'} actual = do_autodoc(app, 'module', 'target.need_mocks', options) assert list(actual) == [] assert "autodoc: failed to import module 'need_mocks'" in warning.getvalue() @@ -1382,6 +1382,12 @@ def test_mocked_module_imports(app, warning): ' :module: target.need_mocks', '', ' decoratedFunction docstring', + ' ', + '', + '.. py:function:: func(arg: missing_module.Class)', + ' :module: target.need_mocks', + '', + ' a function takes mocked object as an argument', ' ' ] assert warning.getvalue() == '' From 0f6398efbb9f48f12bce02eb8a31d9f259605476 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 11 Feb 2019 01:48:21 +0900 Subject: [PATCH 5/5] Close #5394: autodoc: Display readable names in type annotations for mocked objects --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 3d0159f12..336b4ea7c 100644 --- a/CHANGES +++ b/CHANGES @@ -155,6 +155,7 @@ Features added * #4182: autodoc: Support :confval:`suppress_warnings` * #5533: autodoc: :confval:`autodoc_default_options` supports ``member-order`` +* #5394: autodoc: Display readable names in type annotations for mocked objects * #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and :confval:`htmlhelp_link_suffix` * #5559: text: Support complex tables (colspan and rowspan)