Merge branch '3.x' into 7901_resolve_types_for_overloaded_funcs

This commit is contained in:
Takeshi KOMIYA
2020-07-12 14:15:36 +09:00
committed by GitHub
18 changed files with 122 additions and 30 deletions

View File

@@ -13,10 +13,14 @@ Deprecated
Features added
--------------
* #7849: html: Add :confval:`html_codeblock_linenos_style` to change the style
of line numbers for code-blocks
* #7853: C and C++, support parameterized GNU style attributes.
* #7888: napoleon: Add aliases Warn and Raise.
* C, added :rst:dir:`c:alias` directive for inserting copies
of existing declarations.
* #7902: html theme: Add a new option :confval:`globaltoc_maxdepth` to control
the behavior of globaltoc in sidebar
* #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains.
Update the documentation to better reflect the relationship between this option
and the ``:noindex:`` option.
@@ -25,6 +29,8 @@ Bugs fixed
----------
* #7886: autodoc: TypeError is raised on mocking generic-typed classes
* #7935: autodoc: function signature is not shown when the function has a
parameter having ``inspect._empty`` as its default value
* #7901: autodoc: type annotations for overloaded functions are not resolved
* #7839: autosummary: cannot handle umlauts in function names
* #7865: autosummary: Failed to extract summary line when abbreviations found
@@ -34,6 +40,8 @@ Bugs fixed
* #7715: LaTeX: ``numfig_secnum_depth > 1`` leads to wrong figure links
* #7846: html theme: XML-invalid files were generated
* #7894: gettext: Wrong source info is shown when using rst_epilog
* #7691: linkcheck: HEAD requests are not used for checking
* #7928: py domain: failed to resolve a type annotation for the attribute
* #7869: :rst:role:`abbr` role without an explanation will show the explanation
from the previous abbr role
* C and C++, removed ``noindex`` directive option as it did

View File

@@ -926,6 +926,15 @@ that use Sphinx's HTMLWriter class.
.. versionadded:: 1.8
.. confval:: html_codeblock_linenos_style
The style of line numbers for code-blocks.
* ``'table'`` -- display line numbers using ``<table>`` tag (default)
* ``'inline'`` -- display line numbers using ``<span>`` tag
.. versionadded:: 3.2
.. confval:: html_context
A dictionary of values to pass into the template engine's context for all

View File

@@ -172,6 +172,12 @@ These themes are:
.. versionadded:: 3.1
- **globaltoc_maxdepth** (int): The maximum depth of the toctree in
``globaltoc.html`` (see :confval:`html_sidebars`). Set it to -1 to allow
unlimited depth. Defaults to the max depth selected in the toctree directive.
.. versionadded:: 3.2
**alabaster**
`Alabaster theme`_ is a modified "Kr" Sphinx theme from @kennethreitz
(especially as used in his Requests project), which was itself originally

View File

@@ -26,7 +26,7 @@ from docutils.utils import relative_path
from sphinx import package_dir, __display_version__
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.config import Config
from sphinx.config import Config, ENUM
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.domains import Domain, Index, IndexEntry
from sphinx.environment.adapters.asset import ImageAdapter
@@ -886,6 +886,8 @@ class StandaloneHTMLBuilder(Builder):
def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
if 'includehidden' not in kwargs:
kwargs['includehidden'] = False
if kwargs.get('maxdepth') == '':
kwargs.pop('maxdepth')
return self.render_partial(TocTree(self.env).get_toctree_for(
docname, self, collapse, **kwargs))['fragment']
@@ -1226,6 +1228,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value('html_search_scorer', '', None)
app.add_config_value('html_scaled_image_link', True, 'html')
app.add_config_value('html_baseurl', '', 'html')
app.add_config_value('html_codeblock_linenos_style', 'table', 'html',
ENUM('table', 'inline'))
app.add_config_value('html_math_renderer', None, 'env')
app.add_config_value('html4_writer', False, 'html')

