Merge branch '2.4.x' into 3.x

This commit is contained in:
Takeshi KOMIYA 2020-02-22 19:22:31 +09:00
commit 754d04f80d
13 changed files with 106 additions and 18 deletions

View File

@ -64,6 +64,7 @@ Other contributors, listed alphabetically, are:
* \T. Powers -- HTML output improvements
* Jeppe Pihl -- literalinclude improvements
* Rob Ruana -- napoleon extension
* Vince Salvino -- JavaScript search improvements
* Stefan Seefeld -- toctree improvements
* Gregory Szorc -- performance improvements
* Taku Shimizu -- epub3 builder

View File

@ -93,6 +93,14 @@ Features added
Bugs fixed
----------
* #7184: autodoc: ``*args`` and ``**kwarg`` in type comments are not handled
properly
* #7189: autodoc: classmethod coroutines are not detected
* #7183: intersphinx: ``:attr:`` reference to property is broken
* #6244, #6387: html search: Search breaks/hangs when built with dirhtml builder
* #7195: todo: emit doctree-resolved event with non-document node incorrectly
Testing
--------

View File

@ -121,6 +121,17 @@ The following blocks exist in the ``layout.html`` template:
The contents of the document itself. It contains the block "body" where the
individual content is put by subtemplates like ``page.html``.
.. note::
In order for the built-in JavaScript search to show a page preview on
the results page, the document or body content should be wrapped in an
HTML element containing the ``role="main"`` attribute. For example:
.. sourcecode:: html+jinja
<div role="main">
{% block document %}{% endblock %}
</div>
`sidebar1` / `sidebar2`
A possible location for a sidebar. `sidebar1` appears before the document
and is empty by default, `sidebar2` after the document and contains the
@ -427,5 +438,3 @@ are in HTML form), these variables are also available:
* ``includehidden`` (``False`` by default): if true, the TOC tree will also
contain hidden entries.

View File

@ -56,7 +56,7 @@ def signature_from_ast(node: ast.FunctionDef, bound_method: bool,
if node.args.vararg:
param = Parameter(node.args.vararg.arg, Parameter.VAR_POSITIONAL,
annotation=arg.type_comment or Parameter.empty)
annotation=node.args.vararg.type_comment or Parameter.empty)
params.append(param)
for arg in node.args.kwonlyargs:
@ -66,7 +66,7 @@ def signature_from_ast(node: ast.FunctionDef, bound_method: bool,
if node.args.kwarg:
param = Parameter(node.args.kwarg.arg, Parameter.VAR_KEYWORD,
annotation=arg.type_comment or Parameter.empty)
annotation=node.args.kwarg.type_comment or Parameter.empty)
params.append(param)
# Remove first parameter when *obj* is bound_method

View File

@ -282,6 +282,9 @@ def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnod
if 'std:cmdoption' in objtypes:
# until Sphinx-1.6, cmdoptions are stored as std:option
objtypes.append('std:option')
if 'py:attribute' in objtypes:
# Since Sphinx-2.1, properties are stored as py:method
objtypes.append('py:method')
to_try = [(inventories.main_inventory, target)]
if domain:
full_qualified_name = env.get_domain(domain).get_full_qualified_name(node)

View File

@ -28,7 +28,7 @@ from sphinx.environment import BuildEnvironment
from sphinx.errors import NoUri
from sphinx.locale import _, __
from sphinx.util import logging, texescape
from sphinx.util.docutils import SphinxDirective
from sphinx.util.docutils import SphinxDirective, new_document
from sphinx.util.nodes import make_refnode
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.latex import LaTeXTranslator
@ -159,6 +159,7 @@ class TodoListProcessor:
def process(self, doctree: nodes.document, docname: str) -> None:
todos = sum(self.domain.todos.values(), []) # type: List[todo_node]
document = new_document('')
for node in doctree.traverse(todolist):
if not self.config.todo_include_todos:
node.parent.remove(node)
@ -175,7 +176,11 @@ class TodoListProcessor:
new_todo['ids'].clear()
# (Recursively) resolve references in the todo content
self.env.resolve_references(new_todo, todo['docname'], self.builder) # type: ignore # NOQA
#
# Note: To resolve references, it is needed to wrap it with document node
document += new_todo
self.env.resolve_references(document, todo['docname'], self.builder)
document.remove(new_todo)
content.append(new_todo)
todo_ref = self.create_todo_reference(todo, docname)

View File

