Skip to content

Commit

Permalink
Merge branch '2.4.x' into 3.x
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Feb 22, 2020
2 parents db20d92 + 51ac09d commit 754d04f
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 18 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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
--------

Expand Down
13 changes: 11 additions & 2 deletions doc/templating.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.


4 changes: 2 additions & 2 deletions sphinx/ext/autodoc/type_comment.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions sphinx/ext/intersphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 7 additions & 2 deletions sphinx/ext/todo.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
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
Expand Down Expand Up @@ -159,6 +159,7 @@ def __init__(self, app: Sphinx, doctree: nodes.document, docname: str) -> None:

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)
Expand All @@ -175,7 +176,11 @@ def process(self, doctree: nodes.document, docname: str) -> None:
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)
Expand Down
20 changes: 13 additions & 7 deletions sphinx/themes/basic/static/searchtools.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
},

Expand Down Expand Up @@ -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] + '/';
Expand All @@ -253,23 +259,23 @@ 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);
listItem.slideDown(5, function() {
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;
Expand Down
21 changes: 17 additions & 4 deletions sphinx/util/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 10 additions & 0 deletions tests/roots/test-ext-autodoc/target/coroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ async def do_coroutine(self):
"""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"
18 changes: 17 additions & 1 deletion tests/test_autodoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
' ',
]


Expand Down
6 changes: 6 additions & 0 deletions tests/test_ext_intersphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
10 changes: 10 additions & 0 deletions tests/test_util_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,16 @@ def __repr__(self):
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
Expand Down
1 change: 1 addition & 0 deletions tests/test_util_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
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 -
Expand Down

0 comments on commit 754d04f

Please sign in to comment.