mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 7668_wront_retann
This commit is contained in:
commit
c5f7ded772
21
CHANGES
21
CHANGES
@ -48,6 +48,7 @@ Features added
|
|||||||
* #7466: autosummary: headings in generated documents are not translated
|
* #7466: autosummary: headings in generated documents are not translated
|
||||||
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
||||||
caption to the toctree
|
caption to the toctree
|
||||||
|
* #7469: autosummary: Support module attributes
|
||||||
* #248, #6040: autosummary: Add ``:recursive:`` option to autosummary directive
|
* #248, #6040: autosummary: Add ``:recursive:`` option to autosummary directive
|
||||||
to generate stub files recursively
|
to generate stub files recursively
|
||||||
* #4030: autosummary: Add :confval:`autosummary_context` to add template
|
* #4030: autosummary: Add :confval:`autosummary_context` to add template
|
||||||
@ -65,6 +66,7 @@ Features added
|
|||||||
* #7541: html theme: Add a "clearer" at the end of the "body"
|
* #7541: html theme: Add a "clearer" at the end of the "body"
|
||||||
* #7542: html theme: Make admonition/topic/sidebar scrollable
|
* #7542: html theme: Make admonition/topic/sidebar scrollable
|
||||||
* #7543: html theme: Add top and bottom margins to tables
|
* #7543: html theme: Add top and bottom margins to tables
|
||||||
|
* #7695: html theme: Add viewport meta tag for basic theme
|
||||||
* C and C++: allow semicolon in the end of declarations.
|
* C and C++: allow semicolon in the end of declarations.
|
||||||
* C++, parse parameterized noexcept specifiers.
|
* C++, parse parameterized noexcept specifiers.
|
||||||
* #7294: C++, parse expressions with user-defined literals.
|
* #7294: C++, parse expressions with user-defined literals.
|
||||||
@ -73,6 +75,8 @@ Features added
|
|||||||
: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
|
* #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
|
||||||
|
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
||||||
|
handlers to raise specified exceptions
|
||||||
|
|
||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
@ -90,16 +94,32 @@ Bugs fixed
|
|||||||
* #7362: autodoc: does not render correct signatures for built-in functions
|
* #7362: autodoc: does not render correct signatures for built-in functions
|
||||||
* #7654: autodoc: ``Optional[Union[foo, bar]]`` is presented as
|
* #7654: autodoc: ``Optional[Union[foo, bar]]`` is presented as
|
||||||
``Union[foo, bar, None]``
|
``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
|
||||||
|
* #7676: autodoc: wrong value for :member-order: option is ignored silently
|
||||||
|
* #7676: autodoc: member-order="bysource" does not work for C module
|
||||||
* #7668: autodoc: wrong retann value is passed to a handler of
|
* #7668: autodoc: wrong retann value is passed to a handler of
|
||||||
autodoc-proccess-signature
|
autodoc-proccess-signature
|
||||||
* #7551: autosummary: a nested class is indexed as non-nested class
|
* #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
|
||||||
|
* #7685: autosummary: The template variable "members" contains imported members
|
||||||
|
even if :confval:`autossummary_imported_members` is False
|
||||||
* #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
|
* #7628: imgconverter: runs imagemagick once unnecessary for builders not
|
||||||
supporting images
|
supporting images
|
||||||
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
* #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
|
||||||
--------
|
--------
|
||||||
@ -124,6 +144,7 @@ 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
|
* #7637: autodoc: system defined TypeVars are shown in Python 3.9
|
||||||
|
* #7696: html: Updated jQuery version from 3.4.1 to 3.5.1 for security reasons
|
||||||
* #7611: md5 fails when OpenSSL FIPS is enabled
|
* #7611: md5 fails when OpenSSL FIPS is enabled
|
||||||
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
||||||
|
|
||||||
|
@ -230,11 +230,15 @@ connect handlers to the events. Example:
|
|||||||
|
|
||||||
.. event:: missing-reference (app, env, node, contnode)
|
.. event:: missing-reference (app, env, node, contnode)
|
||||||
|
|
||||||
Emitted when a cross-reference to a Python module or object cannot be
|
Emitted when a cross-reference to an object cannot be resolved.
|
||||||
resolved. If the event handler can resolve the reference, it should return a
|
If the event handler can resolve the reference, it should return a
|
||||||
new docutils node to be inserted in the document tree in place of the node
|
new docutils node to be inserted in the document tree in place of the node
|
||||||
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
||||||
as a child.
|
as a child.
|
||||||
|
If the handler can not resolve the cross-reference,
|
||||||
|
it can either return ``None`` to let other handlers try,
|
||||||
|
or raise :class:`NoUri` to prevent other handlers in trying and suppress
|
||||||
|
a warning about this cross-reference being unresolved.
|
||||||
|
|
||||||
:param env: The build environment (``app.builder.env``).
|
:param env: The build environment (``app.builder.env``).
|
||||||
:param node: The :class:`pending_xref` node to be resolved. Its attributes
|
:param node: The :class:`pending_xref` node to be resolved. Its attributes
|
||||||
|
@ -285,8 +285,12 @@ The following variables available in the templates:
|
|||||||
|
|
||||||
.. data:: attributes
|
.. data:: attributes
|
||||||
|
|
||||||
List containing names of "public" attributes in the class. Only available
|
List containing names of "public" attributes in the class/module. Only
|
||||||
for classes.
|
available for classes and modules.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Attributes of modules are supported.
|
||||||
|
|
||||||
.. data:: modules
|
.. data:: modules
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ Doctest blocks
|
|||||||
Doctest blocks (:duref:`ref <doctest-blocks>`) are interactive Python sessions
|
Doctest blocks (:duref:`ref <doctest-blocks>`) are interactive Python sessions
|
||||||
cut-and-pasted into docstrings. They do not require the
|
cut-and-pasted into docstrings. They do not require the
|
||||||
:ref:`literal blocks <rst-literal-blocks>` syntax. The doctest block must end
|
:ref:`literal blocks <rst-literal-blocks>` syntax. The doctest block must end
|
||||||
with a blank line and should *not* end with with an unused prompt::
|
with a blank line and should *not* end with an unused prompt::
|
||||||
|
|
||||||
>>> 1 + 1
|
>>> 1 + 1
|
||||||
2
|
2
|
||||||
|
@ -112,6 +112,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.
|
||||||
@ -150,6 +157,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."""
|
||||||
|
@ -436,22 +436,32 @@ class Sphinx:
|
|||||||
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
logger.debug('[app] disconnecting event: [id=%s]', listener_id)
|
||||||
self.events.disconnect(listener_id)
|
self.events.disconnect(listener_id)
|
||||||
|
|
||||||
def emit(self, event: str, *args: Any) -> List:
|
def emit(self, event: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||||||
"""Emit *event* and pass *arguments* to the callback functions.
|
"""Emit *event* and pass *arguments* to the callback functions.
|
||||||
|
|
||||||
Return the return values of all callbacks as a list. Do not emit core
|
Return the return values of all callbacks as a list. Do not emit core
|
||||||
Sphinx events in extensions!
|
Sphinx events in extensions!
|
||||||
"""
|
|
||||||
return self.events.emit(event, *args)
|
|
||||||
|
|
||||||
def emit_firstresult(self, event: str, *args: Any) -> Any:
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Added *allowed_exceptions* to specify path-through exceptions
|
||||||
|
"""
|
||||||
|
return self.events.emit(event, *args, allowed_exceptions=allowed_exceptions)
|
||||||
|
|
||||||
|
def emit_firstresult(self, event: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||||||
"""Emit *event* and pass *arguments* to the callback functions.
|
"""Emit *event* and pass *arguments* to the callback functions.
|
||||||
|
|
||||||
Return the result of the first callback that doesn't return ``None``.
|
Return the result of the first callback that doesn't return ``None``.
|
||||||
|
|
||||||
.. versionadded:: 0.5
|
.. versionadded:: 0.5
|
||||||
|
.. versionchanged:: 3.1
|
||||||
|
|
||||||
|
Added *allowed_exceptions* to specify path-through exceptions
|
||||||
"""
|
"""
|
||||||
return self.events.emit_firstresult(event, *args)
|
return self.events.emit_firstresult(event, *args,
|
||||||
|
allowed_exceptions=allowed_exceptions)
|
||||||
|
|
||||||
# registering addon parts
|
# registering addon parts
|
||||||
|
|
||||||
|
@ -357,6 +357,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,
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,8 @@ class PycodeError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
class NoUri(Exception):
|
class NoUri(Exception):
|
||||||
"""Raised by builder.get_relative_uri() if there is no URI available."""
|
"""Raised by builder.get_relative_uri() or from missing-reference handlers
|
||||||
|
if there is no URI available."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,15 +13,16 @@
|
|||||||
import warnings
|
import warnings
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import Any, Callable, Dict, List, NamedTuple
|
from typing import Any, Callable, Dict, List, NamedTuple, Tuple
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||||
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
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
|
from typing import Type # for python3.5.1
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +89,8 @@ class EventManager:
|
|||||||
if listener.id == listener_id:
|
if listener.id == listener_id:
|
||||||
listeners.remove(listener)
|
listeners.remove(listener)
|
||||||
|
|
||||||
def emit(self, name: str, *args: Any) -> List:
|
def emit(self, name: str, *args: Any,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> List:
|
||||||
"""Emit a Sphinx event."""
|
"""Emit a Sphinx event."""
|
||||||
try:
|
try:
|
||||||
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
||||||
@ -100,19 +102,29 @@ 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:
|
||||||
if self.app is None:
|
if self.app is None:
|
||||||
# for compatibility; RemovedInSphinx40Warning
|
# for compatibility; RemovedInSphinx40Warning
|
||||||
results.append(listener.handler(*args))
|
results.append(listener.handler(*args))
|
||||||
else:
|
else:
|
||||||
results.append(listener.handler(self.app, *args))
|
results.append(listener.handler(self.app, *args))
|
||||||
|
except allowed_exceptions:
|
||||||
|
# pass through the errors specified as *allowed_exceptions*
|
||||||
|
raise
|
||||||
|
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,
|
||||||
|
allowed_exceptions: Tuple["Type[Exception]", ...] = ()) -> Any:
|
||||||
"""Emit a Sphinx event and returns first result.
|
"""Emit a Sphinx event and returns first result.
|
||||||
|
|
||||||
This returns the result of the first handler that doesn't return ``None``.
|
This returns the result of the first handler that doesn't return ``None``.
|
||||||
"""
|
"""
|
||||||
for result in self.emit(name, *args):
|
for result in self.emit(name, *args, allowed_exceptions=allowed_exceptions):
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
return None
|
return None
|
||||||
|
@ -15,14 +15,15 @@ import re
|
|||||||
import warnings
|
import warnings
|
||||||
from inspect import Parameter
|
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 (
|
||||||
from unittest.mock import patch
|
Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
||||||
|
)
|
||||||
|
|
||||||
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 RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, 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
|
||||||
@ -93,6 +94,16 @@ def inherited_members_option(arg: Any) -> Union[object, Set[str]]:
|
|||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
def member_order_option(arg: Any) -> Optional[str]:
|
||||||
|
"""Used to convert the :members: option to auto directives."""
|
||||||
|
if arg is None:
|
||||||
|
return None
|
||||||
|
elif arg in ('alphabetical', 'bysource', 'groupwise'):
|
||||||
|
return arg
|
||||||
|
else:
|
||||||
|
raise ValueError(__('invalid value for member-order option: %s') % arg)
|
||||||
|
|
||||||
|
|
||||||
SUPPRESS = object()
|
SUPPRESS = object()
|
||||||
|
|
||||||
|
|
||||||
@ -427,8 +438,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:
|
||||||
@ -521,12 +539,12 @@ class Documenter:
|
|||||||
else:
|
else:
|
||||||
logger.warning(__('missing attribute %s in object %s') %
|
logger.warning(__('missing attribute %s in object %s') %
|
||||||
(name, self.fullname), type='autodoc')
|
(name, self.fullname), type='autodoc')
|
||||||
return False, sorted(selected)
|
return False, selected
|
||||||
elif self.options.inherited_members:
|
elif self.options.inherited_members:
|
||||||
return False, sorted((m.name, m.value) for m in members.values())
|
return False, [(m.name, m.value) for m in members.values()]
|
||||||
else:
|
else:
|
||||||
return False, sorted((m.name, m.value) for m in members.values()
|
return False, [(m.name, m.value) for m in members.values()
|
||||||
if m.directly_defined)
|
if m.directly_defined]
|
||||||
|
|
||||||
def filter_members(self, members: List[Tuple[str, Any]], want_all: bool
|
def filter_members(self, members: List[Tuple[str, Any]], want_all: bool
|
||||||
) -> List[Tuple[str, Any, bool]]:
|
) -> List[Tuple[str, Any, bool]]:
|
||||||
@ -699,10 +717,10 @@ class Documenter:
|
|||||||
member_order = self.options.member_order or \
|
member_order = self.options.member_order or \
|
||||||
self.env.config.autodoc_member_order
|
self.env.config.autodoc_member_order
|
||||||
if member_order == 'groupwise':
|
if member_order == 'groupwise':
|
||||||
# sort by group; relies on stable sort to keep items in the
|
# sort by group; alphabetically within groups
|
||||||
# same group sorted alphabetically
|
memberdocumenters.sort(key=lambda e: (e[0].member_order, e[0].name))
|
||||||
memberdocumenters.sort(key=lambda e: e[0].member_order)
|
elif member_order == 'bysource':
|
||||||
elif member_order == 'bysource' and self.analyzer:
|
if self.analyzer:
|
||||||
# sort by source order, by virtue of the module analyzer
|
# sort by source order, by virtue of the module analyzer
|
||||||
tagorder = self.analyzer.tagorder
|
tagorder = self.analyzer.tagorder
|
||||||
|
|
||||||
@ -710,6 +728,15 @@ class Documenter:
|
|||||||
fullname = entry[0].name.split('::')[1]
|
fullname = entry[0].name.split('::')[1]
|
||||||
return tagorder.get(fullname, len(tagorder))
|
return tagorder.get(fullname, len(tagorder))
|
||||||
memberdocumenters.sort(key=keyfunc)
|
memberdocumenters.sort(key=keyfunc)
|
||||||
|
else:
|
||||||
|
# Assume that member discovery order matches source order.
|
||||||
|
# This is a reasonable assumption in Python 3.6 and up, where
|
||||||
|
# module.__dict__ is insertion-ordered.
|
||||||
|
pass
|
||||||
|
elif member_order == 'alphabetical':
|
||||||
|
memberdocumenters.sort(key=lambda e: e[0].name)
|
||||||
|
else:
|
||||||
|
raise ValueError("Illegal member order {}".format(member_order))
|
||||||
|
|
||||||
for documenter, isattr in memberdocumenters:
|
for documenter, isattr in memberdocumenters:
|
||||||
documenter.generate(
|
documenter.generate(
|
||||||
@ -817,7 +844,7 @@ class ModuleDocumenter(Documenter):
|
|||||||
'noindex': bool_option, 'inherited-members': inherited_members_option,
|
'noindex': bool_option, 'inherited-members': inherited_members_option,
|
||||||
'show-inheritance': bool_option, 'synopsis': identity,
|
'show-inheritance': bool_option, 'synopsis': identity,
|
||||||
'platform': identity, 'deprecated': bool_option,
|
'platform': identity, 'deprecated': bool_option,
|
||||||
'member-order': identity, 'exclude-members': members_set_option,
|
'member-order': member_order_option, 'exclude-members': members_set_option,
|
||||||
'private-members': bool_option, 'special-members': members_option,
|
'private-members': bool_option, 'special-members': members_option,
|
||||||
'imported-members': bool_option, 'ignore-module-all': bool_option
|
'imported-members': bool_option, 'ignore-module-all': bool_option
|
||||||
} # type: Dict[str, Callable]
|
} # type: Dict[str, Callable]
|
||||||
@ -948,7 +975,7 @@ class ClassLevelDocumenter(Documenter):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
modname, qualname = split_full_qualified_name(mod_cls)
|
modname, qualname = split_full_qualified_name(mod_cls)
|
||||||
parents = qualname.split(".")
|
parents = qualname.split(".") if qualname else []
|
||||||
except ImportError:
|
except ImportError:
|
||||||
parents = mod_cls.split(".")
|
parents = mod_cls.split(".")
|
||||||
|
|
||||||
@ -1057,12 +1084,18 @@ 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)
|
|
||||||
try:
|
try:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
sig = inspect.signature(unwrapped)
|
if inspect.is_singledispatch_function(self.object):
|
||||||
|
sig = inspect.signature(self.object, follow_wrapped=True)
|
||||||
|
else:
|
||||||
|
sig = inspect.signature(self.object)
|
||||||
args = stringify_signature(sig, **kwargs)
|
args = stringify_signature(sig, **kwargs)
|
||||||
except TypeError:
|
except TypeError as exc:
|
||||||
|
logger.warning(__("Failed to get a function signature for %s: %s"),
|
||||||
|
self.fullname, exc)
|
||||||
|
return None
|
||||||
|
except ValueError:
|
||||||
args = ''
|
args = ''
|
||||||
|
|
||||||
if self.env.config.strip_signature_backslash:
|
if self.env.config.strip_signature_backslash:
|
||||||
@ -1075,26 +1108,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.
|
||||||
@ -1103,13 +1127,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."""
|
||||||
@ -1157,7 +1177,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
option_spec = {
|
option_spec = {
|
||||||
'members': members_option, 'undoc-members': bool_option,
|
'members': members_option, 'undoc-members': bool_option,
|
||||||
'noindex': bool_option, 'inherited-members': inherited_members_option,
|
'noindex': bool_option, 'inherited-members': inherited_members_option,
|
||||||
'show-inheritance': bool_option, 'member-order': identity,
|
'show-inheritance': bool_option, 'member-order': member_order_option,
|
||||||
'exclude-members': members_set_option,
|
'exclude-members': members_set_option,
|
||||||
'private-members': bool_option, 'special-members': members_option,
|
'private-members': bool_option, 'special-members': members_option,
|
||||||
} # type: Dict[str, Callable]
|
} # type: Dict[str, Callable]
|
||||||
@ -1453,7 +1473,6 @@ 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:
|
try:
|
||||||
if self.object == object.__init__ and self.parent != object:
|
if self.object == object.__init__ and self.parent != object:
|
||||||
# Classes not having own __init__() method are shown as no arguments.
|
# Classes not having own __init__() method are shown as no arguments.
|
||||||
@ -1462,13 +1481,23 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
# But it makes users confused.
|
# But it makes users confused.
|
||||||
args = '()'
|
args = '()'
|
||||||
else:
|
else:
|
||||||
if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
|
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
sig = inspect.signature(unwrapped, bound_method=False)
|
sig = inspect.signature(self.object, bound_method=False)
|
||||||
else:
|
else:
|
||||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
|
self.env.app.emit('autodoc-before-process-signature', self.object, True)
|
||||||
sig = inspect.signature(unwrapped, bound_method=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:
|
except ValueError:
|
||||||
args = ''
|
args = ''
|
||||||
|
|
||||||
@ -1478,10 +1507,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()
|
||||||
@ -1500,19 +1525,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.
|
||||||
@ -1520,14 +1539,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."""
|
||||||
@ -1764,6 +1781,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)
|
||||||
@ -1779,7 +1804,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)
|
||||||
@ -1792,6 +1818,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, priority=800)
|
||||||
|
|
||||||
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')
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ from docutils import nodes
|
|||||||
from docutils.nodes import Node
|
from docutils.nodes import Node
|
||||||
|
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.domains.std import StandardDomain
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.nodes import clean_astext
|
from sphinx.util.nodes import clean_astext
|
||||||
@ -33,8 +34,7 @@ def get_node_depth(node: Node) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def register_sections_as_label(app: Sphinx, document: Node) -> None:
|
def register_sections_as_label(app: Sphinx, document: Node) -> None:
|
||||||
labels = app.env.domaindata['std']['labels']
|
domain = cast(StandardDomain, app.env.get_domain('std'))
|
||||||
anonlabels = app.env.domaindata['std']['anonlabels']
|
|
||||||
for node in document.traverse(nodes.section):
|
for node in document.traverse(nodes.section):
|
||||||
if (app.config.autosectionlabel_maxdepth and
|
if (app.config.autosectionlabel_maxdepth and
|
||||||
get_node_depth(node) >= app.config.autosectionlabel_maxdepth):
|
get_node_depth(node) >= app.config.autosectionlabel_maxdepth):
|
||||||
@ -49,13 +49,13 @@ def register_sections_as_label(app: Sphinx, document: Node) -> None:
|
|||||||
name = nodes.fully_normalize_name(ref_name)
|
name = nodes.fully_normalize_name(ref_name)
|
||||||
sectname = clean_astext(title)
|
sectname = clean_astext(title)
|
||||||
|
|
||||||
if name in labels:
|
if name in domain.labels:
|
||||||
logger.warning(__('duplicate label %s, other instance in %s'),
|
logger.warning(__('duplicate label %s, other instance in %s'),
|
||||||
name, app.env.doc2path(labels[name][0]),
|
name, app.env.doc2path(domain.labels[name][0]),
|
||||||
location=node, type='autosectionlabel', subtype=docname)
|
location=node, type='autosectionlabel', subtype=docname)
|
||||||
|
|
||||||
anonlabels[name] = docname, labelid
|
domain.anonlabels[name] = docname, labelid
|
||||||
labels[name] = docname, labelid, sectname
|
domain.labels[name] = docname, labelid, sectname
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
|
@ -302,8 +302,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
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import inspect
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
import pkgutil
|
import pkgutil
|
||||||
@ -42,6 +43,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warnin
|
|||||||
from sphinx.ext.autodoc import Documenter
|
from sphinx.ext.autodoc import Documenter
|
||||||
from sphinx.ext.autosummary import import_by_name, get_documenter
|
from sphinx.ext.autosummary import import_by_name, get_documenter
|
||||||
from sphinx.locale import __
|
from sphinx.locale import __
|
||||||
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
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
|
||||||
@ -175,6 +177,56 @@ class AutosummaryRenderer:
|
|||||||
# -- Generating output ---------------------------------------------------------
|
# -- Generating output ---------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
class ModuleScanner:
|
||||||
|
def __init__(self, app: Any, obj: Any) -> None:
|
||||||
|
self.app = app
|
||||||
|
self.object = obj
|
||||||
|
|
||||||
|
def get_object_type(self, name: str, value: Any) -> str:
|
||||||
|
return get_documenter(self.app, value, self.object).objtype
|
||||||
|
|
||||||
|
def is_skipped(self, name: str, value: Any, objtype: str) -> bool:
|
||||||
|
try:
|
||||||
|
return self.app.emit_firstresult('autodoc-skip-member', objtype,
|
||||||
|
name, value, False, {})
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning(__('autosummary: failed to determine %r to be documented, '
|
||||||
|
'the following exception was raised:\n%s'),
|
||||||
|
name, exc, type='autosummary')
|
||||||
|
return False
|
||||||
|
|
||||||
|
def scan(self, imported_members: bool) -> List[str]:
|
||||||
|
members = []
|
||||||
|
for name in dir(self.object):
|
||||||
|
try:
|
||||||
|
value = safe_getattr(self.object, name)
|
||||||
|
except AttributeError:
|
||||||
|
value = None
|
||||||
|
|
||||||
|
objtype = self.get_object_type(name, value)
|
||||||
|
if self.is_skipped(name, value, objtype):
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
if inspect.ismodule(value):
|
||||||
|
imported = True
|
||||||
|
elif safe_getattr(value, '__module__') != self.object.__name__:
|
||||||
|
imported = True
|
||||||
|
else:
|
||||||
|
imported = False
|
||||||
|
except AttributeError:
|
||||||
|
imported = False
|
||||||
|
|
||||||
|
if imported_members:
|
||||||
|
# list all members up
|
||||||
|
members.append(name)
|
||||||
|
elif imported is False:
|
||||||
|
# list not-imported members up
|
||||||
|
members.append(name)
|
||||||
|
|
||||||
|
return members
|
||||||
|
|
||||||
|
|
||||||
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||||
template: AutosummaryRenderer, template_name: str,
|
template: AutosummaryRenderer, template_name: str,
|
||||||
imported_members: bool, app: Any,
|
imported_members: bool, app: Any,
|
||||||
@ -218,6 +270,21 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
public.append(name)
|
public.append(name)
|
||||||
return public, items
|
return public, items
|
||||||
|
|
||||||
|
def get_module_attrs(members: Any) -> Tuple[List[str], List[str]]:
|
||||||
|
"""Find module attributes with docstrings."""
|
||||||
|
attrs, public = [], []
|
||||||
|
try:
|
||||||
|
analyzer = ModuleAnalyzer.for_module(name)
|
||||||
|
attr_docs = analyzer.find_attr_docs()
|
||||||
|
for namespace, attr_name in attr_docs:
|
||||||
|
if namespace == '' and attr_name in members:
|
||||||
|
attrs.append(attr_name)
|
||||||
|
if not attr_name.startswith('_'):
|
||||||
|
public.append(attr_name)
|
||||||
|
except PycodeError:
|
||||||
|
pass # give up if ModuleAnalyzer fails to parse code
|
||||||
|
return public, attrs
|
||||||
|
|
||||||
def get_modules(obj: Any) -> Tuple[List[str], List[str]]:
|
def get_modules(obj: Any) -> Tuple[List[str], List[str]]:
|
||||||
items = [] # type: List[str]
|
items = [] # type: List[str]
|
||||||
for _, modname, ispkg in pkgutil.iter_modules(obj.__path__):
|
for _, modname, ispkg in pkgutil.iter_modules(obj.__path__):
|
||||||
@ -230,13 +297,16 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
|||||||
ns.update(context)
|
ns.update(context)
|
||||||
|
|
||||||
if doc.objtype == 'module':
|
if doc.objtype == 'module':
|
||||||
ns['members'] = dir(obj)
|
scanner = ModuleScanner(app, obj)
|
||||||
|
ns['members'] = scanner.scan(imported_members)
|
||||||
ns['functions'], ns['all_functions'] = \
|
ns['functions'], ns['all_functions'] = \
|
||||||
get_members(obj, {'function'}, imported=imported_members)
|
get_members(obj, {'function'}, imported=imported_members)
|
||||||
ns['classes'], ns['all_classes'] = \
|
ns['classes'], ns['all_classes'] = \
|
||||||
get_members(obj, {'class'}, imported=imported_members)
|
get_members(obj, {'class'}, imported=imported_members)
|
||||||
ns['exceptions'], ns['all_exceptions'] = \
|
ns['exceptions'], ns['all_exceptions'] = \
|
||||||
get_members(obj, {'exception'}, imported=imported_members)
|
get_members(obj, {'exception'}, imported=imported_members)
|
||||||
|
ns['attributes'], ns['all_attributes'] = \
|
||||||
|
get_module_attrs(ns['members'])
|
||||||
ispackage = hasattr(obj, '__path__')
|
ispackage = hasattr(obj, '__path__')
|
||||||
if ispackage and recursive:
|
if ispackage and recursive:
|
||||||
ns['modules'], ns['all_modules'] = get_modules(obj)
|
ns['modules'], ns['all_modules'] = get_modules(obj)
|
||||||
|
@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
.. automodule:: {{ fullname }}
|
.. automodule:: {{ fullname }}
|
||||||
|
|
||||||
|
{% block attributes %}
|
||||||
|
{% if attributes %}
|
||||||
|
.. rubric:: Module Attributes
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
{% for item in attributes %}
|
||||||
|
{{ item }}
|
||||||
|
{%- endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block functions %}
|
{% block functions %}
|
||||||
{% if functions %}
|
{% if functions %}
|
||||||
.. rubric:: {{ _('Functions') }}
|
.. rubric:: {{ _('Functions') }}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ if "%1" == "help" (
|
|||||||
echo. pickle to make pickle files
|
echo. pickle to make pickle files
|
||||||
echo. json to make JSON files
|
echo. json to make JSON files
|
||||||
echo. htmlhelp to make HTML files and an HTML help project
|
echo. htmlhelp to make HTML files and an HTML help project
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
echo. qthelp to make HTML files and a Qt help project
|
||||||
echo. devhelp to make HTML files and a Devhelp project
|
echo. devhelp to make HTML files and a Devhelp project
|
||||||
echo. epub to make an epub
|
echo. epub to make an EPUB
|
||||||
echo. latex to make LaTeX files (you can set PAPER=a4 or PAPER=letter)
|
echo. latex to make LaTeX files (you can set PAPER=a4 or PAPER=letter)
|
||||||
echo. latexpdf to make LaTeX files and then PDFs out of them
|
echo. latexpdf to make LaTeX files and then PDFs out of them
|
||||||
echo. text to make text files
|
echo. text to make text files
|
||||||
@ -69,7 +69,7 @@ if errorlevel 9009 (
|
|||||||
echo.may add the Sphinx directory to PATH.
|
echo.may add the Sphinx directory to PATH.
|
||||||
echo.
|
echo.
|
||||||
echo.If you don't have Sphinx installed, grab it from
|
echo.If you don't have Sphinx installed, grab it from
|
||||||
echo.http://sphinx-doc.org/
|
echo.https://sphinx-doc.org/
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -120,6 +120,7 @@
|
|||||||
{%- else %}
|
{%- else %}
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
|
<meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{{- metatags }}
|
{{- metatags }}
|
||||||
{%- block htmltitle %}
|
{%- block htmltitle %}
|
||||||
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
<title>{{ title|striptags|e }}{{ titlesuffix }}</title>
|
||||||
|
File diff suppressed because it is too large
Load Diff
4
sphinx/themes/basic/static/jquery.js
vendored
4
sphinx/themes/basic/static/jquery.js
vendored
File diff suppressed because one or more lines are too long
@ -92,7 +92,8 @@ class ReferencesResolver(SphinxPostTransform):
|
|||||||
# no new node found? try the missing-reference event
|
# no new node found? try the missing-reference event
|
||||||
if newnode is None:
|
if newnode is None:
|
||||||
newnode = self.app.emit_firstresult('missing-reference', self.env,
|
newnode = self.app.emit_firstresult('missing-reference', self.env,
|
||||||
node, contnode)
|
node, contnode,
|
||||||
|
allowed_exceptions=(NoUri,))
|
||||||
# still not found? warn if node wishes to be warned about or
|
# still not found? warn if node wishes to be warned about or
|
||||||
# we are in nit-picky mode
|
# we are in nit-picky mode
|
||||||
if newnode is None:
|
if newnode is None:
|
||||||
|
@ -408,12 +408,19 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
|||||||
return getattr(builtins, name, None) is cls
|
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*).
|
||||||
"""
|
"""
|
||||||
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
|
||||||
|
@ -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
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from os import * # NOQA
|
from os import path # NOQA
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
@ -17,5 +17,25 @@ class Foo:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def bar(x: Union[int, str], y: int = 1):
|
class _Baz:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def bar(x: Union[int, str], y: int = 1) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _quux():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Exc(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _Exc(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#: a module-level attribute
|
||||||
|
qux = 2
|
||||||
|
@ -11,4 +11,5 @@
|
|||||||
autosummary_dummy_module.Foo
|
autosummary_dummy_module.Foo
|
||||||
autosummary_dummy_module.Foo.Bar
|
autosummary_dummy_module.Foo.Bar
|
||||||
autosummary_dummy_module.bar
|
autosummary_dummy_module.bar
|
||||||
|
autosummary_dummy_module.qux
|
||||||
autosummary_importfail
|
autosummary_importfail
|
||||||
|
@ -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():
|
||||||
|
@ -33,15 +33,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")
|
||||||
@ -49,6 +63,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):
|
||||||
@ -81,13 +102,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():
|
||||||
@ -113,10 +135,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"]
|
||||||
@ -229,8 +252,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')
|
||||||
@ -291,7 +314,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')
|
||||||
|
|
||||||
@ -301,8 +324,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')
|
||||||
@ -312,58 +335,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():
|
||||||
@ -639,95 +663,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',
|
||||||
@ -747,21 +778,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'})
|
||||||
@ -772,8 +803,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
|
||||||
@ -785,7 +816,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():
|
||||||
@ -797,9 +828,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')
|
||||||
@ -453,8 +453,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):
|
||||||
|
@ -8,6 +8,9 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from sphinx.errors import ExtensionError
|
||||||
from sphinx.events import EventManager
|
from sphinx.events import EventManager
|
||||||
|
|
||||||
|
|
||||||
@ -22,3 +25,19 @@ def test_event_priority():
|
|||||||
|
|
||||||
events.emit('builder-inited')
|
events.emit('builder-inited')
|
||||||
assert result == [3, 1, 2, 5, 4]
|
assert result == [3, 1, 2, 5, 4]
|
||||||
|
|
||||||
|
|
||||||
|
def test_event_allowed_exceptions():
|
||||||
|
def raise_error(app):
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
events = EventManager(object()) # pass an dummy object as an app
|
||||||
|
events.connect('builder-inited', raise_error, priority=500)
|
||||||
|
|
||||||
|
# all errors are conveted to ExtensionError
|
||||||
|
with pytest.raises(ExtensionError):
|
||||||
|
events.emit('builder-inited')
|
||||||
|
|
||||||
|
# Allow RuntimeError (pass-through)
|
||||||
|
with pytest.raises(RuntimeError):
|
||||||
|
events.emit('builder-inited', allowed_exceptions=(RuntimeError,))
|
||||||
|
@ -59,7 +59,7 @@ def make_directive_bridge(env):
|
|||||||
platform = '',
|
platform = '',
|
||||||
deprecated = False,
|
deprecated = False,
|
||||||
members = [],
|
members = [],
|
||||||
member_order = 'alphabetic',
|
member_order = 'alphabetical',
|
||||||
exclude_members = set(),
|
exclude_members = set(),
|
||||||
ignore_module_all = False,
|
ignore_module_all = False,
|
||||||
)
|
)
|
||||||
@ -142,6 +142,7 @@ def test_format_signature(app):
|
|||||||
inst = app.registry.documenters[objtype](directive, name)
|
inst = app.registry.documenters[objtype](directive, name)
|
||||||
inst.fullname = name
|
inst.fullname = name
|
||||||
inst.doc_as_attr = False # for class objtype
|
inst.doc_as_attr = False # for class objtype
|
||||||
|
inst.parent = object # dummy
|
||||||
inst.object = obj
|
inst.object = obj
|
||||||
inst.objpath = [name]
|
inst.objpath = [name]
|
||||||
inst.args = args
|
inst.args = args
|
||||||
@ -271,6 +272,7 @@ def test_get_doc(app):
|
|||||||
|
|
||||||
def getdocl(objtype, obj):
|
def getdocl(objtype, obj):
|
||||||
inst = app.registry.documenters[objtype](directive, 'tmp')
|
inst = app.registry.documenters[objtype](directive, 'tmp')
|
||||||
|
inst.parent = object # dummy
|
||||||
inst.object = obj
|
inst.object = obj
|
||||||
inst.objpath = [obj.__name__]
|
inst.objpath = [obj.__name__]
|
||||||
inst.doc_as_attr = False
|
inst.doc_as_attr = False
|
||||||
@ -1263,6 +1265,17 @@ def test_autofunction_for_methoddescriptor(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autofunction_for_decorated(app):
|
||||||
|
actual = do_autodoc(app, 'function', 'target.decorator.foo')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:function:: foo()',
|
||||||
|
' :module: target.decorator',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_automethod_for_builtin(app):
|
def test_automethod_for_builtin(app):
|
||||||
actual = do_autodoc(app, 'method', 'builtins.int.__add__')
|
actual = do_autodoc(app, 'method', 'builtins.int.__add__')
|
||||||
@ -1276,6 +1289,17 @@ def test_automethod_for_builtin(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_automethod_for_decorated(app):
|
||||||
|
actual = do_autodoc(app, 'method', 'target.decorator.Bar.meth')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:method:: Bar.meth()',
|
||||||
|
' :module: target.decorator',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_abstractmethods(app):
|
def test_abstractmethods(app):
|
||||||
options = {"members": None,
|
options = {"members": None,
|
||||||
@ -1435,7 +1459,7 @@ def test_coroutine(app):
|
|||||||
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:function:: sync_func()',
|
'.. py:function:: sync_func(*args, **kwargs)',
|
||||||
' :module: target.coroutine',
|
' :module: target.coroutine',
|
||||||
'',
|
'',
|
||||||
]
|
]
|
||||||
@ -1795,7 +1819,7 @@ def test_autodoc(app, status, warning):
|
|||||||
|
|
||||||
content = app.env.get_doctree('index')
|
content = app.env.get_doctree('index')
|
||||||
assert isinstance(content[3], addnodes.desc)
|
assert isinstance(content[3], addnodes.desc)
|
||||||
assert content[3][0].astext() == 'autodoc_dummy_module.test'
|
assert content[3][0].astext() == 'autodoc_dummy_module.test()'
|
||||||
assert content[3][1].astext() == 'Dummy function using dummy.*'
|
assert content[3][1].astext() == 'Dummy function using dummy.*'
|
||||||
|
|
||||||
# issue sphinx-doc/sphinx#2437
|
# issue sphinx-doc/sphinx#2437
|
||||||
|
@ -19,7 +19,10 @@ from sphinx import addnodes
|
|||||||
from sphinx.ext.autosummary import (
|
from sphinx.ext.autosummary import (
|
||||||
autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary
|
autosummary_table, autosummary_toc, mangle_signature, import_by_name, extract_summary
|
||||||
)
|
)
|
||||||
from sphinx.ext.autosummary.generate import AutosummaryEntry, generate_autosummary_docs, main as autogen_main
|
from sphinx.ext.autosummary.generate import (
|
||||||
|
AutosummaryEntry, generate_autosummary_content, generate_autosummary_docs,
|
||||||
|
main as autogen_main
|
||||||
|
)
|
||||||
from sphinx.testing.util import assert_node, etree_parse
|
from sphinx.testing.util import assert_node, etree_parse
|
||||||
from sphinx.util.docutils import new_document
|
from sphinx.util.docutils import new_document
|
||||||
from sphinx.util.osutil import cd
|
from sphinx.util.osutil import cd
|
||||||
@ -189,6 +192,83 @@ def test_escaping(app, status, warning):
|
|||||||
assert str_content(title) == 'underscore_module_'
|
assert str_content(title) == 'underscore_module_'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||||
|
def test_autosummary_generate_content_for_module(app):
|
||||||
|
import autosummary_dummy_module
|
||||||
|
template = Mock()
|
||||||
|
|
||||||
|
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||||
|
template, None, False, app, False, {})
|
||||||
|
assert template.render.call_args[0][0] == 'module'
|
||||||
|
|
||||||
|
context = template.render.call_args[0][1]
|
||||||
|
assert context['members'] == ['Exc', 'Foo', '_Baz', '_Exc', '__builtins__',
|
||||||
|
'__cached__', '__doc__', '__file__', '__name__',
|
||||||
|
'__package__', '_quux', 'bar', 'qux']
|
||||||
|
assert context['functions'] == ['bar']
|
||||||
|
assert context['all_functions'] == ['_quux', 'bar']
|
||||||
|
assert context['classes'] == ['Foo']
|
||||||
|
assert context['all_classes'] == ['Foo', '_Baz']
|
||||||
|
assert context['exceptions'] == ['Exc']
|
||||||
|
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||||
|
assert context['attributes'] == ['qux']
|
||||||
|
assert context['all_attributes'] == ['qux']
|
||||||
|
assert context['fullname'] == 'autosummary_dummy_module'
|
||||||
|
assert context['module'] == 'autosummary_dummy_module'
|
||||||
|
assert context['objname'] == ''
|
||||||
|
assert context['name'] == ''
|
||||||
|
assert context['objtype'] == 'module'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||||
|
def test_autosummary_generate_content_for_module_skipped(app):
|
||||||
|
import autosummary_dummy_module
|
||||||
|
template = Mock()
|
||||||
|
|
||||||
|
def skip_member(app, what, name, obj, skip, options):
|
||||||
|
if name in ('Foo', 'bar', 'Exc'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
app.connect('autodoc-skip-member', skip_member)
|
||||||
|
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||||
|
template, None, False, app, False, {})
|
||||||
|
context = template.render.call_args[0][1]
|
||||||
|
assert context['members'] == ['_Baz', '_Exc', '__builtins__', '__cached__', '__doc__',
|
||||||
|
'__file__', '__name__', '__package__', '_quux', 'qux']
|
||||||
|
assert context['functions'] == []
|
||||||
|
assert context['classes'] == []
|
||||||
|
assert context['exceptions'] == []
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx(testroot='ext-autosummary')
|
||||||
|
def test_autosummary_generate_content_for_module_imported_members(app):
|
||||||
|
import autosummary_dummy_module
|
||||||
|
template = Mock()
|
||||||
|
|
||||||
|
generate_autosummary_content('autosummary_dummy_module', autosummary_dummy_module, None,
|
||||||
|
template, None, True, app, False, {})
|
||||||
|
assert template.render.call_args[0][0] == 'module'
|
||||||
|
|
||||||
|
context = template.render.call_args[0][1]
|
||||||
|
assert context['members'] == ['Exc', 'Foo', 'Union', '_Baz', '_Exc', '__builtins__',
|
||||||
|
'__cached__', '__doc__', '__file__', '__loader__',
|
||||||
|
'__name__', '__package__', '__spec__', '_quux',
|
||||||
|
'bar', 'path', 'qux']
|
||||||
|
assert context['functions'] == ['bar']
|
||||||
|
assert context['all_functions'] == ['_quux', 'bar']
|
||||||
|
assert context['classes'] == ['Foo']
|
||||||
|
assert context['all_classes'] == ['Foo', '_Baz']
|
||||||
|
assert context['exceptions'] == ['Exc']
|
||||||
|
assert context['all_exceptions'] == ['Exc', '_Exc']
|
||||||
|
assert context['attributes'] == ['qux']
|
||||||
|
assert context['all_attributes'] == ['qux']
|
||||||
|
assert context['fullname'] == 'autosummary_dummy_module'
|
||||||
|
assert context['module'] == 'autosummary_dummy_module'
|
||||||
|
assert context['objname'] == ''
|
||||||
|
assert context['name'] == ''
|
||||||
|
assert context['objtype'] == 'module'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary')
|
@pytest.mark.sphinx('dummy', testroot='ext-autosummary')
|
||||||
def test_autosummary_generate(app, status, warning):
|
def test_autosummary_generate(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
@ -209,11 +289,12 @@ 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]) == 5
|
||||||
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.Foo.Bar\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_dummy_module.bar(x[, y])\n\n'
|
assert doctree[3][0][0][2][3].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
||||||
assert doctree[3][0][0][2][4].astext() == 'autosummary_importfail\n\n'
|
assert doctree[3][0][0][2][4].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
|
||||||
|
|
||||||
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'
|
||||||
@ -238,6 +319,11 @@ def test_autosummary_generate(app, status, warning):
|
|||||||
'\n'
|
'\n'
|
||||||
'.. autoclass:: Foo.Bar\n' in FooBar)
|
'.. autoclass:: Foo.Bar\n' in FooBar)
|
||||||
|
|
||||||
|
qux = (app.srcdir / 'generated' / 'autosummary_dummy_module.qux.rst').read_text()
|
||||||
|
assert ('.. currentmodule:: autosummary_dummy_module\n'
|
||||||
|
'\n'
|
||||||
|
'.. autodata:: qux' in qux)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
|
@pytest.mark.sphinx('dummy', testroot='ext-autosummary',
|
||||||
confoverrides={'autosummary_generate_overwrite': False})
|
confoverrides={'autosummary_generate_overwrite': False})
|
||||||
@ -397,7 +483,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())
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ def test_js_source(app, status, warning):
|
|||||||
|
|
||||||
app.builder.build(['contents'])
|
app.builder.build(['contents'])
|
||||||
|
|
||||||
v = '3.4.1'
|
v = '3.5.1'
|
||||||
msg = 'jquery.js version does not match to {v}'.format(v=v)
|
msg = 'jquery.js version does not match to {v}'.format(v=v)
|
||||||
jquery_min = (app.outdir / '_static' / 'jquery.js').read_text()
|
jquery_min = (app.outdir / '_static' / 'jquery.js').read_text()
|
||||||
assert 'jQuery v{v}'.format(v=v) in jquery_min, msg
|
assert 'jQuery v{v}'.format(v=v) in jquery_min, msg
|
||||||
|
@ -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():
|
||||||
|
Loading…
Reference in New Issue
Block a user