@ -63,6 +63,11 @@ var Search = {
htmlElement.innerHTML = htmlString;
$(htmlElement).find('.headerlink').remove();
docContent = $(htmlElement).find('[role=main]')[0];
if(docContent === undefined) {
console.warn("Content block not found. Sphinx search tries to obtain it " +
"via '[role=main]'. Could you check your theme or template.");
return "";
}
return docContent.textContent || docContent.innerText;
},
@ -245,6 +250,7 @@ var Search = {
if (results.length) {
var item = results.pop();
var listItem = $('<li style="display:none"></li>');
var requestUrl = "";
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
// dirhtml builder
var dirname = item[0] + '/';
@ -253,15 +259,15 @@ var Search = {
} else if (dirname == 'index/') {
dirname = '';
}
listItem.append($('<a/>').attr('href',
DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
highlightstring + item[2]).html(item[1]));
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
} else {
// normal html builders
listItem.append($('<a/>').attr('href',
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
highlightstring + item[2]).html(item[1]));
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
}
listItem.append($('<a/>').attr('href',
requestUrl +
highlightstring + item[2]).html(item[1]));
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));
Search.output.append(listItem);
@ -269,7 +275,7 @@ var Search = {
displayNextItem();
});
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX,
$.ajax({url: requestUrl,
dataType: "text",
complete: function(jqxhr, textstatus) {
var data = jqxhr.responseText;

View File

@ -116,6 +116,19 @@ def getargspec(func: Callable) -> Any:
kwonlyargs, kwdefaults, annotations)
def unwrap(obj: Any) -> Any:
"""Get an original object from wrapped object."""
while True:
if ispartial(obj):
obj = unpartial(obj)
elif isclassmethod(obj):
obj = obj.__func__
elif isstaticmethod(obj):
obj = obj.__func__
else:
return obj
def isenumclass(x: Any) -> bool:
"""Check if the object is subclass of enum."""
return inspect.isclass(x) and issubclass(x, enum.Enum)
@ -146,7 +159,7 @@ def isclassmethod(obj: Any) -> bool:
"""Check if the object is classmethod."""
if isinstance(obj, classmethod):
return True
elif inspect.ismethod(obj) and obj.__self__ is not None:
elif inspect.ismethod(obj) and obj.__self__ is not None and isclass(obj.__self__):
return True
return False
@ -213,17 +226,17 @@ def isattributedescriptor(obj: Any) -> bool:
def isfunction(obj: Any) -> bool:
"""Check if the object is function."""
return inspect.isfunction(unpartial(obj))
return inspect.isfunction(unwrap(obj))
def isbuiltin(obj: Any) -> bool:
"""Check if the object is builtin."""
return inspect.isbuiltin(unpartial(obj))
return inspect.isbuiltin(unwrap(obj))
def iscoroutinefunction(obj: Any) -> bool:
"""Check if the object is coroutine-function."""
obj = unpartial(obj)
obj = unwrap(obj)
if hasattr(obj, '__code__') and inspect.iscoroutinefunction(obj):
# check obj.__code__ because iscoroutinefunction() crashes for custom method-like
# objects (see https://github.com/sphinx-doc/sphinx/issues/6605)

View File

@ -3,6 +3,16 @@ class AsyncClass:
"""A documented coroutine function"""
attr_coro_result = await _other_coro_func() # NOQA
@classmethod
async def do_coroutine2(cls):
"""A documented coroutine classmethod"""
pass
@staticmethod
async def do_coroutine3():
"""A documented coroutine staticmethod"""
pass
async def _other_coro_func():
return "run"

View File

@ -1356,7 +1356,23 @@ def test_coroutine():
' :async:',
' ',
' A documented coroutine function',
' '
' ',
' ',
' .. py:method:: AsyncClass.do_coroutine2()',
' :module: target.coroutine',
' :async:',
' :classmethod:',
' ',
' A documented coroutine classmethod',
' ',
' ',
' .. py:method:: AsyncClass.do_coroutine3()',
' :module: target.coroutine',
' :async:',
' :staticmethod:',
' ',
' A documented coroutine staticmethod',
' ',
]

View File

@ -190,6 +190,12 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'func()'
# py:attr context helps to search objects
kwargs = {'py:module': 'module1'}
node, contnode = fake_node('py', 'attr', 'Foo.bar', 'Foo.bar', **kwargs)
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'Foo.bar'
def test_missing_reference_stddomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'

View File

@ -434,6 +434,16 @@ def test_dict_customtype():
assert "<CustomType(2)>: 2" in description
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_isclassmethod(app):
from target.methods import Base, Inherited
assert inspect.isclassmethod(Base.classmeth) is True
assert inspect.isclassmethod(Base.meth) is False
assert inspect.isclassmethod(Inherited.classmeth) is True
assert inspect.isclassmethod(Inherited.meth) is False
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_isstaticmethod(app):
from target.methods import Base, Inherited

View File

@ -31,6 +31,7 @@ inventory_v2 = '''\
module1 py:module 0 foo.html#module-module1 Long Module desc
module2 py:module 0 foo.html#module-$ -
module1.func py:function 1 sub/foo.html#$ -
module1.Foo.bar py:method 1 index.html#foo.Bar.baz -
CFunc c:function 2 cfunc.html#CFunc -
std cpp:type 1 index.html#std -
std::uint8_t cpp:type 1 index.html#std_uint8_t -