mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #6052 from tk0miya/5394_meaningful_annotations_for_mock
Display meaningful names in type annotations for mocked objects
This commit is contained in:
1
CHANGES
1
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)
|
||||
|
||||
@@ -32,13 +32,18 @@ logger = logging.getLogger(__name__)
|
||||
class _MockObject:
|
||||
"""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
|
||||
@@ -62,11 +67,11 @@ class _MockObject:
|
||||
|
||||
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
|
||||
@@ -75,6 +80,18 @@ class _MockObject:
|
||||
return args[0]
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
# type: () -> str
|
||||
return self.__display_name__
|
||||
|
||||
|
||||
def _make_subclass(name, module, superclass=_MockObject, attributes=None):
|
||||
# type: (str, str, Any, dict) -> Any
|
||||
attrs = {'__module__': module, '__display_name__': module + '.' + name}
|
||||
attrs.update(attributes or {})
|
||||
|
||||
return type(name, (superclass,), attrs)
|
||||
|
||||
|
||||
class _MockModule(ModuleType):
|
||||
"""Used by autodoc_mock_imports."""
|
||||
@@ -92,9 +109,11 @@ 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):
|
||||
# type: () -> str
|
||||
return self.__name__
|
||||
|
||||
|
||||
class _MockImporter(MetaPathFinder):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() == ''
|
||||
|
||||
@@ -16,6 +16,21 @@ import pytest
|
||||
from sphinx.ext.autodoc.importer import _MockModule, _MockObject, mock
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
||||
def test_MockObject():
|
||||
mock = _MockObject()
|
||||
assert isinstance(mock.some_attr, _MockObject)
|
||||
@@ -25,6 +40,7 @@ def test_MockObject():
|
||||
|
||||
class SubClass(mock.SomeClass):
|
||||
"""docstring of SubClass"""
|
||||
|
||||
def method(self):
|
||||
return "string"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user