Merge branch 'master' into 5459_autodoc_default_options_accepts_True

This commit is contained in:
Takeshi KOMIYA 2019-02-11 12:34:59 +09:00 committed by GitHub
commit ad517af81c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 93 additions and 22 deletions

View File

@ -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
* #5459: autodoc: :confval:`autodoc_default_options` accepts ``True`` as a value
* #4018: htmlhelp: Add :confval:`htmlhelp_file_suffix` and
:confval:`htmlhelp_link_suffix`
@ -178,6 +179,7 @@ Features added
* #6016: HTML search: A placeholder for the search summary prevents search
result links from changing their position when the search terminates. This
makes navigating search results easier.
* #5196: linkcheck also checks remote images exist
Bugs fixed
----------

View File

@ -25,7 +25,7 @@ from sphinx.util import encode_uri, requests, logging
from sphinx.util.console import ( # type: ignore
purple, red, darkgreen, darkgray, darkred, turquoise
)
from sphinx.util.nodes import traverse_parent
from sphinx.util.nodes import get_node_line
from sphinx.util.requests import is_ssl_error
if False:
@ -271,17 +271,24 @@ class CheckExternalLinksBuilder(Builder):
# type: (str, nodes.Node) -> None
logger.info('')
n = 0
for node in doctree.traverse(nodes.reference):
if 'refuri' not in node:
# reference nodes
for refnode in doctree.traverse(nodes.reference):
if 'refuri' not in refnode:
continue
uri = node['refuri']
lineno = None
for parent in traverse_parent(node):
if parent.line:
lineno = parent.line
break
uri = refnode['refuri']
lineno = get_node_line(refnode)
self.wqueue.put((uri, docname, lineno), False)
n += 1
# image nodes
for imgnode in doctree.traverse(nodes.image):
uri = imgnode['candidates'].get('?')
if uri and '://' in uri:
lineno = get_node_line(imgnode)
self.wqueue.put((uri, docname, lineno), False)
n += 1
done = 0
while done < n:
self.process_result(self.rqueue.get())

View File

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

View File

@ -285,6 +285,14 @@ def find_source_node(node):
return None
def get_node_line(node):
# type: (nodes.Element) -> int
for pnode in traverse_parent(node):
if pnode.line:
return pnode.line
return None
def traverse_parent(node, cls=None):
# type: (nodes.Element, Any) -> Iterable[nodes.Element]
while node:

View File

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

View File

@ -11,3 +11,6 @@ Some additional anchors to exercise ignore code
* `Example Bar invalid <http://example.com/#top>`_
* `Example anchor invalid <http://www.sphinx-doc.org/en/1.7/intro.html#does-not-exist>`_
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
.. image:: http://example.com/image.png
.. figure:: http://example.com/image2.png

View File

@ -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() == ''

View File

@ -24,7 +24,10 @@ def test_defaults(app, status, warning):
assert "Anchor 'does-not-exist' not found" in content
# looking for non-existent URL should fail
assert " Max retries exceeded with url: /doesnotexist" in content
assert len(content.splitlines()) == 3
# images should fail
assert "Not Found for url: http://example.com/image.png" in content
assert "Not Found for url: http://example.com/image2.png" in content
assert len(content.splitlines()) == 5
@pytest.mark.sphinx(
@ -32,7 +35,9 @@ def test_defaults(app, status, warning):
confoverrides={'linkcheck_anchors_ignore': ["^!", "^top$"],
'linkcheck_ignore': [
'https://localhost:7777/doesnotexist',
'http://www.sphinx-doc.org/en/1.7/intro.html#']
'http://www.sphinx-doc.org/en/1.7/intro.html#',
'http://example.com/image.png',
'http://example.com/image2.png']
})
def test_anchors_ignored(app, status, warning):
app.builder.build_all()

View File

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