mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 7901_resolve_types_for_overloaded_funcs
This commit is contained in:
8
CHANGES
8
CHANGES
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) }}
|
||||
|
||||
@@ -12,3 +12,4 @@ body_max_width = 800
|
||||
navigation_with_keys = False
|
||||
globaltoc_collapse = true
|
||||
globaltoc_includehidden = false
|
||||
globaltoc_maxdepth =
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
7
tests/roots/test-reST-code-block/index.rst
Normal file
7
tests/roots/test-reST-code-block/index.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. code-block::
|
||||
:linenos:
|
||||
|
||||
def hello(name)
|
||||
print("hello", name)
|
||||
|
||||
hello("Sphinx")
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user