mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x'
This commit is contained in:
commit
3c7d35d2a2
1
AUTHORS
1
AUTHORS
@ -85,6 +85,7 @@ Other contributors, listed alphabetically, are:
|
|||||||
* Daniel Pizetta -- inheritance diagram improvements
|
* Daniel Pizetta -- inheritance diagram improvements
|
||||||
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
|
* KINEBUCHI Tomohiko -- typing Sphinx as well as docutils
|
||||||
* Adrián Chaves (Gallaecio) -- coverage builder improvements
|
* Adrián Chaves (Gallaecio) -- coverage builder improvements
|
||||||
|
* Lars Hupfeldt Nielsen - OpenSSL FIPS mode md5 bug fix
|
||||||
|
|
||||||
Many thanks for all contributions!
|
Many thanks for all contributions!
|
||||||
|
|
||||||
|
25
CHANGES
25
CHANGES
@ -64,6 +64,7 @@ Deprecated
|
|||||||
generate_autosummary_docs()``
|
generate_autosummary_docs()``
|
||||||
* The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()``
|
* The ``ignore`` argument of ``sphinx.util.docstring.prepare_docstring()``
|
||||||
* ``sphinx.ext.autosummary.generate.AutosummaryRenderer.exists()``
|
* ``sphinx.ext.autosummary.generate.AutosummaryRenderer.exists()``
|
||||||
|
* ``sphinx.util.rpartition()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
--------------
|
--------------
|
||||||
@ -107,6 +108,7 @@ Features added
|
|||||||
* C++, parse trailing return types.
|
* C++, parse trailing return types.
|
||||||
* #7143: py domain: Add ``:final:`` option to :rst:dir:`py:class:`,
|
* #7143: py domain: Add ``:final:`` option to :rst:dir:`py:class:`,
|
||||||
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
||||||
|
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
||||||
* #7582: napoleon: a type for attribute are represented like type annotation
|
* #7582: napoleon: a type for attribute are represented like type annotation
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
@ -121,10 +123,30 @@ Bugs fixed
|
|||||||
* #6857: autodoc: failed to detect a classmethod on Enum class
|
* #6857: autodoc: failed to detect a classmethod on Enum class
|
||||||
* #7562: autodoc: a typehint contains spaces is wrongly rendered under
|
* #7562: autodoc: a typehint contains spaces is wrongly rendered under
|
||||||
autodoc_typehints='description' mode
|
autodoc_typehints='description' mode
|
||||||
|
* #7551: autodoc: failed to import nested class
|
||||||
|
* #7362: autodoc: does not render correct signatures for built-in functions
|
||||||
|
* #7654: autodoc: ``Optional[Union[foo, bar]]`` is presented as
|
||||||
|
``Union[foo, bar, None]``
|
||||||
|
* #7629: autodoc: autofunction emits an unfriendly warning if an invalid object
|
||||||
|
specified
|
||||||
|
* #7650: autodoc: undecorated signature is shown for decorated functions
|
||||||
|
* #7676: autodoc: typo in the default value of autodoc_member_order
|
||||||
|
* #7551: autosummary: a nested class is indexed as non-nested class
|
||||||
|
* #7661: autosummary: autosummary directive emits warnings twices if failed to
|
||||||
|
import the target module
|
||||||
* #7535: sphinx-autogen: crashes when custom template uses inheritance
|
* #7535: sphinx-autogen: crashes when custom template uses inheritance
|
||||||
* #7536: sphinx-autogen: crashes when template uses i18n feature
|
* #7536: sphinx-autogen: crashes when template uses i18n feature
|
||||||
|
* #7653: sphinx-quickstart: Fix multiple directory creation for nested relpath
|
||||||
* #2785: html: Bad alignment of equation links
|
* #2785: html: Bad alignment of equation links
|
||||||
* #7581: napoleon: bad parsing of inline code in attribute docstrings
|
* #7581: napoleon: bad parsing of inline code in attribute docstrings
|
||||||
|
* #7628: imgconverter: runs imagemagick once unnecessary for builders not
|
||||||
|
supporting images
|
||||||
|
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
||||||
|
* #7646: handle errors on event handlers
|
||||||
|
* C++, fix rendering and xrefs in nested names explicitly starting
|
||||||
|
in global scope, e.g., ``::A::B``.
|
||||||
|
* C, fix rendering and xrefs in nested names explicitly starting
|
||||||
|
in global scope, e.g., ``.A.B``.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
@ -148,6 +170,9 @@ Bugs fixed
|
|||||||
----------
|
----------
|
||||||
|
|
||||||
* #7567: autodoc: parametrized types are shown twice for generic types
|
* #7567: autodoc: parametrized types are shown twice for generic types
|
||||||
|
* #7637: autodoc: system defined TypeVars are shown in Python 3.9
|
||||||
|
* #7611: md5 fails when OpenSSL FIPS is enabled
|
||||||
|
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -3,6 +3,7 @@ include LICENSE
|
|||||||
include AUTHORS
|
include AUTHORS
|
||||||
include CHANGES
|
include CHANGES
|
||||||
include CHANGES.old
|
include CHANGES.old
|
||||||
|
include CODE_OF_CONDUCT
|
||||||
include CONTRIBUTING.rst
|
include CONTRIBUTING.rst
|
||||||
include EXAMPLES
|
include EXAMPLES
|
||||||
|
|
||||||
|
@ -108,6 +108,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 5.0
|
- 5.0
|
||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.util.rpartition()``
|
||||||
|
- 3.1
|
||||||
|
- 5.0
|
||||||
|
- ``str.rpartition()``
|
||||||
|
|
||||||
* - ``desc_signature['first']``
|
* - ``desc_signature['first']``
|
||||||
-
|
-
|
||||||
- 3.0
|
- 3.0
|
||||||
|
@ -1384,7 +1384,7 @@ that use Sphinx's HTMLWriter class.
|
|||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
|
|
||||||
.. versionchanged:: 2.4
|
.. versionchanged:: 3.0
|
||||||
|
|
||||||
It is disabled for images having ``no-scaled-link`` class
|
It is disabled for images having ``no-scaled-link`` class
|
||||||
|
|
||||||
|
@ -109,6 +109,13 @@ class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement):
|
|||||||
In that case all child nodes must be ``desc_signature_line`` nodes.
|
In that case all child nodes must be ``desc_signature_line`` nodes.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def child_text_separator(self):
|
||||||
|
if self.get('is_multiline'):
|
||||||
|
return ' '
|
||||||
|
else:
|
||||||
|
return super().child_text_separator
|
||||||
|
|
||||||
|
|
||||||
class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
||||||
"""Node for a line in a multi-line object signatures.
|
"""Node for a line in a multi-line object signatures.
|
||||||
@ -147,6 +154,9 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
|||||||
"""Node for a general parameter list."""
|
"""Node for a general parameter list."""
|
||||||
child_text_separator = ', '
|
child_text_separator = ', '
|
||||||
|
|
||||||
|
def astext(self):
|
||||||
|
return '({})'.format(super().astext())
|
||||||
|
|
||||||
|
|
||||||
class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
||||||
"""Node for a single parameter."""
|
"""Node for a single parameter."""
|
||||||
|
@ -12,7 +12,6 @@ import html
|
|||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from hashlib import md5
|
|
||||||
from os import path
|
from os import path
|
||||||
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple, Type
|
from typing import Any, Dict, IO, Iterable, Iterator, List, Set, Tuple, Type
|
||||||
|
|
||||||
@ -36,7 +35,7 @@ from sphinx.highlighting import PygmentsBridge
|
|||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.search import js_index
|
from sphinx.search import js_index
|
||||||
from sphinx.theming import HTMLThemeFactory
|
from sphinx.theming import HTMLThemeFactory
|
||||||
from sphinx.util import logging, progress_message, status_iterator
|
from sphinx.util import logging, progress_message, status_iterator, md5
|
||||||
from sphinx.util.docutils import is_html5_writer_available, new_document
|
from sphinx.util.docutils import is_html5_writer_available, new_document
|
||||||
from sphinx.util.fileutil import copy_asset
|
from sphinx.util.fileutil import copy_asset
|
||||||
from sphinx.util.i18n import format_date
|
from sphinx.util.i18n import format_date
|
||||||
|
@ -319,6 +319,7 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
|
|||||||
d.setdefault('extensions', [])
|
d.setdefault('extensions', [])
|
||||||
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
||||||
|
|
||||||
|
d["path"] = os.path.abspath(d['path'])
|
||||||
ensuredir(d['path'])
|
ensuredir(d['path'])
|
||||||
|
|
||||||
srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
|
srcdir = path.join(d['path'], 'source') if d['sep'] else d['path']
|
||||||
|
@ -186,11 +186,17 @@ class ASTNestedName(ASTBase):
|
|||||||
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
||||||
# else append directly to signode.
|
# else append directly to signode.
|
||||||
# TODO: also for C?
|
# TODO: also for C?
|
||||||
# NOTE: Breathe relies on the prefix being in the desc_addname node,
|
# NOTE: Breathe previously relied on the prefix being in the desc_addname node,
|
||||||
# so it can remove it in inner declarations.
|
# so it can remove it in inner declarations.
|
||||||
dest = signode
|
dest = signode
|
||||||
if mode == 'lastIsName':
|
if mode == 'lastIsName':
|
||||||
dest = addnodes.desc_addname()
|
dest = addnodes.desc_addname()
|
||||||
|
if self.rooted:
|
||||||
|
prefix += '.'
|
||||||
|
if mode == 'lastIsName' and len(names) == 0:
|
||||||
|
signode += nodes.Text('.')
|
||||||
|
else:
|
||||||
|
dest += nodes.Text('.')
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
ident = names[i]
|
ident = names[i]
|
||||||
if not first:
|
if not first:
|
||||||
|
@ -752,11 +752,17 @@ class ASTNestedName(ASTBase):
|
|||||||
names = self.names[:-1] if mode == 'lastIsName' else self.names
|
names = self.names[:-1] if mode == 'lastIsName' else self.names
|
||||||
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
||||||
# else append directly to signode.
|
# else append directly to signode.
|
||||||
# NOTE: Breathe relies on the prefix being in the desc_addname node,
|
# NOTE: Breathe previously relied on the prefix being in the desc_addname node,
|
||||||
# so it can remove it in inner declarations.
|
# so it can remove it in inner declarations.
|
||||||
dest = signode
|
dest = signode
|
||||||
if mode == 'lastIsName':
|
if mode == 'lastIsName':
|
||||||
dest = addnodes.desc_addname()
|
dest = addnodes.desc_addname()
|
||||||
|
if self.rooted:
|
||||||
|
prefix += '::'
|
||||||
|
if mode == 'lastIsName' and len(names) == 0:
|
||||||
|
signode += nodes.Text('::')
|
||||||
|
else:
|
||||||
|
dest += nodes.Text('::')
|
||||||
for i in range(len(names)):
|
for i in range(len(names)):
|
||||||
nne = names[i]
|
nne = names[i]
|
||||||
template = self.templates[i]
|
template = self.templates[i]
|
||||||
@ -3722,8 +3728,8 @@ class LookupKey:
|
|||||||
class Symbol:
|
class Symbol:
|
||||||
debug_indent = 0
|
debug_indent = 0
|
||||||
debug_indent_string = " "
|
debug_indent_string = " "
|
||||||
debug_lookup = False
|
debug_lookup = False # overridden by the corresponding config value
|
||||||
debug_show_tree = False
|
debug_show_tree = False # overridden by the corresponding config value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def debug_print(*args: Any) -> None:
|
def debug_print(*args: Any) -> None:
|
||||||
@ -7383,9 +7389,18 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_config_value("cpp_paren_attributes", [], 'env')
|
app.add_config_value("cpp_paren_attributes", [], 'env')
|
||||||
app.add_post_transform(AliasTransform)
|
app.add_post_transform(AliasTransform)
|
||||||
|
|
||||||
|
# debug stuff
|
||||||
|
app.add_config_value("cpp_debug_lookup", False, '')
|
||||||
|
app.add_config_value("cpp_debug_show_tree", False, '')
|
||||||
|
|
||||||
|
def setDebugFlags(app):
|
||||||
|
Symbol.debug_lookup = app.config.cpp_debug_lookup
|
||||||
|
Symbol.debug_show_tree = app.config.cpp_debug_show_tree
|
||||||
|
app.connect("builder-inited", setDebugFlags)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'version': 'builtin',
|
'version': 'builtin',
|
||||||
'env_version': 2,
|
'env_version': 3,
|
||||||
'parallel_read_safe': True,
|
'parallel_read_safe': True,
|
||||||
'parallel_write_safe': True,
|
'parallel_write_safe': True,
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,8 @@ ModuleEntry = NamedTuple('ModuleEntry', [('docname', str),
|
|||||||
('deprecated', bool)])
|
('deprecated', bool)])
|
||||||
|
|
||||||
|
|
||||||
def _parse_annotation(annotation: str) -> List[Node]:
|
def type_to_xref(text: str) -> addnodes.pending_xref:
|
||||||
"""Parse type annotation."""
|
"""Convert a type string to a cross reference node."""
|
||||||
def make_xref(text: str) -> addnodes.pending_xref:
|
|
||||||
if text == 'None':
|
if text == 'None':
|
||||||
reftype = 'obj'
|
reftype = 'obj'
|
||||||
else:
|
else:
|
||||||
@ -85,6 +84,9 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
|||||||
return pending_xref('', nodes.Text(text),
|
return pending_xref('', nodes.Text(text),
|
||||||
refdomain='py', reftype=reftype, reftarget=text)
|
refdomain='py', reftype=reftype, reftarget=text)
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_annotation(annotation: str) -> List[Node]:
|
||||||
|
"""Parse type annotation."""
|
||||||
def unparse(node: ast.AST) -> List[Node]:
|
def unparse(node: ast.AST) -> List[Node]:
|
||||||
if isinstance(node, ast.Attribute):
|
if isinstance(node, ast.Attribute):
|
||||||
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
|
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
|
||||||
@ -130,10 +132,10 @@ def _parse_annotation(annotation: str) -> List[Node]:
|
|||||||
result = unparse(tree)
|
result = unparse(tree)
|
||||||
for i, node in enumerate(result):
|
for i, node in enumerate(result):
|
||||||
if isinstance(node, nodes.Text):
|
if isinstance(node, nodes.Text):
|
||||||
result[i] = make_xref(str(node))
|
result[i] = type_to_xref(str(node))
|
||||||
return result
|
return result
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
return [make_xref(annotation)]
|
return [type_to_xref(annotation)]
|
||||||
|
|
||||||
|
|
||||||
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||||
@ -590,7 +592,7 @@ class PyVariable(PyObject):
|
|||||||
|
|
||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
@ -750,7 +752,7 @@ class PyAttribute(PyObject):
|
|||||||
|
|
||||||
typ = self.options.get('type')
|
typ = self.options.get('type')
|
||||||
if typ:
|
if typ:
|
||||||
signode += addnodes.desc_annotation(typ, ': ' + typ)
|
signode += addnodes.desc_annotation(typ, '', nodes.Text(': '), type_to_xref(typ))
|
||||||
|
|
||||||
value = self.options.get('value')
|
value = self.options.get('value')
|
||||||
if value:
|
if value:
|
||||||
|
@ -15,7 +15,7 @@ from operator import attrgetter
|
|||||||
from typing import Any, Callable, Dict, List, NamedTuple
|
from typing import Any, Callable, Dict, List, NamedTuple
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sphinx.errors import ExtensionError
|
from sphinx.errors import ExtensionError, SphinxError
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
|
|
||||||
@ -95,7 +95,13 @@ class EventManager:
|
|||||||
results = []
|
results = []
|
||||||
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
||||||
for listener in listeners:
|
for listener in listeners:
|
||||||
|
try:
|
||||||
results.append(listener.handler(self.app, *args))
|
results.append(listener.handler(self.app, *args))
|
||||||
|
except SphinxError:
|
||||||
|
raise
|
||||||
|
except Exception as exc:
|
||||||
|
raise ExtensionError(__("Handler %r for event %r threw an exception") %
|
||||||
|
(listener.handler, name)) from exc
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def emit_firstresult(self, name: str, *args: Any) -> Any:
|
def emit_firstresult(self, name: str, *args: Any) -> Any:
|
||||||
|
@ -17,13 +17,12 @@ from inspect import Parameter
|
|||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
from docutils.statemachine import StringList
|
from docutils.statemachine import StringList
|
||||||
|
|
||||||
import sphinx
|
import sphinx
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.config import ENUM
|
from sphinx.config import Config, ENUM
|
||||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
|
from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
|
||||||
@ -32,7 +31,7 @@ from sphinx.locale import _, __
|
|||||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
from sphinx.util import inspect
|
from sphinx.util import inspect
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util import rpartition
|
from sphinx.util import split_full_qualified_name
|
||||||
from sphinx.util.docstrings import extract_metadata, prepare_docstring
|
from sphinx.util.docstrings import extract_metadata, prepare_docstring
|
||||||
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
|
||||||
from sphinx.util.typing import stringify as stringify_typehint
|
from sphinx.util.typing import stringify as stringify_typehint
|
||||||
@ -311,6 +310,7 @@ class Documenter:
|
|||||||
modname = None
|
modname = None
|
||||||
parents = []
|
parents = []
|
||||||
|
|
||||||
|
with mock(self.env.config.autodoc_mock_imports):
|
||||||
self.modname, self.objpath = self.resolve_name(modname, parents, path, base)
|
self.modname, self.objpath = self.resolve_name(modname, parents, path, base)
|
||||||
|
|
||||||
if not self.modname:
|
if not self.modname:
|
||||||
@ -419,8 +419,15 @@ class Documenter:
|
|||||||
directive = getattr(self, 'directivetype', self.objtype)
|
directive = getattr(self, 'directivetype', self.objtype)
|
||||||
name = self.format_name()
|
name = self.format_name()
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
self.add_line('.. %s:%s:: %s%s' % (domain, directive, name, sig),
|
|
||||||
|
# one signature per line, indented by column
|
||||||
|
prefix = '.. %s:%s:: ' % (domain, directive)
|
||||||
|
for i, sig_line in enumerate(sig.split("\n")):
|
||||||
|
self.add_line('%s%s%s' % (prefix, name, sig_line),
|
||||||
sourcename)
|
sourcename)
|
||||||
|
if i == 0:
|
||||||
|
prefix = " " * len(prefix)
|
||||||
|
|
||||||
if self.options.noindex:
|
if self.options.noindex:
|
||||||
self.add_line(' :noindex:', sourcename)
|
self.add_line(' :noindex:', sourcename)
|
||||||
if self.objpath:
|
if self.objpath:
|
||||||
@ -893,8 +900,14 @@ class ModuleLevelDocumenter(Documenter):
|
|||||||
) -> Tuple[str, List[str]]:
|
) -> Tuple[str, List[str]]:
|
||||||
if modname is None:
|
if modname is None:
|
||||||
if path:
|
if path:
|
||||||
modname = path.rstrip('.')
|
stripped = path.rstrip('.')
|
||||||
|
modname, qualname = split_full_qualified_name(stripped)
|
||||||
|
if qualname:
|
||||||
|
parents = qualname.split(".")
|
||||||
else:
|
else:
|
||||||
|
parents = []
|
||||||
|
|
||||||
|
if modname is None:
|
||||||
# if documenting a toplevel object without explicit module,
|
# if documenting a toplevel object without explicit module,
|
||||||
# it can be contained in another auto directive ...
|
# it can be contained in another auto directive ...
|
||||||
modname = self.env.temp_data.get('autodoc:module')
|
modname = self.env.temp_data.get('autodoc:module')
|
||||||
@ -927,8 +940,13 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
# ... if still None, there's no way to know
|
# ... if still None, there's no way to know
|
||||||
if mod_cls is None:
|
if mod_cls is None:
|
||||||
return None, []
|
return None, []
|
||||||
modname, cls = rpartition(mod_cls, '.')
|
|
||||||
parents = [cls]
|
try:
|
||||||
|
modname, qualname = split_full_qualified_name(mod_cls)
|
||||||
|
parents = qualname.split(".") if qualname else []
|
||||||
|
except ImportError:
|
||||||
|
parents = mod_cls.split(".")
|
||||||
|
|
||||||
# if the module name is still missing, get it like above
|
# if the module name is still missing, get it like above
|
||||||
if not modname:
|
if not modname:
|
||||||
modname = self.env.temp_data.get('autodoc:module')
|
modname = self.env.temp_data.get('autodoc:module')
|
||||||
@ -1026,43 +1044,19 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
unwrapped = inspect.unwrap(self.object)
|
|
||||||
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
|
||||||
not inspect.is_cython_function_or_method(unwrapped)):
|
|
||||||
# cannot introspect arguments of a C function or method
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
if (not inspect.isfunction(unwrapped) and
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
not inspect.ismethod(unwrapped) and
|
if inspect.is_singledispatch_function(self.object):
|
||||||
not inspect.isbuiltin(unwrapped) and
|
sig = inspect.signature(self.object, follow_wrapped=True)
|
||||||
not inspect.is_cython_function_or_method(unwrapped) and
|
|
||||||
not inspect.isclass(unwrapped) and
|
|
||||||
hasattr(unwrapped, '__call__')):
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__call__, False)
|
|
||||||
sig = inspect.signature(unwrapped.__call__)
|
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
sig = inspect.signature(self.object)
|
||||||
sig = inspect.signature(unwrapped)
|
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
except TypeError:
|
except TypeError as exc:
|
||||||
if (inspect.is_builtin_class_method(unwrapped, '__new__') and
|
logger.warning(__("Failed to get a function signature for %s: %s"),
|
||||||
inspect.is_builtin_class_method(unwrapped, '__init__')):
|
self.fullname, exc)
|
||||||
raise TypeError('%r is a builtin class' % unwrapped)
|
return None
|
||||||
|
except ValueError:
|
||||||
# if a class should be documented as function (yay duck
|
args = ''
|
||||||
# typing) we try to use the constructor signature as function
|
|
||||||
# signature without the first argument.
|
|
||||||
try:
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__new__, True)
|
|
||||||
sig = inspect.signature(unwrapped.__new__, bound_method=True)
|
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
|
||||||
except TypeError:
|
|
||||||
self.env.app.emit('autodoc-before-process-signature',
|
|
||||||
unwrapped.__init__, True)
|
|
||||||
sig = inspect.signature(unwrapped.__init__, bound_method=True)
|
|
||||||
args = stringify_signature(sig, show_return_annotation=False, **kwargs)
|
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
@ -1074,26 +1068,17 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
if inspect.is_singledispatch_function(self.object):
|
|
||||||
self.add_singledispatch_directive_header(sig)
|
|
||||||
else:
|
|
||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
|
|
||||||
if inspect.iscoroutinefunction(self.object):
|
if inspect.iscoroutinefunction(self.object):
|
||||||
self.add_line(' :async:', sourcename)
|
self.add_line(' :async:', sourcename)
|
||||||
|
|
||||||
def add_singledispatch_directive_header(self, sig: str) -> None:
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
sourcename = self.get_sourcename()
|
sig = super().format_signature(**kwargs)
|
||||||
|
sigs = [sig]
|
||||||
|
|
||||||
# intercept generated directive headers
|
if inspect.is_singledispatch_function(self.object):
|
||||||
# TODO: It is very hacky to use mock to intercept header generation
|
# append signature of singledispatch'ed functions
|
||||||
with patch.object(self, 'add_line') as add_line:
|
|
||||||
super().add_directive_header(sig)
|
|
||||||
|
|
||||||
# output first line of header
|
|
||||||
self.add_line(*add_line.call_args_list[0][0])
|
|
||||||
|
|
||||||
# inserts signature of singledispatch'ed functions
|
|
||||||
for typ, func in self.object.registry.items():
|
for typ, func in self.object.registry.items():
|
||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
@ -1102,13 +1087,9 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
|
|
||||||
documenter = FunctionDocumenter(self.directive, '')
|
documenter = FunctionDocumenter(self.directive, '')
|
||||||
documenter.object = func
|
documenter.object = func
|
||||||
self.add_line(' %s%s' % (self.format_name(),
|
sigs.append(documenter.format_signature())
|
||||||
documenter.format_signature()),
|
|
||||||
sourcename)
|
|
||||||
|
|
||||||
# output remains of directive header
|
return "\n".join(sigs)
|
||||||
for call in add_line.call_args_list[1:]:
|
|
||||||
self.add_line(*call[0])
|
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
@ -1448,18 +1429,33 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
unwrapped = inspect.unwrap(self.object)
|
try:
|
||||||
if ((inspect.isbuiltin(unwrapped) or inspect.ismethoddescriptor(unwrapped)) and
|
if self.object == object.__init__ and self.parent != object:
|
||||||
not inspect.is_cython_function_or_method(unwrapped)):
|
# Classes not having own __init__() method are shown as no arguments.
|
||||||
# can never get arguments of a C function or method
|
#
|
||||||
return None
|
# Note: The signature of object.__init__() is (self, /, *args, **kwargs).
|
||||||
if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
|
# But it makes users confused.
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
args = '()'
|
||||||
sig = inspect.signature(unwrapped, bound_method=False)
|
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
|
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||||
sig = inspect.signature(unwrapped, bound_method=True)
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
|
sig = inspect.signature(self.object, bound_method=False)
|
||||||
|
else:
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', self.object, True)
|
||||||
|
|
||||||
|
meth = self.parent.__dict__.get(self.objpath[-1], None)
|
||||||
|
if meth and inspect.is_singledispatch_method(meth):
|
||||||
|
sig = inspect.signature(self.object, bound_method=True,
|
||||||
|
follow_wrapped=True)
|
||||||
|
else:
|
||||||
|
sig = inspect.signature(self.object, bound_method=True)
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
|
except TypeError as exc:
|
||||||
|
logger.warning(__("Failed to get a method signature for %s: %s"),
|
||||||
|
self.fullname, exc)
|
||||||
|
return None
|
||||||
|
except ValueError:
|
||||||
|
args = ''
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
# escape backslashes for reST
|
# escape backslashes for reST
|
||||||
@ -1467,10 +1463,6 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
return args
|
return args
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
meth = self.parent.__dict__.get(self.objpath[-1])
|
|
||||||
if inspect.is_singledispatch_method(meth):
|
|
||||||
self.add_singledispatch_directive_header(sig)
|
|
||||||
else:
|
|
||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
|
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
@ -1489,19 +1481,13 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
def document_members(self, all_members: bool = False) -> None:
|
def document_members(self, all_members: bool = False) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def add_singledispatch_directive_header(self, sig: str) -> None:
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
sourcename = self.get_sourcename()
|
sig = super().format_signature(**kwargs)
|
||||||
|
sigs = [sig]
|
||||||
|
|
||||||
# intercept generated directive headers
|
|
||||||
# TODO: It is very hacky to use mock to intercept header generation
|
|
||||||
with patch.object(self, 'add_line') as add_line:
|
|
||||||
super().add_directive_header(sig)
|
|
||||||
|
|
||||||
# output first line of header
|
|
||||||
self.add_line(*add_line.call_args_list[0][0])
|
|
||||||
|
|
||||||
# inserts signature of singledispatch'ed functions
|
|
||||||
meth = self.parent.__dict__.get(self.objpath[-1])
|
meth = self.parent.__dict__.get(self.objpath[-1])
|
||||||
|
if inspect.is_singledispatch_method(meth):
|
||||||
|
# append signature of singledispatch'ed functions
|
||||||
for typ, func in meth.dispatcher.registry.items():
|
for typ, func in meth.dispatcher.registry.items():
|
||||||
if typ is object:
|
if typ is object:
|
||||||
pass # default implementation. skipped.
|
pass # default implementation. skipped.
|
||||||
@ -1509,14 +1495,12 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
self.annotate_to_first_argument(func, typ)
|
self.annotate_to_first_argument(func, typ)
|
||||||
|
|
||||||
documenter = MethodDocumenter(self.directive, '')
|
documenter = MethodDocumenter(self.directive, '')
|
||||||
|
documenter.parent = self.parent
|
||||||
documenter.object = func
|
documenter.object = func
|
||||||
self.add_line(' %s%s' % (self.format_name(),
|
documenter.objpath = [None]
|
||||||
documenter.format_signature()),
|
sigs.append(documenter.format_signature())
|
||||||
sourcename)
|
|
||||||
|
|
||||||
# output remains of directive header
|
return "\n".join(sigs)
|
||||||
for call in add_line.call_args_list[1:]:
|
|
||||||
self.add_line(*call[0])
|
|
||||||
|
|
||||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||||
"""Annotate type hint to the first argument of function if needed."""
|
"""Annotate type hint to the first argument of function if needed."""
|
||||||
@ -1753,6 +1737,14 @@ def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any:
|
|||||||
return safe_getattr(obj, name, *defargs)
|
return safe_getattr(obj, name, *defargs)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_autodoc_member_order(app: Sphinx, config: Config) -> None:
|
||||||
|
if config.autodoc_member_order == 'alphabetic':
|
||||||
|
# RemovedInSphinx50Warning
|
||||||
|
logger.warning(__('autodoc_member_order now accepts "alphabetical" '
|
||||||
|
'instead of "alphabetic". Please update your setting.'))
|
||||||
|
config.autodoc_member_order = 'alphabetical' # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.add_autodocumenter(ModuleDocumenter)
|
app.add_autodocumenter(ModuleDocumenter)
|
||||||
app.add_autodocumenter(ClassDocumenter)
|
app.add_autodocumenter(ClassDocumenter)
|
||||||
@ -1768,7 +1760,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
app.add_autodocumenter(SlotsAttributeDocumenter)
|
||||||
|
|
||||||
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init'))
|
||||||
app.add_config_value('autodoc_member_order', 'alphabetic', True)
|
app.add_config_value('autodoc_member_order', 'alphabetical', True,
|
||||||
|
ENUM('alphabetic', 'alphabetical', 'bysource', 'groupwise'))
|
||||||
app.add_config_value('autodoc_default_options', {}, True)
|
app.add_config_value('autodoc_default_options', {}, True)
|
||||||
app.add_config_value('autodoc_docstring_signature', True, True)
|
app.add_config_value('autodoc_docstring_signature', True, True)
|
||||||
app.add_config_value('autodoc_mock_imports', [], True)
|
app.add_config_value('autodoc_mock_imports', [], True)
|
||||||
@ -1781,6 +1774,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_event('autodoc-process-signature')
|
app.add_event('autodoc-process-signature')
|
||||||
app.add_event('autodoc-skip-member')
|
app.add_event('autodoc-skip-member')
|
||||||
|
|
||||||
|
app.connect('config-inited', migrate_autodoc_member_order)
|
||||||
|
|
||||||
app.setup_extension('sphinx.ext.autodoc.type_comment')
|
app.setup_extension('sphinx.ext.autodoc.type_comment')
|
||||||
app.setup_extension('sphinx.ext.autodoc.typehints')
|
app.setup_extension('sphinx.ext.autodoc.typehints')
|
||||||
|
|
||||||
|
@ -298,8 +298,7 @@ class Autosummary(SphinxDirective):
|
|||||||
with mock(self.config.autosummary_mock_imports):
|
with mock(self.config.autosummary_mock_imports):
|
||||||
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.warning(__('failed to import %s'), name)
|
logger.warning(__('autosummary: failed to import %s'), name)
|
||||||
items.append((name, '', '', name))
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.bridge.result = StringList() # initialize for each documenter
|
self.bridge.result = StringList() # initialize for each documenter
|
||||||
|
@ -45,6 +45,7 @@ from sphinx.locale import __
|
|||||||
from sphinx.registry import SphinxComponentRegistry
|
from sphinx.registry import SphinxComponentRegistry
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util import rst
|
from sphinx.util import rst
|
||||||
|
from sphinx.util import split_full_qualified_name
|
||||||
from sphinx.util.inspect import safe_getattr
|
from sphinx.util.inspect import safe_getattr
|
||||||
from sphinx.util.osutil import ensuredir
|
from sphinx.util.osutil import ensuredir
|
||||||
from sphinx.util.template import SphinxTemplateLoader
|
from sphinx.util.template import SphinxTemplateLoader
|
||||||
@ -244,19 +245,19 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
ns['attributes'], ns['all_attributes'] = \
|
ns['attributes'], ns['all_attributes'] = \
|
||||||
get_members(obj, {'attribute', 'property'})
|
get_members(obj, {'attribute', 'property'})
|
||||||
|
|
||||||
parts = name.split('.')
|
modname, qualname = split_full_qualified_name(name)
|
||||||
if doc.objtype in ('method', 'attribute', 'property'):
|
if doc.objtype in ('method', 'attribute', 'property'):
|
||||||
mod_name = '.'.join(parts[:-2])
|
ns['class'] = qualname.rsplit(".", 1)[0]
|
||||||
cls_name = parts[-2]
|
|
||||||
obj_name = '.'.join(parts[-2:])
|
if doc.objtype in ('class',):
|
||||||
ns['class'] = cls_name
|
shortname = qualname
|
||||||
else:
|
else:
|
||||||
mod_name, obj_name = '.'.join(parts[:-1]), parts[-1]
|
shortname = qualname.rsplit(".", 1)[-1]
|
||||||
|
|
||||||
ns['fullname'] = name
|
ns['fullname'] = name
|
||||||
ns['module'] = mod_name
|
ns['module'] = modname
|
||||||
ns['objname'] = obj_name
|
ns['objname'] = qualname
|
||||||
ns['name'] = parts[-1]
|
ns['name'] = shortname
|
||||||
|
|
||||||
ns['objtype'] = doc.objtype
|
ns['objtype'] = doc.objtype
|
||||||
ns['underline'] = len(name) * '='
|
ns['underline'] = len(name) * '='
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from hashlib import sha1
|
|
||||||
from os import path
|
from os import path
|
||||||
from subprocess import CalledProcessError, PIPE
|
from subprocess import CalledProcessError, PIPE
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
@ -25,7 +24,7 @@ import sphinx
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging, sha1
|
||||||
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
|
from sphinx.util.docutils import SphinxDirective, SphinxTranslator
|
||||||
from sphinx.util.fileutil import copy_asset
|
from sphinx.util.fileutil import copy_asset
|
||||||
from sphinx.util.i18n import search_image_for_language
|
from sphinx.util.i18n import search_image_for_language
|
||||||
|
@ -14,7 +14,6 @@ import shutil
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from hashlib import sha1
|
|
||||||
from os import path
|
from os import path
|
||||||
from subprocess import CalledProcessError, PIPE
|
from subprocess import CalledProcessError, PIPE
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
@ -29,7 +28,7 @@ from sphinx.builders import Builder
|
|||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging, sha1
|
||||||
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
from sphinx.util.math import get_node_equation_number, wrap_displaymath
|
||||||
from sphinx.util.osutil import ensuredir
|
from sphinx.util.osutil import ensuredir
|
||||||
from sphinx.util.png import read_png_depth, write_png_depth
|
from sphinx.util.png import read_png_depth, write_png_depth
|
||||||
|
@ -38,7 +38,6 @@ r"""
|
|||||||
import builtins
|
import builtins
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
from hashlib import md5
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from typing import Any, Dict, Iterable, List, Tuple
|
from typing import Any, Dict, Iterable, List, Tuple
|
||||||
from typing import cast
|
from typing import cast
|
||||||
@ -55,6 +54,7 @@ from sphinx.ext.graphviz import (
|
|||||||
graphviz, figure_wrapper,
|
graphviz, figure_wrapper,
|
||||||
render_dot_html, render_dot_latex, render_dot_texinfo
|
render_dot_html, render_dot_latex, render_dot_texinfo
|
||||||
)
|
)
|
||||||
|
from sphinx.util import md5
|
||||||
from sphinx.util.docutils import SphinxDirective
|
from sphinx.util.docutils import SphinxDirective
|
||||||
from sphinx.writers.html import HTMLTranslator
|
from sphinx.writers.html import HTMLTranslator
|
||||||
from sphinx.writers.latex import LaTeXTranslator
|
from sphinx.writers.latex import LaTeXTranslator
|
||||||
|
@ -131,8 +131,10 @@ def env_merge_info(app: Sphinx, env: BuildEnvironment, docnames: Iterable[str],
|
|||||||
|
|
||||||
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element, contnode: Node
|
||||||
) -> Node:
|
) -> Node:
|
||||||
|
if app.builder.format != 'html':
|
||||||
|
return None
|
||||||
|
elif node['reftype'] == 'viewcode':
|
||||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||||
if node['reftype'] == 'viewcode':
|
|
||||||
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||||
node['refid'], contnode)
|
node['refid'], contnode)
|
||||||
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -75,81 +75,35 @@ def unparse(node: Optional[ast.AST]) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
elif isinstance(node, str):
|
elif isinstance(node, str):
|
||||||
return node
|
return node
|
||||||
elif node.__class__ in OPERATORS:
|
return _UnparseVisitor().visit(node)
|
||||||
|
|
||||||
|
|
||||||
|
# a greatly cut-down version of `ast._Unparser`
|
||||||
|
class _UnparseVisitor(ast.NodeVisitor):
|
||||||
|
|
||||||
|
def _visit_op(self, node: ast.AST) -> str:
|
||||||
return OPERATORS[node.__class__]
|
return OPERATORS[node.__class__]
|
||||||
elif isinstance(node, ast.arg):
|
for _op in OPERATORS:
|
||||||
|
locals()['visit_{}'.format(_op.__name__)] = _visit_op
|
||||||
|
|
||||||
|
def visit_arg(self, node: ast.arg) -> str:
|
||||||
if node.annotation:
|
if node.annotation:
|
||||||
return "%s: %s" % (node.arg, unparse(node.annotation))
|
return "%s: %s" % (node.arg, self.visit(node.annotation))
|
||||||
else:
|
else:
|
||||||
return node.arg
|
return node.arg
|
||||||
elif isinstance(node, ast.arguments):
|
|
||||||
return unparse_arguments(node)
|
|
||||||
elif isinstance(node, ast.Attribute):
|
|
||||||
return "%s.%s" % (unparse(node.value), node.attr)
|
|
||||||
elif isinstance(node, ast.BinOp):
|
|
||||||
return " ".join(unparse(e) for e in [node.left, node.op, node.right])
|
|
||||||
elif isinstance(node, ast.BoolOp):
|
|
||||||
op = " %s " % unparse(node.op)
|
|
||||||
return op.join(unparse(e) for e in node.values)
|
|
||||||
elif isinstance(node, ast.Bytes):
|
|
||||||
return repr(node.s)
|
|
||||||
elif isinstance(node, ast.Call):
|
|
||||||
args = ([unparse(e) for e in node.args] +
|
|
||||||
["%s=%s" % (k.arg, unparse(k.value)) for k in node.keywords])
|
|
||||||
return "%s(%s)" % (unparse(node.func), ", ".join(args))
|
|
||||||
elif isinstance(node, ast.Dict):
|
|
||||||
keys = (unparse(k) for k in node.keys)
|
|
||||||
values = (unparse(v) for v in node.values)
|
|
||||||
items = (k + ": " + v for k, v in zip(keys, values))
|
|
||||||
return "{" + ", ".join(items) + "}"
|
|
||||||
elif isinstance(node, ast.Ellipsis):
|
|
||||||
return "..."
|
|
||||||
elif isinstance(node, ast.Index):
|
|
||||||
return unparse(node.value)
|
|
||||||
elif isinstance(node, ast.Lambda):
|
|
||||||
return "lambda %s: ..." % unparse(node.args)
|
|
||||||
elif isinstance(node, ast.List):
|
|
||||||
return "[" + ", ".join(unparse(e) for e in node.elts) + "]"
|
|
||||||
elif isinstance(node, ast.Name):
|
|
||||||
return node.id
|
|
||||||
elif isinstance(node, ast.NameConstant):
|
|
||||||
return repr(node.value)
|
|
||||||
elif isinstance(node, ast.Num):
|
|
||||||
return repr(node.n)
|
|
||||||
elif isinstance(node, ast.Set):
|
|
||||||
return "{" + ", ".join(unparse(e) for e in node.elts) + "}"
|
|
||||||
elif isinstance(node, ast.Str):
|
|
||||||
return repr(node.s)
|
|
||||||
elif isinstance(node, ast.Subscript):
|
|
||||||
return "%s[%s]" % (unparse(node.value), unparse(node.slice))
|
|
||||||
elif isinstance(node, ast.UnaryOp):
|
|
||||||
return "%s %s" % (unparse(node.op), unparse(node.operand))
|
|
||||||
elif isinstance(node, ast.Tuple):
|
|
||||||
if node.elts:
|
|
||||||
return ", ".join(unparse(e) for e in node.elts)
|
|
||||||
else:
|
|
||||||
return "()"
|
|
||||||
elif sys.version_info > (3, 6) and isinstance(node, ast.Constant):
|
|
||||||
# this branch should be placed at last
|
|
||||||
return repr(node.value)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
|
|
||||||
|
|
||||||
|
def _visit_arg_with_default(self, arg: ast.arg, default: Optional[ast.AST]) -> str:
|
||||||
def _unparse_arg(arg: ast.arg, default: Optional[ast.AST]) -> str:
|
|
||||||
"""Unparse a single argument to a string."""
|
"""Unparse a single argument to a string."""
|
||||||
name = unparse(arg)
|
name = self.visit(arg)
|
||||||
if default:
|
if default:
|
||||||
if arg.annotation:
|
if arg.annotation:
|
||||||
name += " = %s" % unparse(default)
|
name += " = %s" % self.visit(default)
|
||||||
else:
|
else:
|
||||||
name += "=%s" % unparse(default)
|
name += "=%s" % self.visit(default)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
def visit_arguments(self, node: ast.arguments) -> str:
|
||||||
def unparse_arguments(node: ast.arguments) -> str:
|
defaults = list(node.defaults)
|
||||||
"""Unparse an arguments to string."""
|
|
||||||
defaults = list(node.defaults) # type: List[Optional[ast.AST]]
|
|
||||||
positionals = len(node.args)
|
positionals = len(node.args)
|
||||||
posonlyargs = 0
|
posonlyargs = 0
|
||||||
if hasattr(node, "posonlyargs"): # for py38+
|
if hasattr(node, "posonlyargs"): # for py38+
|
||||||
@ -158,30 +112,105 @@ def unparse_arguments(node: ast.arguments) -> str:
|
|||||||
for _ in range(len(defaults), positionals):
|
for _ in range(len(defaults), positionals):
|
||||||
defaults.insert(0, None)
|
defaults.insert(0, None)
|
||||||
|
|
||||||
kw_defaults = list(node.kw_defaults) # type: List[Optional[ast.AST]]
|
kw_defaults = list(node.kw_defaults)
|
||||||
for _ in range(len(kw_defaults), len(node.kwonlyargs)):
|
for _ in range(len(kw_defaults), len(node.kwonlyargs)):
|
||||||
kw_defaults.insert(0, None)
|
kw_defaults.insert(0, None)
|
||||||
|
|
||||||
args = [] # type: List[str]
|
args = [] # type: List[str]
|
||||||
if hasattr(node, "posonlyargs"): # for py38+
|
if hasattr(node, "posonlyargs"): # for py38+
|
||||||
for i, arg in enumerate(node.posonlyargs): # type: ignore
|
for i, arg in enumerate(node.posonlyargs): # type: ignore
|
||||||
args.append(_unparse_arg(arg, defaults[i]))
|
args.append(self._visit_arg_with_default(arg, defaults[i]))
|
||||||
|
|
||||||
if node.posonlyargs: # type: ignore
|
if node.posonlyargs: # type: ignore
|
||||||
args.append('/')
|
args.append('/')
|
||||||
|
|
||||||
for i, arg in enumerate(node.args):
|
for i, arg in enumerate(node.args):
|
||||||
args.append(_unparse_arg(arg, defaults[i + posonlyargs]))
|
args.append(self._visit_arg_with_default(arg, defaults[i + posonlyargs]))
|
||||||
|
|
||||||
if node.vararg:
|
if node.vararg:
|
||||||
args.append("*" + unparse(node.vararg))
|
args.append("*" + self.visit(node.vararg))
|
||||||
|
|
||||||
if node.kwonlyargs and not node.vararg:
|
if node.kwonlyargs and not node.vararg:
|
||||||
args.append('*')
|
args.append('*')
|
||||||
for i, arg in enumerate(node.kwonlyargs):
|
for i, arg in enumerate(node.kwonlyargs):
|
||||||
args.append(_unparse_arg(arg, kw_defaults[i]))
|
args.append(self._visit_arg_with_default(arg, kw_defaults[i]))
|
||||||
|
|
||||||
if node.kwarg:
|
if node.kwarg:
|
||||||
args.append("**" + unparse(node.kwarg))
|
args.append("**" + self.visit(node.kwarg))
|
||||||
|
|
||||||
return ", ".join(args)
|
return ", ".join(args)
|
||||||
|
|
||||||
|
def visit_Attribute(self, node: ast.Attribute) -> str:
|
||||||
|
return "%s.%s" % (self.visit(node.value), node.attr)
|
||||||
|
|
||||||
|
def visit_BinOp(self, node: ast.BinOp) -> str:
|
||||||
|
return " ".join(self.visit(e) for e in [node.left, node.op, node.right])
|
||||||
|
|
||||||
|
def visit_BoolOp(self, node: ast.BoolOp) -> str:
|
||||||
|
op = " %s " % self.visit(node.op)
|
||||||
|
return op.join(self.visit(e) for e in node.values)
|
||||||
|
|
||||||
|
def visit_Call(self, node: ast.Call) -> str:
|
||||||
|
args = ([self.visit(e) for e in node.args] +
|
||||||
|
["%s=%s" % (k.arg, self.visit(k.value)) for k in node.keywords])
|
||||||
|
return "%s(%s)" % (self.visit(node.func), ", ".join(args))
|
||||||
|
|
||||||
|
def visit_Dict(self, node: ast.Dict) -> str:
|
||||||
|
keys = (self.visit(k) for k in node.keys)
|
||||||
|
values = (self.visit(v) for v in node.values)
|
||||||
|
items = (k + ": " + v for k, v in zip(keys, values))
|
||||||
|
return "{" + ", ".join(items) + "}"
|
||||||
|
|
||||||
|
def visit_Index(self, node: ast.Index) -> str:
|
||||||
|
return self.visit(node.value)
|
||||||
|
|
||||||
|
def visit_Lambda(self, node: ast.Lambda) -> str:
|
||||||
|
return "lambda %s: ..." % self.visit(node.args)
|
||||||
|
|
||||||
|
def visit_List(self, node: ast.List) -> str:
|
||||||
|
return "[" + ", ".join(self.visit(e) for e in node.elts) + "]"
|
||||||
|
|
||||||
|
def visit_Name(self, node: ast.Name) -> str:
|
||||||
|
return node.id
|
||||||
|
|
||||||
|
def visit_Set(self, node: ast.Set) -> str:
|
||||||
|
return "{" + ", ".join(self.visit(e) for e in node.elts) + "}"
|
||||||
|
|
||||||
|
def visit_Subscript(self, node: ast.Subscript) -> str:
|
||||||
|
return "%s[%s]" % (self.visit(node.value), self.visit(node.slice))
|
||||||
|
|
||||||
|
def visit_UnaryOp(self, node: ast.UnaryOp) -> str:
|
||||||
|
return "%s %s" % (self.visit(node.op), self.visit(node.operand))
|
||||||
|
|
||||||
|
def visit_Tuple(self, node: ast.Tuple) -> str:
|
||||||
|
if node.elts:
|
||||||
|
return ", ".join(self.visit(e) for e in node.elts)
|
||||||
|
else:
|
||||||
|
return "()"
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 6):
|
||||||
|
def visit_Constant(self, node: ast.Constant) -> str:
|
||||||
|
if node.value is Ellipsis:
|
||||||
|
return "..."
|
||||||
|
else:
|
||||||
|
return repr(node.value)
|
||||||
|
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
# these ast nodes were deprecated in python 3.8
|
||||||
|
def visit_Bytes(self, node: ast.Bytes) -> str:
|
||||||
|
return repr(node.s)
|
||||||
|
|
||||||
|
def visit_Ellipsis(self, node: ast.Ellipsis) -> str:
|
||||||
|
return "..."
|
||||||
|
|
||||||
|
def visit_NameConstant(self, node: ast.NameConstant) -> str:
|
||||||
|
return repr(node.value)
|
||||||
|
|
||||||
|
def visit_Num(self, node: ast.Num) -> str:
|
||||||
|
return repr(node.n)
|
||||||
|
|
||||||
|
def visit_Str(self, node: ast.Str) -> str:
|
||||||
|
return repr(node.s)
|
||||||
|
|
||||||
|
def generic_visit(self, node):
|
||||||
|
raise NotImplementedError('Unable to parse %s object' % type(node).__name__)
|
||||||
|
@ -22,7 +22,7 @@ from sphinx import addnodes
|
|||||||
from sphinx import package_dir
|
from sphinx import package_dir
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.search.jssplitter import splitter_code
|
from sphinx.search.jssplitter import splitter_code
|
||||||
from sphinx.util import jsdump, rpartition
|
from sphinx.util import jsdump
|
||||||
|
|
||||||
|
|
||||||
class SearchLanguage:
|
class SearchLanguage:
|
||||||
@ -324,7 +324,7 @@ class IndexBuilder:
|
|||||||
continue
|
continue
|
||||||
fullname = html.escape(fullname)
|
fullname = html.escape(fullname)
|
||||||
dispname = html.escape(dispname)
|
dispname = html.escape(dispname)
|
||||||
prefix, name = rpartition(dispname, '.')
|
prefix, _, name = dispname.rpartition('.')
|
||||||
pdict = rv.setdefault(prefix, {})
|
pdict = rv.setdefault(prefix, {})
|
||||||
try:
|
try:
|
||||||
typeindex = otypes[domainname, type]
|
typeindex = otypes[domainname, type]
|
||||||
|
@ -23,6 +23,7 @@ from docutils.utils.smartquotes import smartchars
|
|||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
|
from sphinx.util import docutils
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.docutils import new_document
|
from sphinx.util.docutils import new_document
|
||||||
from sphinx.util.i18n import format_date
|
from sphinx.util.i18n import format_date
|
||||||
@ -359,12 +360,18 @@ class SphinxSmartQuotes(SmartQuotes, SphinxTransform):
|
|||||||
def get_tokens(self, txtnodes: List[Text]) -> Generator[Tuple[str, str], None, None]:
|
def get_tokens(self, txtnodes: List[Text]) -> Generator[Tuple[str, str], None, None]:
|
||||||
# A generator that yields ``(texttype, nodetext)`` tuples for a list
|
# A generator that yields ``(texttype, nodetext)`` tuples for a list
|
||||||
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
|
# of "Text" nodes (interface to ``smartquotes.educate_tokens()``).
|
||||||
|
|
||||||
texttype = {True: 'literal', # "literal" text is not changed:
|
|
||||||
False: 'plain'}
|
|
||||||
for txtnode in txtnodes:
|
for txtnode in txtnodes:
|
||||||
notsmartquotable = not is_smartquotable(txtnode)
|
if is_smartquotable(txtnode):
|
||||||
yield (texttype[notsmartquotable], txtnode.astext())
|
if docutils.__version_info__ >= (0, 16):
|
||||||
|
# SmartQuotes uses backslash escapes instead of null-escapes
|
||||||
|
text = re.sub(r'(?<=\x00)([-\\\'".`])', r'\\\1', str(txtnode))
|
||||||
|
else:
|
||||||
|
text = txtnode.astext()
|
||||||
|
|
||||||
|
yield ('plain', text)
|
||||||
|
else:
|
||||||
|
# skip smart quotes
|
||||||
|
yield ('literal', txtnode.astext())
|
||||||
|
|
||||||
|
|
||||||
class DoctreeReadEvent(SphinxTransform):
|
class DoctreeReadEvent(SphinxTransform):
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from hashlib import sha1
|
|
||||||
from math import ceil
|
from math import ceil
|
||||||
from typing import Any, Dict, List, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
@ -19,7 +18,7 @@ from docutils import nodes
|
|||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.transforms import SphinxTransform
|
from sphinx.transforms import SphinxTransform
|
||||||
from sphinx.util import epoch_to_rfc1123, rfc1123_to_epoch
|
from sphinx.util import epoch_to_rfc1123, rfc1123_to_epoch, sha1
|
||||||
from sphinx.util import logging, requests
|
from sphinx.util import logging, requests
|
||||||
from sphinx.util.images import guess_mimetype, get_image_extension, parse_data_uri
|
from sphinx.util.images import guess_mimetype, get_image_extension, parse_data_uri
|
||||||
from sphinx.util.osutil import ensuredir, movefile
|
from sphinx.util.osutil import ensuredir, movefile
|
||||||
@ -194,7 +193,9 @@ class ImageConverter(BaseImageConverter):
|
|||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def match(self, node: nodes.image) -> bool:
|
def match(self, node: nodes.image) -> bool:
|
||||||
if self.available is None:
|
if not self.app.builder.supported_image_types:
|
||||||
|
return False
|
||||||
|
elif self.available is None:
|
||||||
self.available = self.is_available()
|
self.available = self.is_available()
|
||||||
|
|
||||||
if not self.available:
|
if not self.available:
|
||||||
|
@ -9,15 +9,16 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import traceback
|
import traceback
|
||||||
|
import warnings
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import md5
|
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
from os import path
|
from os import path
|
||||||
from time import mktime, strptime
|
from time import mktime, strptime
|
||||||
@ -25,6 +26,7 @@ from typing import Any, Callable, Dict, IO, Iterable, Iterator, List, Pattern, S
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
|
from urllib.parse import urlsplit, urlunsplit, quote_plus, parse_qsl, urlencode
|
||||||
|
|
||||||
|
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||||
from sphinx.errors import SphinxParallelError, ExtensionError, FiletypeNotFoundError
|
from sphinx.errors import SphinxParallelError, ExtensionError, FiletypeNotFoundError
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
@ -145,6 +147,36 @@ class FilenameUniqDict(dict):
|
|||||||
self._existing = state
|
self._existing = state
|
||||||
|
|
||||||
|
|
||||||
|
def md5(data=b'', **kwargs):
|
||||||
|
"""Wrapper around hashlib.md5
|
||||||
|
|
||||||
|
Attempt call with 'usedforsecurity=False' if we get a ValueError, which happens when
|
||||||
|
OpenSSL FIPS mode is enabled:
|
||||||
|
ValueError: error:060800A3:digital envelope routines:EVP_DigestInit_ex:disabled for fips
|
||||||
|
|
||||||
|
See: https://github.com/sphinx-doc/sphinx/issues/7611
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return hashlib.md5(data, **kwargs) # type: ignore
|
||||||
|
except ValueError:
|
||||||
|
return hashlib.md5(data, **kwargs, usedforsecurity=False) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def sha1(data=b'', **kwargs):
|
||||||
|
"""Wrapper around hashlib.sha1
|
||||||
|
|
||||||
|
Attempt call with 'usedforsecurity=False' if we get a ValueError
|
||||||
|
|
||||||
|
See: https://github.com/sphinx-doc/sphinx/issues/7611
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
return hashlib.sha1(data, **kwargs) # type: ignore
|
||||||
|
except ValueError:
|
||||||
|
return hashlib.sha1(data, **kwargs, usedforsecurity=False) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class DownloadFiles(dict):
|
class DownloadFiles(dict):
|
||||||
"""A special dictionary for download files.
|
"""A special dictionary for download files.
|
||||||
|
|
||||||
@ -310,6 +342,7 @@ def parselinenos(spec: str, total: int) -> List[int]:
|
|||||||
|
|
||||||
def rpartition(s: str, t: str) -> Tuple[str, str]:
|
def rpartition(s: str, t: str) -> Tuple[str, str]:
|
||||||
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
|
"""Similar to str.rpartition from 2.5, but doesn't return the separator."""
|
||||||
|
warnings.warn('rpartition() is now deprecated.', RemovedInSphinx50Warning, stacklevel=2)
|
||||||
i = s.rfind(t)
|
i = s.rfind(t)
|
||||||
if i != -1:
|
if i != -1:
|
||||||
return s[:i], s[i + len(t):]
|
return s[:i], s[i + len(t):]
|
||||||
@ -378,6 +411,31 @@ def import_object(objname: str, source: str = None) -> Any:
|
|||||||
raise ExtensionError('Could not import %s' % objname, exc)
|
raise ExtensionError('Could not import %s' % objname, exc)
|
||||||
|
|
||||||
|
|
||||||
|
def split_full_qualified_name(name: str) -> Tuple[str, str]:
|
||||||
|
"""Split full qualified name to a pair of modname and qualname.
|
||||||
|
|
||||||
|
A qualname is an abbreviation for "Qualified name" introduced at PEP-3155
|
||||||
|
(https://www.python.org/dev/peps/pep-3155/). It is a dotted path name
|
||||||
|
from the module top-level.
|
||||||
|
|
||||||
|
A "full" qualified name means a string containing both module name and
|
||||||
|
qualified name.
|
||||||
|
|
||||||
|
.. note:: This function imports module actually to check the exisitence.
|
||||||
|
Therefore you need to mock 3rd party modules if needed before
|
||||||
|
calling this function.
|
||||||
|
"""
|
||||||
|
parts = name.split('.')
|
||||||
|
for i, part in enumerate(parts, 1):
|
||||||
|
try:
|
||||||
|
modname = ".".join(parts[:i])
|
||||||
|
import_module(modname)
|
||||||
|
except ImportError:
|
||||||
|
return ".".join(parts[:i - 1]), ".".join(parts[i - 1:])
|
||||||
|
|
||||||
|
return name, ""
|
||||||
|
|
||||||
|
|
||||||
def encode_uri(uri: str) -> str:
|
def encode_uri(uri: str) -> str:
|
||||||
split = list(urlsplit(uri))
|
split = list(urlsplit(uri))
|
||||||
split[1] = split[1].encode('idna').decode('ascii')
|
split[1] = split[1].encode('idna').decode('ascii')
|
||||||
|
@ -372,26 +372,38 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
|||||||
Why this function needed? CPython implements int.__init__ by Descriptor
|
Why this function needed? CPython implements int.__init__ by Descriptor
|
||||||
but PyPy implements it by pure Python code.
|
but PyPy implements it by pure Python code.
|
||||||
"""
|
"""
|
||||||
classes = [c for c in inspect.getmro(obj) if attr_name in c.__dict__]
|
try:
|
||||||
cls = classes[0] if classes else object
|
mro = inspect.getmro(obj)
|
||||||
|
except AttributeError:
|
||||||
if not hasattr(builtins, safe_getattr(cls, '__name__', '')):
|
# no __mro__, assume the object has no methods as we know them
|
||||||
return False
|
return False
|
||||||
return getattr(builtins, safe_getattr(cls, '__name__', '')) is cls
|
|
||||||
|
try:
|
||||||
|
cls = next(c for c in mro if attr_name in safe_getattr(c, '__dict__', {}))
|
||||||
|
except StopIteration:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
name = safe_getattr(cls, '__name__')
|
||||||
|
except AttributeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return getattr(builtins, name, None) is cls
|
||||||
|
|
||||||
|
|
||||||
def signature(subject: Callable, bound_method: bool = False) -> inspect.Signature:
|
def signature(subject: Callable, bound_method: bool = False, follow_wrapped: bool = False
|
||||||
|
) -> inspect.Signature:
|
||||||
"""Return a Signature object for the given *subject*.
|
"""Return a Signature object for the given *subject*.
|
||||||
|
|
||||||
:param bound_method: Specify *subject* is a bound method or not
|
:param bound_method: Specify *subject* is a bound method or not
|
||||||
|
:param follow_wrapped: Same as ``inspect.signature()``.
|
||||||
|
Defaults to ``False`` (get a signature of *subject*).
|
||||||
"""
|
"""
|
||||||
# check subject is not a built-in class (ex. int, str)
|
|
||||||
if (isinstance(subject, type) and
|
|
||||||
is_builtin_class_method(subject, "__new__") and
|
|
||||||
is_builtin_class_method(subject, "__init__")):
|
|
||||||
raise TypeError("can't compute signature for built-in type {}".format(subject))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
try:
|
||||||
|
signature = inspect.signature(subject, follow_wrapped=follow_wrapped)
|
||||||
|
except ValueError:
|
||||||
|
# follow built-in wrappers up (ex. functools.lru_cache)
|
||||||
signature = inspect.signature(subject)
|
signature = inspect.signature(subject)
|
||||||
parameters = list(signature.parameters.values())
|
parameters = list(signature.parameters.values())
|
||||||
return_annotation = signature.return_annotation
|
return_annotation = signature.return_annotation
|
||||||
|
@ -39,6 +39,12 @@ TitleGetter = Callable[[nodes.Node], str]
|
|||||||
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
|
Inventory = Dict[str, Dict[str, Tuple[str, str, str, str]]]
|
||||||
|
|
||||||
|
|
||||||
|
def is_system_TypeVar(typ: Any) -> bool:
|
||||||
|
"""Check *typ* is system defined TypeVar."""
|
||||||
|
modname = getattr(typ, '__module__', '')
|
||||||
|
return modname == 'typing' and isinstance(typ, TypeVar) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def stringify(annotation: Any) -> str:
|
def stringify(annotation: Any) -> str:
|
||||||
"""Stringify type annotation object."""
|
"""Stringify type annotation object."""
|
||||||
if isinstance(annotation, str):
|
if isinstance(annotation, str):
|
||||||
@ -85,18 +91,23 @@ def _stringify_py37(annotation: Any) -> str:
|
|||||||
|
|
||||||
if getattr(annotation, '__args__', None):
|
if getattr(annotation, '__args__', None):
|
||||||
if qualname == 'Union':
|
if qualname == 'Union':
|
||||||
if len(annotation.__args__) == 2 and annotation.__args__[1] is NoneType: # type: ignore # NOQA
|
if len(annotation.__args__) > 1 and annotation.__args__[-1] is NoneType: # type: ignore # NOQA
|
||||||
|
if len(annotation.__args__) > 2:
|
||||||
|
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
||||||
|
return 'Optional[Union[%s]]' % args
|
||||||
|
else:
|
||||||
return 'Optional[%s]' % stringify(annotation.__args__[0])
|
return 'Optional[%s]' % stringify(annotation.__args__[0])
|
||||||
else:
|
else:
|
||||||
args = ', '.join(stringify(a) for a in annotation.__args__)
|
args = ', '.join(stringify(a) for a in annotation.__args__)
|
||||||
return '%s[%s]' % (qualname, args)
|
return 'Union[%s]' % args
|
||||||
elif qualname == 'Callable':
|
elif qualname == 'Callable':
|
||||||
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
args = ', '.join(stringify(a) for a in annotation.__args__[:-1])
|
||||||
returns = stringify(annotation.__args__[-1])
|
returns = stringify(annotation.__args__[-1])
|
||||||
return '%s[[%s], %s]' % (qualname, args, returns)
|
return '%s[[%s], %s]' % (qualname, args, returns)
|
||||||
elif str(annotation).startswith('typing.Annotated'): # for py39+
|
elif str(annotation).startswith('typing.Annotated'): # for py39+
|
||||||
return stringify(annotation.__args__[0])
|
return stringify(annotation.__args__[0])
|
||||||
elif getattr(annotation, '_special', False):
|
elif all(is_system_TypeVar(a) for a in annotation.__args__):
|
||||||
|
# Suppress arguments if all system defined TypeVars (ex. Dict[KT, VT])
|
||||||
return qualname
|
return qualname
|
||||||
else:
|
else:
|
||||||
args = ', '.join(stringify(a) for a in annotation.__args__)
|
args = ', '.join(stringify(a) for a in annotation.__args__)
|
||||||
@ -148,7 +159,11 @@ def _stringify_py36(annotation: Any) -> str:
|
|||||||
annotation.__origin__ is typing.Union):
|
annotation.__origin__ is typing.Union):
|
||||||
params = annotation.__args__
|
params = annotation.__args__
|
||||||
if params is not None:
|
if params is not None:
|
||||||
if len(params) == 2 and params[1] is NoneType: # type: ignore
|
if len(params) > 1 and params[-1] is NoneType: # type: ignore
|
||||||
|
if len(params) > 2:
|
||||||
|
param_str = ", ".join(stringify(p) for p in params[:-1])
|
||||||
|
return 'Optional[Union[%s]]' % param_str
|
||||||
|
else:
|
||||||
return 'Optional[%s]' % stringify(params[0])
|
return 'Optional[%s]' % stringify(params[0])
|
||||||
else:
|
else:
|
||||||
param_str = ', '.join(stringify(p) for p in params)
|
param_str = ', '.join(stringify(p) for p in params)
|
||||||
|
12
tests/roots/test-ext-autodoc/target/classes.py
Normal file
12
tests/roots/test-ext-autodoc/target/classes.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
class Foo:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Bar:
|
||||||
|
def __init__(self, x, y):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Baz:
|
||||||
|
def __new__(cls, x, y):
|
||||||
|
pass
|
@ -1,5 +1,9 @@
|
|||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
def deco1(func):
|
def deco1(func):
|
||||||
"""docstring for deco1"""
|
"""docstring for deco1"""
|
||||||
|
@wraps(func)
|
||||||
def wrapper():
|
def wrapper():
|
||||||
return func()
|
return func()
|
||||||
|
|
||||||
@ -14,3 +18,14 @@ def deco2(condition, message):
|
|||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
@deco1
|
||||||
|
def foo(name=None, age=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Bar:
|
||||||
|
@deco1
|
||||||
|
def meth(self, name=None, age=None):
|
||||||
|
pass
|
||||||
|
@ -3,6 +3,9 @@ from typing import Union
|
|||||||
|
|
||||||
|
|
||||||
class Foo:
|
class Foo:
|
||||||
|
class Bar:
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -9,5 +9,6 @@
|
|||||||
|
|
||||||
autosummary_dummy_module
|
autosummary_dummy_module
|
||||||
autosummary_dummy_module.Foo
|
autosummary_dummy_module.Foo
|
||||||
|
autosummary_dummy_module.Foo.Bar
|
||||||
autosummary_dummy_module.bar
|
autosummary_dummy_module.bar
|
||||||
autosummary_importfail
|
autosummary_importfail
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from hashlib import md5
|
|
||||||
from itertools import cycle, chain
|
from itertools import cycle, chain
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -19,6 +18,7 @@ from html5lib import HTMLParser
|
|||||||
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
|
from sphinx.builders.html import validate_html_extra_path, validate_html_static_path
|
||||||
from sphinx.errors import ConfigError
|
from sphinx.errors import ConfigError
|
||||||
from sphinx.testing.util import strip_escseq
|
from sphinx.testing.util import strip_escseq
|
||||||
|
from sphinx.util import md5
|
||||||
from sphinx.util.inventory import InventoryFile
|
from sphinx.util.inventory import InventoryFile
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,15 +27,29 @@ def parse(name, string):
|
|||||||
return ast
|
return ast
|
||||||
|
|
||||||
|
|
||||||
def _check(name, input, idDict, output):
|
def _check(name, input, idDict, output, key, asTextOutput):
|
||||||
|
if key is None:
|
||||||
|
key = name
|
||||||
|
key += ' '
|
||||||
|
if name in ('function', 'member'):
|
||||||
|
inputActual = input
|
||||||
|
outputAst = output
|
||||||
|
outputAsText = output
|
||||||
|
else:
|
||||||
|
inputActual = input.format(key='')
|
||||||
|
outputAst = output.format(key='')
|
||||||
|
outputAsText = output.format(key=key)
|
||||||
|
if asTextOutput is not None:
|
||||||
|
outputAsText = asTextOutput
|
||||||
|
|
||||||
# first a simple check of the AST
|
# first a simple check of the AST
|
||||||
ast = parse(name, input)
|
ast = parse(name, inputActual)
|
||||||
res = str(ast)
|
res = str(ast)
|
||||||
if res != output:
|
if res != outputAst:
|
||||||
print("")
|
print("")
|
||||||
print("Input: ", input)
|
print("Input: ", input)
|
||||||
print("Result: ", res)
|
print("Result: ", res)
|
||||||
print("Expected: ", output)
|
print("Expected: ", outputAst)
|
||||||
raise DefinitionError("")
|
raise DefinitionError("")
|
||||||
rootSymbol = Symbol(None, None, None, None)
|
rootSymbol = Symbol(None, None, None, None)
|
||||||
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
||||||
@ -43,6 +57,13 @@ def _check(name, input, idDict, output):
|
|||||||
signode = addnodes.desc_signature(input, '')
|
signode = addnodes.desc_signature(input, '')
|
||||||
parentNode += signode
|
parentNode += signode
|
||||||
ast.describe_signature(signode, 'lastIsName', symbol, options={})
|
ast.describe_signature(signode, 'lastIsName', symbol, options={})
|
||||||
|
resAsText = parentNode.astext()
|
||||||
|
if resAsText != outputAsText:
|
||||||
|
print("")
|
||||||
|
print("Input: ", input)
|
||||||
|
print("astext(): ", resAsText)
|
||||||
|
print("Expected: ", outputAsText)
|
||||||
|
raise DefinitionError("")
|
||||||
|
|
||||||
idExpected = [None]
|
idExpected = [None]
|
||||||
for i in range(1, _max_id + 1):
|
for i in range(1, _max_id + 1):
|
||||||
@ -75,14 +96,15 @@ def _check(name, input, idDict, output):
|
|||||||
raise DefinitionError("")
|
raise DefinitionError("")
|
||||||
|
|
||||||
|
|
||||||
def check(name, input, idDict, output=None):
|
def check(name, input, idDict, output=None, key=None, asTextOutput=None):
|
||||||
if output is None:
|
if output is None:
|
||||||
output = input
|
output = input
|
||||||
# First, check without semicolon
|
# First, check without semicolon
|
||||||
_check(name, input, idDict, output)
|
_check(name, input, idDict, output, key, asTextOutput)
|
||||||
if name != 'macro':
|
if name != 'macro':
|
||||||
# Second, check with semicolon
|
# Second, check with semicolon
|
||||||
_check(name, input + ' ;', idDict, output + ';')
|
_check(name, input + ' ;', idDict, output + ';', key,
|
||||||
|
asTextOutput + ';' if asTextOutput is not None else None)
|
||||||
|
|
||||||
|
|
||||||
def test_expressions():
|
def test_expressions():
|
||||||
@ -234,24 +256,24 @@ def test_expressions():
|
|||||||
|
|
||||||
|
|
||||||
def test_type_definitions():
|
def test_type_definitions():
|
||||||
check('type', "T", {1: "T"})
|
check('type', "{key}T", {1: "T"})
|
||||||
|
|
||||||
check('type', "bool *b", {1: 'b'})
|
check('type', "{key}bool *b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *const b", {1: 'b'})
|
check('type', "{key}bool *const b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *const *b", {1: 'b'})
|
check('type', "{key}bool *const *b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *volatile *b", {1: 'b'})
|
check('type', "{key}bool *volatile *b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *restrict *b", {1: 'b'})
|
check('type', "{key}bool *restrict *b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *volatile const b", {1: 'b'})
|
check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *volatile const b", {1: 'b'})
|
check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool *volatile const *b", {1: 'b'})
|
check('type', "{key}bool *volatile const *b", {1: 'b'}, key='typedef')
|
||||||
check('type', "bool b[]", {1: 'b'})
|
check('type', "{key}bool b[]", {1: 'b'}, key='typedef')
|
||||||
check('type', "long long int foo", {1: 'foo'})
|
check('type', "{key}long long int foo", {1: 'foo'}, key='typedef')
|
||||||
# test decl specs on right
|
# test decl specs on right
|
||||||
check('type', "bool const b", {1: 'b'})
|
check('type', "{key}bool const b", {1: 'b'}, key='typedef')
|
||||||
|
|
||||||
# from breathe#267 (named function parameters for function pointers
|
# from breathe#267 (named function parameters for function pointers
|
||||||
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||||
{1: 'gpio_callback_t'})
|
{1: 'gpio_callback_t'}, key='typedef')
|
||||||
|
|
||||||
|
|
||||||
def test_macro_definitions():
|
def test_macro_definitions():
|
||||||
@ -378,28 +400,34 @@ def test_function_definitions():
|
|||||||
output='void f(int arr[static volatile const 42])')
|
output='void f(int arr[static volatile const 42])')
|
||||||
|
|
||||||
|
|
||||||
def test_union_definitions():
|
class test_nested_name():
|
||||||
check('struct', 'A', {1: 'A'})
|
check('struct', '{key}.A', {1: "A"})
|
||||||
|
check('struct', '{key}.A.B', {1: "A.B"})
|
||||||
|
check('function', 'void f(.A a)', {1: "f"})
|
||||||
|
check('function', 'void f(.A.B a)', {1: "f"})
|
||||||
|
|
||||||
|
|
||||||
def test_union_definitions():
|
def test_union_definitions():
|
||||||
check('union', 'A', {1: 'A'})
|
check('struct', '{key}A', {1: 'A'})
|
||||||
|
|
||||||
|
|
||||||
|
def test_union_definitions():
|
||||||
|
check('union', '{key}A', {1: 'A'})
|
||||||
|
|
||||||
|
|
||||||
def test_enum_definitions():
|
def test_enum_definitions():
|
||||||
check('enum', 'A', {1: 'A'})
|
check('enum', '{key}A', {1: 'A'})
|
||||||
|
|
||||||
check('enumerator', 'A', {1: 'A'})
|
check('enumerator', '{key}A', {1: 'A'})
|
||||||
check('enumerator', 'A = 42', {1: 'A'})
|
check('enumerator', '{key}A = 42', {1: 'A'})
|
||||||
|
|
||||||
|
|
||||||
def test_anon_definitions():
|
def test_anon_definitions():
|
||||||
return # TODO
|
check('struct', '@a', {1: "@a"}, asTextOutput='struct [anonymous]')
|
||||||
check('class', '@a', {3: "Ut1_a"})
|
check('union', '@a', {1: "@a"}, asTextOutput='union [anonymous]')
|
||||||
check('union', '@a', {3: "Ut1_a"})
|
check('enum', '@a', {1: "@a"}, asTextOutput='enum [anonymous]')
|
||||||
check('enum', '@a', {3: "Ut1_a"})
|
check('struct', '@1', {1: "@1"}, asTextOutput='struct [anonymous]')
|
||||||
check('class', '@1', {3: "Ut1_1"})
|
check('struct', '@a.A', {1: "@a.A"}, asTextOutput='struct [anonymous].A')
|
||||||
check('class', '@a::A', {3: "NUt1_a1AE"})
|
|
||||||
|
|
||||||
|
|
||||||
def test_initializers():
|
def test_initializers():
|
||||||
|
@ -32,15 +32,29 @@ def parse(name, string):
|
|||||||
return ast
|
return ast
|
||||||
|
|
||||||
|
|
||||||
def _check(name, input, idDict, output):
|
def _check(name, input, idDict, output, key, asTextOutput):
|
||||||
|
if key is None:
|
||||||
|
key = name
|
||||||
|
key += ' '
|
||||||
|
if name in ('function', 'member'):
|
||||||
|
inputActual = input
|
||||||
|
outputAst = output
|
||||||
|
outputAsText = output
|
||||||
|
else:
|
||||||
|
inputActual = input.format(key='')
|
||||||
|
outputAst = output.format(key='')
|
||||||
|
outputAsText = output.format(key=key)
|
||||||
|
if asTextOutput is not None:
|
||||||
|
outputAsText = asTextOutput
|
||||||
|
|
||||||
# first a simple check of the AST
|
# first a simple check of the AST
|
||||||
ast = parse(name, input)
|
ast = parse(name, inputActual)
|
||||||
res = str(ast)
|
res = str(ast)
|
||||||
if res != output:
|
if res != outputAst:
|
||||||
print("")
|
print("")
|
||||||
print("Input: ", input)
|
print("Input: ", input)
|
||||||
print("Result: ", res)
|
print("Result: ", res)
|
||||||
print("Expected: ", output)
|
print("Expected: ", outputAst)
|
||||||
raise DefinitionError("")
|
raise DefinitionError("")
|
||||||
rootSymbol = Symbol(None, None, None, None, None, None)
|
rootSymbol = Symbol(None, None, None, None, None, None)
|
||||||
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
||||||
@ -48,6 +62,13 @@ def _check(name, input, idDict, output):
|
|||||||
signode = addnodes.desc_signature(input, '')
|
signode = addnodes.desc_signature(input, '')
|
||||||
parentNode += signode
|
parentNode += signode
|
||||||
ast.describe_signature(signode, 'lastIsName', symbol, options={})
|
ast.describe_signature(signode, 'lastIsName', symbol, options={})
|
||||||
|
resAsText = parentNode.astext()
|
||||||
|
if resAsText != outputAsText:
|
||||||
|
print("")
|
||||||
|
print("Input: ", input)
|
||||||
|
print("astext(): ", resAsText)
|
||||||
|
print("Expected: ", outputAsText)
|
||||||
|
raise DefinitionError("")
|
||||||
|
|
||||||
idExpected = [None]
|
idExpected = [None]
|
||||||
for i in range(1, _max_id + 1):
|
for i in range(1, _max_id + 1):
|
||||||
@ -80,13 +101,14 @@ def _check(name, input, idDict, output):
|
|||||||
raise DefinitionError("")
|
raise DefinitionError("")
|
||||||
|
|
||||||
|
|
||||||
def check(name, input, idDict, output=None):
|
def check(name, input, idDict, output=None, key=None, asTextOutput=None):
|
||||||
if output is None:
|
if output is None:
|
||||||
output = input
|
output = input
|
||||||
# First, check without semicolon
|
# First, check without semicolon
|
||||||
_check(name, input, idDict, output)
|
_check(name, input, idDict, output, key, asTextOutput)
|
||||||
# Second, check with semicolon
|
# Second, check with semicolon
|
||||||
_check(name, input + ' ;', idDict, output + ';')
|
_check(name, input + ' ;', idDict, output + ';', key,
|
||||||
|
asTextOutput + ';' if asTextOutput is not None else None)
|
||||||
|
|
||||||
|
|
||||||
def test_fundamental_types():
|
def test_fundamental_types():
|
||||||
@ -112,10 +134,11 @@ def test_fundamental_types():
|
|||||||
def test_expressions():
|
def test_expressions():
|
||||||
def exprCheck(expr, id, id4=None):
|
def exprCheck(expr, id, id4=None):
|
||||||
ids = 'IE1CIA%s_1aE'
|
ids = 'IE1CIA%s_1aE'
|
||||||
idDict = {2: ids % expr, 3: ids % id}
|
# call .format() on the expr to unescape double curly braces
|
||||||
|
idDict = {2: ids % expr.format(), 3: ids % id}
|
||||||
if id4 is not None:
|
if id4 is not None:
|
||||||
idDict[4] = ids % id4
|
idDict[4] = ids % id4
|
||||||
check('class', 'template<> C<a[%s]>' % expr, idDict)
|
check('class', 'template<> {key}C<a[%s]>' % expr, idDict)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
cpp_id_attributes = ["id_attr"]
|
cpp_id_attributes = ["id_attr"]
|
||||||
@ -228,8 +251,8 @@ def test_expressions():
|
|||||||
exprCheck('new int()', 'nw_ipiE')
|
exprCheck('new int()', 'nw_ipiE')
|
||||||
exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE')
|
exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE')
|
||||||
exprCheck('::new int', 'nw_iE')
|
exprCheck('::new int', 'nw_iE')
|
||||||
exprCheck('new int{}', 'nw_iilE')
|
exprCheck('new int{{}}', 'nw_iilE')
|
||||||
exprCheck('new int{5, 42}', 'nw_iilL5EL42EE')
|
exprCheck('new int{{5, 42}}', 'nw_iilL5EL42EE')
|
||||||
# delete-expression
|
# delete-expression
|
||||||
exprCheck('delete p', 'dl1p')
|
exprCheck('delete p', 'dl1p')
|
||||||
exprCheck('delete [] p', 'da1p')
|
exprCheck('delete [] p', 'da1p')
|
||||||
@ -290,7 +313,7 @@ def test_expressions():
|
|||||||
exprCheck('a xor_eq 5', 'eO1aL5E')
|
exprCheck('a xor_eq 5', 'eO1aL5E')
|
||||||
exprCheck('a |= 5', 'oR1aL5E')
|
exprCheck('a |= 5', 'oR1aL5E')
|
||||||
exprCheck('a or_eq 5', 'oR1aL5E')
|
exprCheck('a or_eq 5', 'oR1aL5E')
|
||||||
exprCheck('a = {1, 2, 3}', 'aS1ailL1EL2EL3EE')
|
exprCheck('a = {{1, 2, 3}}', 'aS1ailL1EL2EL3EE')
|
||||||
# comma operator
|
# comma operator
|
||||||
exprCheck('a, 5', 'cm1aL5E')
|
exprCheck('a, 5', 'cm1aL5E')
|
||||||
|
|
||||||
@ -300,8 +323,8 @@ def test_expressions():
|
|||||||
check('function', 'template<> void f(A<B, 2> &v)',
|
check('function', 'template<> void f(A<B, 2> &v)',
|
||||||
{2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"})
|
{2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"})
|
||||||
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
|
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
|
||||||
check('class', "template<int T = 42> A", {2: "I_iE1A"})
|
check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
|
||||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||||
|
|
||||||
exprCheck('operator()()', 'clclE')
|
exprCheck('operator()()', 'clclE')
|
||||||
exprCheck('operator()<int>()', 'clclIiEE')
|
exprCheck('operator()<int>()', 'clclIiEE')
|
||||||
@ -311,58 +334,59 @@ def test_expressions():
|
|||||||
|
|
||||||
|
|
||||||
def test_type_definitions():
|
def test_type_definitions():
|
||||||
check("type", "public bool b", {1: "b", 2: "1b"}, "bool b")
|
check("type", "public bool b", {1: "b", 2: "1b"}, "{key}bool b", key='typedef')
|
||||||
check("type", "bool A::b", {1: "A::b", 2: "N1A1bE"})
|
check("type", "{key}bool A::b", {1: "A::b", 2: "N1A1bE"}, key='typedef')
|
||||||
check("type", "bool *b", {1: "b", 2: "1b"})
|
check("type", "{key}bool *b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool *const b", {1: "b", 2: "1b"})
|
check("type", "{key}bool *const b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool *volatile const b", {1: "b", 2: "1b"})
|
check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool *volatile const b", {1: "b", 2: "1b"})
|
check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool *volatile const *b", {1: "b", 2: "1b"})
|
check("type", "{key}bool *volatile const *b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool &b", {1: "b", 2: "1b"})
|
check("type", "{key}bool &b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "bool b[]", {1: "b", 2: "1b"})
|
check("type", "{key}bool b[]", {1: "b", 2: "1b"}, key='typedef')
|
||||||
check("type", "std::pair<int, int> coord", {1: "coord", 2: "5coord"})
|
check("type", "{key}std::pair<int, int> coord", {1: "coord", 2: "5coord"}, key='typedef')
|
||||||
check("type", "long long int foo", {1: "foo", 2: "3foo"})
|
check("type", "{key}long long int foo", {1: "foo", 2: "3foo"}, key='typedef')
|
||||||
check("type", 'std::vector<std::pair<std::string, long long>> module::blah',
|
check("type", '{key}std::vector<std::pair<std::string, long long>> module::blah',
|
||||||
{1: "module::blah", 2: "N6module4blahE"})
|
{1: "module::blah", 2: "N6module4blahE"}, key='typedef')
|
||||||
check("type", "std::function<void()> F", {1: "F", 2: "1F"})
|
check("type", "{key}std::function<void()> F", {1: "F", 2: "1F"}, key='typedef')
|
||||||
check("type", "std::function<R(A1, A2)> F", {1: "F", 2: "1F"})
|
check("type", "{key}std::function<R(A1, A2)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||||
check("type", "std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"})
|
check("type", "{key}std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||||
check("type", "std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"})
|
check("type", "{key}std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||||
check("type", "MyContainer::const_iterator",
|
check("type", "{key}MyContainer::const_iterator",
|
||||||
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"})
|
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"})
|
||||||
check("type",
|
check("type",
|
||||||
"public MyContainer::const_iterator",
|
"public MyContainer::const_iterator",
|
||||||
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"},
|
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"},
|
||||||
output="MyContainer::const_iterator")
|
output="{key}MyContainer::const_iterator")
|
||||||
# test decl specs on right
|
# test decl specs on right
|
||||||
check("type", "bool const b", {1: "b", 2: "1b"})
|
check("type", "{key}bool const b", {1: "b", 2: "1b"}, key='typedef')
|
||||||
# test name in global scope
|
# test name in global scope
|
||||||
check("type", "bool ::B::b", {1: "B::b", 2: "N1B1bE"})
|
check("type", "{key}bool ::B::b", {1: "B::b", 2: "N1B1bE"}, key='typedef')
|
||||||
|
|
||||||
check('type', 'A = B', {2: '1A'})
|
check('type', '{key}A = B', {2: '1A'}, key='using')
|
||||||
check('type', 'A = decltype(b)', {2: '1A'})
|
check('type', '{key}A = decltype(b)', {2: '1A'}, key='using')
|
||||||
|
|
||||||
# from breathe#267 (named function parameters for function pointers
|
# from breathe#267 (named function parameters for function pointers
|
||||||
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||||
{1: 'gpio_callback_t', 2: '15gpio_callback_t'})
|
{1: 'gpio_callback_t', 2: '15gpio_callback_t'}, key='typedef')
|
||||||
check('type', 'void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'})
|
check('type', '{key}void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'},
|
||||||
|
key='typedef')
|
||||||
|
|
||||||
check('type', 'T = A::template B<int>::template C<double>', {2: '1T'})
|
check('type', '{key}T = A::template B<int>::template C<double>', {2: '1T'}, key='using')
|
||||||
|
|
||||||
check('type', 'T = Q<A::operator()>', {2: '1T'})
|
check('type', '{key}T = Q<A::operator()>', {2: '1T'}, key='using')
|
||||||
check('type', 'T = Q<A::operator()<int>>', {2: '1T'})
|
check('type', '{key}T = Q<A::operator()<int>>', {2: '1T'}, key='using')
|
||||||
check('type', 'T = Q<A::operator bool>', {2: '1T'})
|
check('type', '{key}T = Q<A::operator bool>', {2: '1T'}, key='using')
|
||||||
|
|
||||||
|
|
||||||
def test_concept_definitions():
|
def test_concept_definitions():
|
||||||
check('concept', 'template<typename Param> A::B::Concept',
|
check('concept', 'template<typename Param> {key}A::B::Concept',
|
||||||
{2: 'I0EN1A1B7ConceptE'})
|
{2: 'I0EN1A1B7ConceptE'})
|
||||||
check('concept', 'template<typename A, typename B, typename ...C> Foo',
|
check('concept', 'template<typename A, typename B, typename ...C> {key}Foo',
|
||||||
{2: 'I00DpE3Foo'})
|
{2: 'I00DpE3Foo'})
|
||||||
with pytest.raises(DefinitionError):
|
with pytest.raises(DefinitionError):
|
||||||
parse('concept', 'Foo')
|
parse('concept', '{key}Foo')
|
||||||
with pytest.raises(DefinitionError):
|
with pytest.raises(DefinitionError):
|
||||||
parse('concept', 'template<typename T> template<typename U> Foo')
|
parse('concept', 'template<typename T> template<typename U> {key}Foo')
|
||||||
|
|
||||||
|
|
||||||
def test_member_definitions():
|
def test_member_definitions():
|
||||||
@ -638,95 +662,102 @@ def test_operators():
|
|||||||
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
|
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
|
||||||
|
|
||||||
|
|
||||||
|
class test_nested_name():
|
||||||
|
check('class', '{key}::A', {1: "A", 2: "1A"})
|
||||||
|
check('class', '{key}::A::B', {1: "A::B", 2: "N1A1BE"})
|
||||||
|
check('function', 'void f(::A a)', {1: "f__A", 2: "1f1A"})
|
||||||
|
check('function', 'void f(::A::B a)', {1: "f__A::B", 2: "1fN1A1BE"})
|
||||||
|
|
||||||
|
|
||||||
def test_class_definitions():
|
def test_class_definitions():
|
||||||
check('class', 'public A', {1: "A", 2: "1A"}, output='A')
|
check('class', 'public A', {1: "A", 2: "1A"}, output='{key}A')
|
||||||
check('class', 'private A', {1: "A", 2: "1A"})
|
check('class', 'private {key}A', {1: "A", 2: "1A"})
|
||||||
check('class', 'A final', {1: 'A', 2: '1A'})
|
check('class', '{key}A final', {1: 'A', 2: '1A'})
|
||||||
|
|
||||||
# test bases
|
# test bases
|
||||||
check('class', 'A', {1: "A", 2: "1A"})
|
check('class', '{key}A', {1: "A", 2: "1A"})
|
||||||
check('class', 'A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
|
check('class', '{key}A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
|
||||||
check('class', 'A : B', {1: "A", 2: "1A"})
|
check('class', '{key}A : B', {1: "A", 2: "1A"})
|
||||||
check('class', 'A : private B', {1: "A", 2: "1A"})
|
check('class', '{key}A : private B', {1: "A", 2: "1A"})
|
||||||
check('class', 'A : public B', {1: "A", 2: "1A"})
|
check('class', '{key}A : public B', {1: "A", 2: "1A"})
|
||||||
check('class', 'A : B, C', {1: "A", 2: "1A"})
|
check('class', '{key}A : B, C', {1: "A", 2: "1A"})
|
||||||
check('class', 'A : B, protected C, D', {1: "A", 2: "1A"})
|
check('class', '{key}A : B, protected C, D', {1: "A", 2: "1A"})
|
||||||
check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='A : private virtual B')
|
check('class', 'A : virtual private B', {1: 'A', 2: '1A'}, output='{key}A : private virtual B')
|
||||||
check('class', 'A : private virtual B', {1: 'A', 2: '1A'})
|
check('class', '{key}A : private virtual B', {1: 'A', 2: '1A'})
|
||||||
check('class', 'A : B, virtual C', {1: 'A', 2: '1A'})
|
check('class', '{key}A : B, virtual C', {1: 'A', 2: '1A'})
|
||||||
check('class', 'A : public virtual B', {1: 'A', 2: '1A'})
|
check('class', '{key}A : public virtual B', {1: 'A', 2: '1A'})
|
||||||
check('class', 'A : B, C...', {1: 'A', 2: '1A'})
|
check('class', '{key}A : B, C...', {1: 'A', 2: '1A'})
|
||||||
check('class', 'A : B..., C', {1: 'A', 2: '1A'})
|
check('class', '{key}A : B..., C', {1: 'A', 2: '1A'})
|
||||||
|
|
||||||
# from #4094
|
# from #4094
|
||||||
check('class', 'template<class, class = std::void_t<>> has_var', {2: 'I00E7has_var'})
|
check('class', 'template<class, class = std::void_t<>> {key}has_var', {2: 'I00E7has_var'})
|
||||||
check('class', 'template<class T> has_var<T, std::void_t<decltype(&T::var)>>',
|
check('class', 'template<class T> {key}has_var<T, std::void_t<decltype(&T::var)>>',
|
||||||
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
|
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
|
||||||
|
|
||||||
|
|
||||||
check('class', 'template<typename ...Ts> T<int (*)(Ts)...>',
|
check('class', 'template<typename ...Ts> {key}T<int (*)(Ts)...>',
|
||||||
{2: 'IDpE1TIJPFi2TsEEE'})
|
{2: 'IDpE1TIJPFi2TsEEE'})
|
||||||
check('class', 'template<int... Is> T<(Is)...>',
|
check('class', 'template<int... Is> {key}T<(Is)...>',
|
||||||
{2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'})
|
{2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'})
|
||||||
|
|
||||||
|
|
||||||
def test_union_definitions():
|
def test_union_definitions():
|
||||||
check('union', 'A', {2: "1A"})
|
check('union', '{key}A', {2: "1A"})
|
||||||
|
|
||||||
|
|
||||||
def test_enum_definitions():
|
def test_enum_definitions():
|
||||||
check('enum', 'A', {2: "1A"})
|
check('enum', '{key}A', {2: "1A"})
|
||||||
check('enum', 'A : std::underlying_type<B>::type', {2: "1A"})
|
check('enum', '{key}A : std::underlying_type<B>::type', {2: "1A"})
|
||||||
check('enum', 'A : unsigned int', {2: "1A"})
|
check('enum', '{key}A : unsigned int', {2: "1A"})
|
||||||
check('enum', 'public A', {2: "1A"}, output='A')
|
check('enum', 'public A', {2: "1A"}, output='{key}A')
|
||||||
check('enum', 'private A', {2: "1A"})
|
check('enum', 'private {key}A', {2: "1A"})
|
||||||
|
|
||||||
check('enumerator', 'A', {2: "1A"})
|
check('enumerator', '{key}A', {2: "1A"})
|
||||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||||
|
|
||||||
|
|
||||||
def test_anon_definitions():
|
def test_anon_definitions():
|
||||||
check('class', '@a', {3: "Ut1_a"})
|
check('class', '@a', {3: "Ut1_a"}, asTextOutput='class [anonymous]')
|
||||||
check('union', '@a', {3: "Ut1_a"})
|
check('union', '@a', {3: "Ut1_a"}, asTextOutput='union [anonymous]')
|
||||||
check('enum', '@a', {3: "Ut1_a"})
|
check('enum', '@a', {3: "Ut1_a"}, asTextOutput='enum [anonymous]')
|
||||||
check('class', '@1', {3: "Ut1_1"})
|
check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]')
|
||||||
check('class', '@a::A', {3: "NUt1_a1AE"})
|
check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A')
|
||||||
|
|
||||||
|
|
||||||
def test_templates():
|
def test_templates():
|
||||||
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> A<T>")
|
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>")
|
||||||
# first just check which objects support templating
|
# first just check which objects support templating
|
||||||
check('class', "template<> A", {2: "IE1A"})
|
check('class', "template<> {key}A", {2: "IE1A"})
|
||||||
check('function', "template<> void A()", {2: "IE1Av", 4: "IE1Avv"})
|
check('function', "template<> void A()", {2: "IE1Av", 4: "IE1Avv"})
|
||||||
check('member', "template<> A a", {2: "IE1a"})
|
check('member', "template<> A a", {2: "IE1a"})
|
||||||
check('type', "template<> a = A", {2: "IE1a"})
|
check('type', "template<> {key}a = A", {2: "IE1a"}, key='using')
|
||||||
with pytest.raises(DefinitionError):
|
with pytest.raises(DefinitionError):
|
||||||
parse('enum', "template<> A")
|
parse('enum', "template<> A")
|
||||||
with pytest.raises(DefinitionError):
|
with pytest.raises(DefinitionError):
|
||||||
parse('enumerator', "template<> A")
|
parse('enumerator', "template<> A")
|
||||||
# then all the real tests
|
# then all the real tests
|
||||||
check('class', "template<typename T1, typename T2> A", {2: "I00E1A"})
|
check('class', "template<typename T1, typename T2> {key}A", {2: "I00E1A"})
|
||||||
check('type', "template<> a", {2: "IE1a"})
|
check('type', "template<> {key}a", {2: "IE1a"}, key='using')
|
||||||
|
|
||||||
check('class', "template<typename T> A", {2: "I0E1A"})
|
check('class', "template<typename T> {key}A", {2: "I0E1A"})
|
||||||
check('class', "template<class T> A", {2: "I0E1A"})
|
check('class', "template<class T> {key}A", {2: "I0E1A"})
|
||||||
check('class', "template<typename ...T> A", {2: "IDpE1A"})
|
check('class', "template<typename ...T> {key}A", {2: "IDpE1A"})
|
||||||
check('class', "template<typename...> A", {2: "IDpE1A"})
|
check('class', "template<typename...> {key}A", {2: "IDpE1A"})
|
||||||
check('class', "template<typename = Test> A", {2: "I0E1A"})
|
check('class', "template<typename = Test> {key}A", {2: "I0E1A"})
|
||||||
check('class', "template<typename T = Test> A", {2: "I0E1A"})
|
check('class', "template<typename T = Test> {key}A", {2: "I0E1A"})
|
||||||
|
|
||||||
check('class', "template<template<typename> typename T> A", {2: "II0E0E1A"})
|
check('class', "template<template<typename> typename T> {key}A", {2: "II0E0E1A"})
|
||||||
check('class', "template<template<typename> typename> A", {2: "II0E0E1A"})
|
check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"})
|
||||||
check('class', "template<template<typename> typename ...T> A", {2: "II0EDpE1A"})
|
check('class', "template<template<typename> typename ...T> {key}A", {2: "II0EDpE1A"})
|
||||||
check('class', "template<template<typename> typename...> A", {2: "II0EDpE1A"})
|
check('class', "template<template<typename> typename...> {key}A", {2: "II0EDpE1A"})
|
||||||
|
|
||||||
check('class', "template<int> A", {2: "I_iE1A"})
|
check('class', "template<int> {key}A", {2: "I_iE1A"})
|
||||||
check('class', "template<int T> A", {2: "I_iE1A"})
|
check('class', "template<int T> {key}A", {2: "I_iE1A"})
|
||||||
check('class', "template<int... T> A", {2: "I_DpiE1A"})
|
check('class', "template<int... T> {key}A", {2: "I_DpiE1A"})
|
||||||
check('class', "template<int T = 42> A", {2: "I_iE1A"})
|
check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
|
||||||
check('class', "template<int = 42> A", {2: "I_iE1A"})
|
check('class', "template<int = 42> {key}A", {2: "I_iE1A"})
|
||||||
|
|
||||||
check('class', "template<> A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"})
|
check('class', "template<> {key}A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"})
|
||||||
|
|
||||||
# from #2058
|
# from #2058
|
||||||
check('function',
|
check('function',
|
||||||
@ -746,21 +777,21 @@ def test_templates():
|
|||||||
parse('enum', 'abc::ns::foo{id_0, id_1, id_2} A')
|
parse('enum', 'abc::ns::foo{id_0, id_1, id_2} A')
|
||||||
with pytest.raises(DefinitionError):
|
with pytest.raises(DefinitionError):
|
||||||
parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A')
|
parse('enumerator', 'abc::ns::foo{id_0, id_1, id_2} A')
|
||||||
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar',
|
check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar',
|
||||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
||||||
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar',
|
check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar',
|
||||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
||||||
check('class', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar<id_0, id_1, id_2>',
|
check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar<id_0, id_1, id_2>',
|
||||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
|
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'})
|
||||||
check('class', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar<id_0, id_1, id_2...>',
|
check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar<id_0, id_1, id_2...>',
|
||||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
|
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'})
|
||||||
|
|
||||||
check('class', 'template<> Concept{U} A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'})
|
check('class', 'template<> Concept{{U}} {key}A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'})
|
||||||
|
|
||||||
check('type', 'abc::ns::foo{id_0, id_1, id_2} xyz::bar = ghi::qux',
|
check('type', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar = ghi::qux',
|
||||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}, key='using')
|
||||||
check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux',
|
check('type', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar = ghi::qux',
|
||||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}, key='using')
|
||||||
check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()',
|
check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()',
|
||||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv',
|
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv',
|
||||||
4: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEvv'})
|
4: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEvv'})
|
||||||
@ -771,8 +802,8 @@ def test_templates():
|
|||||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
||||||
check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar',
|
check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar',
|
||||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
||||||
check('concept', 'Iterator{T, U} Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
|
check('concept', 'Iterator{{T, U}} {key}Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
|
||||||
check('concept', 'template<typename ...Pack> Numerics = (... && Numeric<Pack>)',
|
check('concept', 'template<typename ...Pack> {key}Numerics = (... && Numeric<Pack>)',
|
||||||
{2: 'IDpE8Numerics'})
|
{2: 'IDpE8Numerics'})
|
||||||
|
|
||||||
# explicit specializations of members
|
# explicit specializations of members
|
||||||
@ -784,7 +815,7 @@ def test_templates():
|
|||||||
output='template<> template<> int A<int>::B<int>::b') # same as above
|
output='template<> template<> int A<int>::B<int>::b') # same as above
|
||||||
|
|
||||||
# defaulted constrained type parameters
|
# defaulted constrained type parameters
|
||||||
check('type', 'template<C T = int&> A', {2: 'I_1CE1A'})
|
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
|
||||||
|
|
||||||
|
|
||||||
def test_template_args():
|
def test_template_args():
|
||||||
@ -796,9 +827,10 @@ def test_template_args():
|
|||||||
3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE",
|
3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE",
|
||||||
4: "I0E5allowvP1FN4funcI1F1BXne1GL1EEE4typeE"})
|
4: "I0E5allowvP1FN4funcI1F1BXne1GL1EEE4typeE"})
|
||||||
# from #3542
|
# from #3542
|
||||||
check('type', "template<typename T> "
|
check('type', "template<typename T> {key}"
|
||||||
"enable_if_not_array_t = std::enable_if_t<!is_array<T>::value, int>",
|
"enable_if_not_array_t = std::enable_if_t<!is_array<T>::value, int>",
|
||||||
{2: "I0E21enable_if_not_array_t"})
|
{2: "I0E21enable_if_not_array_t"},
|
||||||
|
key='using')
|
||||||
|
|
||||||
|
|
||||||
def test_initializers():
|
def test_initializers():
|
||||||
|
@ -40,22 +40,22 @@ def parse(sig):
|
|||||||
|
|
||||||
def test_function_signatures():
|
def test_function_signatures():
|
||||||
rv = parse('func(a=1) -> int object')
|
rv = parse('func(a=1) -> int object')
|
||||||
assert rv == 'a=1'
|
assert rv == '(a=1)'
|
||||||
|
|
||||||
rv = parse('func(a=1, [b=None])')
|
rv = parse('func(a=1, [b=None])')
|
||||||
assert rv == 'a=1, [b=None]'
|
assert rv == '(a=1, [b=None])'
|
||||||
|
|
||||||
rv = parse('func(a=1[, b=None])')
|
rv = parse('func(a=1[, b=None])')
|
||||||
assert rv == 'a=1, [b=None]'
|
assert rv == '(a=1, [b=None])'
|
||||||
|
|
||||||
rv = parse("compile(source : string, filename, symbol='file')")
|
rv = parse("compile(source : string, filename, symbol='file')")
|
||||||
assert rv == "source : string, filename, symbol='file'"
|
assert rv == "(source : string, filename, symbol='file')"
|
||||||
|
|
||||||
rv = parse('func(a=[], [b=None])')
|
rv = parse('func(a=[], [b=None])')
|
||||||
assert rv == 'a=[], [b=None]'
|
assert rv == '(a=[], [b=None])'
|
||||||
|
|
||||||
rv = parse('func(a=[][, b=None])')
|
rv = parse('func(a=[][, b=None])')
|
||||||
assert rv == 'a=[], [b=None]'
|
assert rv == '(a=[], [b=None])'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('dummy', testroot='domain-py')
|
@pytest.mark.sphinx('dummy', testroot='domain-py')
|
||||||
@ -422,7 +422,8 @@ def test_pydata_signature(app):
|
|||||||
doctree = restructuredtext.parse(app, text)
|
doctree = restructuredtext.parse(app, text)
|
||||||
assert_node(doctree, (addnodes.index,
|
assert_node(doctree, (addnodes.index,
|
||||||
[desc, ([desc_signature, ([desc_name, "version"],
|
[desc, ([desc_signature, ([desc_name, "version"],
|
||||||
[desc_annotation, ": int"],
|
[desc_annotation, (": ",
|
||||||
|
[pending_xref, "int"])],
|
||||||
[desc_annotation, " = 1"])],
|
[desc_annotation, " = 1"])],
|
||||||
desc_content)]))
|
desc_content)]))
|
||||||
assert_node(doctree[1], addnodes.desc, desctype="data",
|
assert_node(doctree[1], addnodes.desc, desctype="data",
|
||||||
@ -454,8 +455,8 @@ def test_pyobject_prefix(app):
|
|||||||
desc,
|
desc,
|
||||||
addnodes.index,
|
addnodes.index,
|
||||||
desc)])]))
|
desc)])]))
|
||||||
assert doctree[1][1][1].astext().strip() == 'say' # prefix is stripped
|
assert doctree[1][1][1].astext().strip() == 'say()' # prefix is stripped
|
||||||
assert doctree[1][1][3].astext().strip() == 'FooBar.say' # not stripped
|
assert doctree[1][1][3].astext().strip() == 'FooBar.say()' # not stripped
|
||||||
|
|
||||||
|
|
||||||
def test_pydata(app):
|
def test_pydata(app):
|
||||||
@ -692,7 +693,8 @@ def test_pyattribute(app):
|
|||||||
assert_node(doctree[1][1][0], addnodes.index,
|
assert_node(doctree[1][1][0], addnodes.index,
|
||||||
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
|
||||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||||
[desc_annotation, ": str"],
|
[desc_annotation, (": ",
|
||||||
|
[pending_xref, "str"])],
|
||||||
[desc_annotation, " = ''"])],
|
[desc_annotation, " = ''"])],
|
||||||
[desc_content, ()]))
|
[desc_content, ()]))
|
||||||
assert 'Class.attr' in domain.objects
|
assert 'Class.attr' in domain.objects
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@ import platform
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from test_autodoc import do_autodoc
|
from test_ext_autodoc import do_autodoc
|
||||||
|
|
||||||
IS_PYPY = platform.python_implementation() == 'PyPy'
|
IS_PYPY = platform.python_implementation() == 'PyPy'
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sphinx.ext.autodoc import between, cut_lines
|
from sphinx.ext.autodoc import between, cut_lines
|
||||||
from test_autodoc import do_autodoc
|
from test_ext_autodoc import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from test_autodoc import do_autodoc
|
from test_ext_autodoc import do_autodoc
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
@ -208,10 +208,11 @@ def test_autosummary_generate(app, status, warning):
|
|||||||
nodes.row)])])
|
nodes.row)])])
|
||||||
assert_node(doctree[4][0], addnodes.toctree, caption="An autosummary")
|
assert_node(doctree[4][0], addnodes.toctree, caption="An autosummary")
|
||||||
|
|
||||||
|
assert len(doctree[3][0][0][2]) == 4
|
||||||
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
||||||
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
||||||
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.Foo.Bar\n\n'
|
||||||
assert doctree[3][0][0][2][3].astext() == 'autosummary_importfail\n\n'
|
assert doctree[3][0][0][2][3].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
||||||
|
|
||||||
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
|
module = (app.srcdir / 'generated' / 'autosummary_dummy_module.rst').read_text()
|
||||||
assert (' .. autosummary::\n'
|
assert (' .. autosummary::\n'
|
||||||
@ -231,6 +232,11 @@ def test_autosummary_generate(app, status, warning):
|
|||||||
' ~Foo.baz\n'
|
' ~Foo.baz\n'
|
||||||
' \n' in Foo)
|
' \n' in Foo)
|
||||||
|
|
||||||
|
FooBar = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.Bar.rst').read_text()
|
||||||
|
assert ('.. currentmodule:: autosummary_dummy_module\n'
|
||||||
|
'\n'
|
||||||
|
'.. autoclass:: Foo.Bar\n' in FooBar)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
|
@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
|
||||||
confoverrides={'autosummary_generate_overwrite': False})
|
confoverrides={'autosummary_generate_overwrite': False})
|
||||||
@ -390,7 +396,7 @@ def test_autosummary_template(app):
|
|||||||
confoverrides={'autosummary_generate': []})
|
confoverrides={'autosummary_generate': []})
|
||||||
def test_empty_autosummary_generate(app, status, warning):
|
def test_empty_autosummary_generate(app, status, warning):
|
||||||
app.build()
|
app.build()
|
||||||
assert ("WARNING: autosummary: stub file not found 'autosummary_importfail'"
|
assert ("WARNING: autosummary: failed to import autosummary_importfail"
|
||||||
in warning.getvalue())
|
in warning.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@ import re
|
|||||||
import pytest
|
import pytest
|
||||||
from docutils import frontend, utils, nodes
|
from docutils import frontend, utils, nodes
|
||||||
from docutils.parsers.rst import Parser as RstParser
|
from docutils.parsers.rst import Parser as RstParser
|
||||||
from docutils.transforms.universal import SmartQuotes
|
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.builders.html.transforms import KeyboardTransform
|
from sphinx.builders.html.transforms import KeyboardTransform
|
||||||
@ -21,6 +20,8 @@ from sphinx.builders.latex import LaTeXBuilder
|
|||||||
from sphinx.builders.latex.theming import ThemeFactory
|
from sphinx.builders.latex.theming import ThemeFactory
|
||||||
from sphinx.roles import XRefRole
|
from sphinx.roles import XRefRole
|
||||||
from sphinx.testing.util import Struct, assert_node
|
from sphinx.testing.util import Struct, assert_node
|
||||||
|
from sphinx.transforms import SphinxSmartQuotes
|
||||||
|
from sphinx.util import docutils
|
||||||
from sphinx.util import texescape
|
from sphinx.util import texescape
|
||||||
from sphinx.util.docutils import sphinx_domains
|
from sphinx.util.docutils import sphinx_domains
|
||||||
from sphinx.writers.html import HTMLWriter, HTMLTranslator
|
from sphinx.writers.html import HTMLWriter, HTMLTranslator
|
||||||
@ -67,7 +68,7 @@ def parse(new_document):
|
|||||||
document = new_document()
|
document = new_document()
|
||||||
parser = RstParser()
|
parser = RstParser()
|
||||||
parser.parse(rst, document)
|
parser.parse(rst, document)
|
||||||
SmartQuotes(document, startnode=None).apply()
|
SphinxSmartQuotes(document, startnode=None).apply()
|
||||||
for msg in document.traverse(nodes.system_message):
|
for msg in document.traverse(nodes.system_message):
|
||||||
if msg['level'] == 1:
|
if msg['level'] == 1:
|
||||||
msg.replace_self([])
|
msg.replace_self([])
|
||||||
@ -349,6 +350,21 @@ def test_inline(get_verifier, type, rst, html_expected, latex_expected):
|
|||||||
verifier(rst, html_expected, latex_expected)
|
verifier(rst, html_expected, latex_expected)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
|
||||||
|
(
|
||||||
|
'verify',
|
||||||
|
r'4 backslashes \\\\',
|
||||||
|
r'<p>4 backslashes \\</p>',
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
@pytest.mark.skipif(docutils.__version_info__ < (0, 16),
|
||||||
|
reason='docutils-0.16 or above is required')
|
||||||
|
def test_inline_docutils16(get_verifier, type, rst, html_expected, latex_expected):
|
||||||
|
verifier = get_verifier(type)
|
||||||
|
verifier(rst, html_expected, latex_expected)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
|
@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
|
||||||
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
|
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
|
||||||
(
|
(
|
||||||
|
@ -18,7 +18,7 @@ from inspect import Parameter
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sphinx.util import inspect
|
from sphinx.util import inspect
|
||||||
from sphinx.util.inspect import stringify_signature
|
from sphinx.util.inspect import stringify_signature, is_builtin_class_method
|
||||||
|
|
||||||
|
|
||||||
def test_signature():
|
def test_signature():
|
||||||
@ -30,10 +30,10 @@ def test_signature():
|
|||||||
inspect.signature('')
|
inspect.signature('')
|
||||||
|
|
||||||
# builitin classes
|
# builitin classes
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(ValueError):
|
||||||
inspect.signature(int)
|
inspect.signature(int)
|
||||||
|
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(ValueError):
|
||||||
inspect.signature(str)
|
inspect.signature(str)
|
||||||
|
|
||||||
# normal function
|
# normal function
|
||||||
@ -97,7 +97,7 @@ def test_signature_methods():
|
|||||||
|
|
||||||
# wrapped bound method
|
# wrapped bound method
|
||||||
sig = inspect.signature(wrapped_bound_method)
|
sig = inspect.signature(wrapped_bound_method)
|
||||||
assert stringify_signature(sig) == '(arg1, **kwargs)'
|
assert stringify_signature(sig) == '(*args, **kwargs)'
|
||||||
|
|
||||||
|
|
||||||
def test_signature_partialmethod():
|
def test_signature_partialmethod():
|
||||||
@ -127,7 +127,7 @@ def test_signature_partialmethod():
|
|||||||
|
|
||||||
def test_signature_annotations():
|
def test_signature_annotations():
|
||||||
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
|
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, Node)
|
f11, f12, f13, f14, f15, f16, f17, f18, f19, f20, Node)
|
||||||
|
|
||||||
# Class annotations
|
# Class annotations
|
||||||
sig = inspect.signature(f0)
|
sig = inspect.signature(f0)
|
||||||
@ -184,6 +184,10 @@ def test_signature_annotations():
|
|||||||
sig = inspect.signature(f13)
|
sig = inspect.signature(f13)
|
||||||
assert stringify_signature(sig) == '() -> Optional[str]'
|
assert stringify_signature(sig) == '() -> Optional[str]'
|
||||||
|
|
||||||
|
# optional union
|
||||||
|
sig = inspect.signature(f20)
|
||||||
|
assert stringify_signature(sig) == '() -> Optional[Union[int, str]]'
|
||||||
|
|
||||||
# Any
|
# Any
|
||||||
sig = inspect.signature(f14)
|
sig = inspect.signature(f14)
|
||||||
assert stringify_signature(sig) == '() -> Any'
|
assert stringify_signature(sig) == '() -> Any'
|
||||||
@ -579,3 +583,21 @@ def test_getdoc_inherited_decorated_method():
|
|||||||
|
|
||||||
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
|
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
|
||||||
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == "docstring."
|
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == "docstring."
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_builtin_class_method():
|
||||||
|
class MyInt(int):
|
||||||
|
def my_method(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
assert inspect.is_builtin_class_method(MyInt, 'to_bytes')
|
||||||
|
assert inspect.is_builtin_class_method(MyInt, '__init__')
|
||||||
|
assert not inspect.is_builtin_class_method(MyInt, 'my_method')
|
||||||
|
assert not inspect.is_builtin_class_method(MyInt, 'does_not_exist')
|
||||||
|
assert not inspect.is_builtin_class_method(4, 'still does not crash')
|
||||||
|
|
||||||
|
class ObjectWithMroAttr:
|
||||||
|
def __init__(self, mro_attr):
|
||||||
|
self.__mro__ = mro_attr
|
||||||
|
|
||||||
|
assert not inspect.is_builtin_class_method(ObjectWithMroAttr([1, 2, 3]), 'still does not crash')
|
||||||
|
@ -96,6 +96,10 @@ def f19(*args: int, **kwargs: str):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def f20() -> Optional[Union[int, str]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
def __init__(self, parent: Optional['Node']) -> None:
|
def __init__(self, parent: Optional['Node']) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user