View File

@@ -77,18 +77,24 @@ ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
('deprecated', bool)])
def type_to_xref(text: str) -> addnodes.pending_xref:
def type_to_xref(text: str, env: BuildEnvironment = None) -> addnodes.pending_xref:
"""Convert a type string to a cross reference node."""
if text == 'None':
reftype = 'obj'
else:
reftype = 'class'
if env:
kwargs = {'py:module': env.ref_context.get('py:module'),
'py:class': env.ref_context.get('py:class')}
else:
kwargs = {}
return pending_xref('', nodes.Text(text),
refdomain='py', reftype=reftype, reftarget=text)
refdomain='py', reftype=reftype, reftarget=text, **kwargs)
def _parse_annotation(annotation: str) -> List[Node]:
def _parse_annotation(annotation: str, env: BuildEnvironment = None) -> List[Node]:
"""Parse type annotation."""
def unparse(node: ast.AST) -> List[Node]:
if isinstance(node, ast.Attribute):
@@ -130,18 +136,22 @@ def _parse_annotation(annotation: str) -> List[Node]:
else:
raise SyntaxError # unsupported syntax
if env is None:
warnings.warn("The env parameter for _parse_annotation becomes required now.",
RemovedInSphinx50Warning, stacklevel=2)
try:
tree = ast_parse(annotation)
result = unparse(tree)
for i, node in enumerate(result):
if isinstance(node, nodes.Text):
result[i] = type_to_xref(str(node))
result[i] = type_to_xref(str(node), env)
return result
except SyntaxError:
return [type_to_xref(annotation)]
return [type_to_xref(annotation, env)]
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
def _parse_arglist(arglist: str, env: BuildEnvironment = None) -> addnodes.desc_parameterlist:
"""Parse a list of arguments using AST parser"""
params = addnodes.desc_parameterlist(arglist)
sig = signature_from_str('(%s)' % arglist)
@@ -167,7 +177,7 @@ def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
node += addnodes.desc_sig_name('', param.name)
if param.annotation is not param.empty:
children = _parse_annotation(param.annotation)
children = _parse_annotation(param.annotation, env)
node += addnodes.desc_sig_punctuation('', ':')
node += nodes.Text(' ')
node += addnodes.desc_sig_name('', '', *children) # type: ignore
@@ -415,7 +425,7 @@ class PyObject(ObjectDescription):
signode += addnodes.desc_name(name, name)
if arglist:
try:
signode += _parse_arglist(arglist)
signode += _parse_arglist(arglist, self.env)
except SyntaxError:
# fallback to parse arglist original parser.
# it supports to represent optional arguments (ex. "func(foo [, bar])")
@@ -430,7 +440,7 @@ class PyObject(ObjectDescription):
signode += addnodes.desc_parameterlist()
if retann:
children = _parse_annotation(retann)
children = _parse_annotation(retann, self.env)
signode += addnodes.desc_returns(retann, '', *children)
anno = self.options.get('annotation')
@@ -626,7 +636,7 @@ class PyVariable(PyObject):
typ = self.options.get('type')
if typ:
annotations = _parse_annotation(typ)
annotations = _parse_annotation(typ, self.env)
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
value = self.options.get('value')
@@ -872,7 +882,7 @@ class PyAttribute(PyObject):
typ = self.options.get('type')
if typ:
annotations = _parse_annotation(typ)
annotations = _parse_annotation(typ, self.env)
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), *annotations)
value = self.options.get('value')

View File

