mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.4.x' into 3.x
This commit is contained in:
commit
754d04f80d
1
AUTHORS
1
AUTHORS
@ -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
|
||||
|
8
CHANGES
8
CHANGES
@ -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
|
||||
--------
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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',
|
||||
' ',
|
||||
]
|
||||
|
||||
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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 -
|
||||
|
Loading…
Reference in New Issue
Block a user