@@ -115,7 +115,7 @@ class EventManager:
raise
except Exception as exc:
raise ExtensionError(__("Handler %r for event %r threw an exception") %
(listener.handler, name)) from exc
(listener.handler, name), exc) from exc
return results
def emit_firstresult(self, name: str, *args: Any,

View File

@@ -8,4 +8,4 @@
:license: BSD, see LICENSE for details.
#}
<h3><a href="{{ pathto(master_doc)|e }}">{{ _('Table of Contents') }}</a></h3>
{{ toctree(includehidden=theme_globaltoc_includehidden, collapse=theme_globaltoc_collapse) }}
{{ toctree(includehidden=theme_globaltoc_includehidden, collapse=theme_globaltoc_collapse, maxdepth=theme_globaltoc_maxdepth) }}

View File

@@ -12,3 +12,4 @@ body_max_width = 800
navigation_with_keys = False
globaltoc_collapse = true
globaltoc_includehidden = false
globaltoc_maxdepth =

View File

@@ -485,7 +485,13 @@ def signature(subject: Callable, bound_method: bool = False, follow_wrapped: boo
if len(parameters) > 0:
parameters.pop(0)
return inspect.Signature(parameters, return_annotation=return_annotation)
# To allow to create signature object correctly for pure python functions,
# pass an internal parameter __validate_parameters__=False to Signature
#
# For example, this helps a function having a default value `inspect._empty`.
# refs: https://github.com/sphinx-doc/sphinx/issues/7935
return inspect.Signature(parameters, return_annotation=return_annotation, # type: ignore
__validate_parameters__=False)
def evaluate_signature(sig: inspect.Signature, globalns: Dict = None, localns: Dict = None

View File

@@ -124,4 +124,4 @@ def head(url: str, **kwargs: Any) -> requests.Response:
headers.setdefault('User-Agent', useragent_header[0][1])
with ignore_insecure_warning(**kwargs):
return requests.get(url, **kwargs)
return requests.head(url, **kwargs)

View File

@@ -445,6 +445,9 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
else:
opts = {}
if linenos and self.builder.config.html_codeblock_linenos_style:
linenos = self.builder.config.html_codeblock_linenos_style
highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos,
location=(self.builder.current_docname, node.line), **highlight_args

View File

@@ -397,6 +397,9 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
else:
opts = {}
if linenos and self.builder.config.html_codeblock_linenos_style:
linenos = self.builder.config.html_codeblock_linenos_style
highlighted = self.highlighter.highlight_block(
node.rawsource, lang, opts=opts, linenos=linenos,
location=(self.builder.current_docname, node.line), **highlight_args

View File

@@ -0,0 +1,7 @@
.. code-block::
:linenos:
def hello(name)
print("hello", name)
hello("Sphinx")

View File

@@ -1575,3 +1575,21 @@ def test_html_scaled_image_link(app):
assert re.search('\n<img alt="_images/img.png" class="no-scaled-link"'
' src="_images/img.png" style="[^"]+" />',
context)
@pytest.mark.sphinx('html', testroot='reST-code-block',
confoverrides={'html_codeblock_linenos_style': 'table'})
def test_html_codeblock_linenos_style_table(app):
app.build()
content = (app.outdir / 'index.html').read_text()
assert '<div class="linenodiv"><pre>1\n2\n3\n4</pre></div>' in content
@pytest.mark.sphinx('html', testroot='reST-code-block',
confoverrides={'html_codeblock_linenos_style': 'inline'})
def test_html_codeblock_linenos_style_inline(app):
app.build()
content = (app.outdir / 'index.html').read_text()
assert '<span class="lineno">1 </span>' in content

View File

@@ -236,18 +236,18 @@ def test_get_full_qualified_name():
assert domain.get_full_qualified_name(node) == 'module1.Class.func'
def test_parse_annotation():
doctree = _parse_annotation("int")
def test_parse_annotation(app):
doctree = _parse_annotation("int", app.env)
assert_node(doctree, ([pending_xref, "int"],))
assert_node(doctree[0], pending_xref, refdomain="py", reftype="class", reftarget="int")
doctree = _parse_annotation("List[int]")
doctree = _parse_annotation("List[int]", app.env)
assert_node(doctree, ([pending_xref, "List"],
[desc_sig_punctuation, "["],
[pending_xref, "int"],
[desc_sig_punctuation, "]"]))
doctree = _parse_annotation("Tuple[int, int]")
doctree = _parse_annotation("Tuple[int, int]", app.env)
assert_node(doctree, ([pending_xref, "Tuple"],
[desc_sig_punctuation, "["],
[pending_xref, "int"],
@@ -255,14 +255,14 @@ def test_parse_annotation():
[pending_xref, "int"],
[desc_sig_punctuation, "]"]))
doctree = _parse_annotation("Tuple[()]")
doctree = _parse_annotation("Tuple[()]", app.env)
assert_node(doctree, ([pending_xref, "Tuple"],
[desc_sig_punctuation, "["],
[desc_sig_punctuation, "("],
[desc_sig_punctuation, ")"],
[desc_sig_punctuation, "]"]))
doctree = _parse_annotation("Callable[[int, int], int]")
doctree = _parse_annotation("Callable[[int, int], int]", app.env)
assert_node(doctree, ([pending_xref, "Callable"],
[desc_sig_punctuation, "["],
[desc_sig_punctuation, "["],
@@ -275,12 +275,11 @@ def test_parse_annotation():
[desc_sig_punctuation, "]"]))
# None type makes an object-reference (not a class reference)
doctree = _parse_annotation("None")
doctree = _parse_annotation("None", app.env)
assert_node(doctree, ([pending_xref, "None"],))
assert_node(doctree[0], pending_xref, refdomain="py", reftype="obj", reftarget="None")
def test_pyfunction_signature(app):
text = ".. py:function:: hello(name: str) -> str"
doctree = restructuredtext.parse(app, text)
@@ -458,14 +457,22 @@ def test_pyobject_prefix(app):
def test_pydata(app):
text = ".. py:data:: var\n"
text = (".. py:module:: example\n"
".. py:data:: var\n"
" :type: int\n")
domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, desc_name, "var"],
assert_node(doctree, (nodes.target,
addnodes.index,
addnodes.index,
[desc, ([desc_signature, ([desc_addname, "example."],
[desc_name, "var"],
[desc_annotation, (": ",
[pending_xref, "int"])])],
[desc_content, ()])]))
assert 'var' in domain.objects
assert domain.objects['var'] == ('index', 'var', 'data')
assert_node(doctree[3][0][2][1], pending_xref, **{"py:module": "example"})
assert 'example.var' in domain.objects
assert domain.objects['example.var'] == ('index', 'example.var', 'data')
def test_pyfunction(app):
@@ -698,6 +705,8 @@ def test_pyattribute(app):
[desc_sig_punctuation, "]"])],
[desc_annotation, " = ''"])],
[desc_content, ()]))
assert_node(doctree[1][1][1][0][1][1], pending_xref, **{"py:class": "Class"})
assert_node(doctree[1][1][1][0][1][3], pending_xref, **{"py:class": "Class"})
assert 'Class.attr' in domain.objects
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')

View File

@@ -130,7 +130,7 @@ def test_signature_partialmethod():
def test_signature_annotations():
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, Node)
f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, f21, Node)
# Class annotations
sig = inspect.signature(f0)
@@ -214,6 +214,10 @@ def test_signature_annotations():
sig = inspect.signature(f19)
assert stringify_signature(sig) == '(*args: int, **kwargs: str)'
# default value is inspect.Signature.empty
sig = inspect.signature(f21)
assert stringify_signature(sig) == "(arg1='whatever', arg2)"
# type hints by string
sig = inspect.signature(Node.children)
if (3, 5, 0) <= sys.version_info < (3, 5, 3):

View File

@@ -1,3 +1,4 @@
from inspect import Signature
from numbers import Integral
from typing import Any, Dict, List, TypeVar, Union, Callable, Tuple, Optional
@@ -100,6 +101,9 @@ def f20() -> Optional[Union[int, str]]:
pass
def f21(arg1='whatever', arg2=Signature.empty):
pass
class Node:
def __init__(self, parent: Optional['Node']) -> None: