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
|
||||
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
||||
caption to the toctree
|
||||
* #7469: autosummary: Support module attributes
|
||||
* #248, #6040: autosummary: Add ``:recursive:`` option to autosummary directive
|
||||
to generate stub files recursively
|
||||
* #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"
|
||||
* #7542: html theme: Make admonition/topic/sidebar scrollable
|
||||
* #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++, parse parameterized noexcept specifiers.
|
||||
* #7294: C++, parse expressions with user-defined literals.
|
||||
@ -73,6 +75,8 @@ Features added
|
||||
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
||||
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
||||
* #7582: napoleon: a type for attribute are represented like type annotation
|
||||
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
||||
handlers to raise specified exceptions
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
@ -90,16 +94,32 @@ Bugs fixed
|
||||
* #7362: autodoc: does not render correct signatures for built-in functions
|
||||
* #7654: autodoc: ``Optional[Union[foo, bar]]`` is presented as
|
||||
``Union[foo, bar, None]``
|
||||
* #7629: autodoc: autofunction emits an unfriendly warning if an invalid object
|
||||
specified
|
||||
* #7650: autodoc: undecorated signature is shown for decorated functions
|
||||
* #7676: autodoc: typo in the default value of autodoc_member_order
|
||||
* #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
|
||||
autodoc-proccess-signature
|
||||
* #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
|
||||
* #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
|
||||
* #7581: napoleon: bad parsing of inline code in attribute docstrings
|
||||
* #7628: imgconverter: runs imagemagick once unnecessary for builders not
|
||||
supporting images
|
||||
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
||||
* #7646: handle errors on event handlers
|
||||
* C++, fix rendering and xrefs in nested names explicitly starting
|
||||
in global scope, e.g., ``::A::B``.
|
||||
* C, fix rendering and xrefs in nested names explicitly starting
|
||||
in global scope, e.g., ``.A.B``.
|
||||
|
||||
Testing
|
||||
--------
|
||||
@ -124,6 +144,7 @@ Bugs fixed
|
||||
|
||||
* #7567: autodoc: parametrized types are shown twice for generic types
|
||||
* #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
|
||||
* #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)
|
||||
|
||||
Emitted when a cross-reference to a Python module or object cannot be
|
||||
resolved. If the event handler can resolve the reference, it should return a
|
||||
Emitted when a cross-reference to an object cannot be resolved.
|
||||
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
|
||||
*node*. Usually this node is a :class:`reference` node containing *contnode*
|
||||
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 node: The :class:`pending_xref` node to be resolved. Its attributes
|
||||
|
@ -285,8 +285,12 @@ The following variables available in the templates:
|
||||
|
||||
.. data:: attributes
|
||||
|
||||
List containing names of "public" attributes in the class. Only available
|
||||
for classes.
|
||||
List containing names of "public" attributes in the class/module. Only
|
||||
available for classes and modules.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Attributes of modules are supported.
|
||||
|
||||
.. data:: modules
|
||||
|
||||
|
@ -159,7 +159,7 @@ Doctest blocks
|
||||
Doctest blocks (:duref:`ref <doctest-blocks>`) are interactive Python sessions
|
||||
cut-and-pasted into docstrings. They do not require the
|
||||
: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
|
||||
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.
|
||||
"""
|
||||
|
||||
@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):
|
||||
"""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."""
|
||||
child_text_separator = ', '
|
||||
|
||||
def astext(self):
|
||||
return '({})'.format(super().astext())
|
||||
|
||||
|
||||
class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
|
||||
"""Node for a single parameter."""
|
||||
|
@ -436,22 +436,32 @@ class Sphinx:
|
||||
logger.debug('[app] disconnecting event: [id=%s]', 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.
|
||||
|
||||
Return the return values of all callbacks as a list. Do not emit core
|
||||
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.
|
||||
|
||||
Return the result of the first callback that doesn't return ``None``.
|
||||
|
||||
.. 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
|
||||
|
||||
|
@ -357,6 +357,7 @@ def generate(d: Dict, overwrite: bool = True, silent: bool = False, templatedir:
|
||||
d.setdefault('extensions', [])
|
||||
d['copyright'] = time.strftime('%Y') + ', ' + d['author']
|
||||
|
||||
d["path"] = os.path.abspath(d['path'])
|
||||
ensuredir(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,
|
||||
# else append directly to signode.
|
||||
# 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.
|
||||
dest = signode
|
||||
if mode == 'lastIsName':
|
||||
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)):
|
||||
ident = names[i]
|
||||
if not first:
|
||||
|
@ -752,11 +752,17 @@ class ASTNestedName(ASTBase):
|
||||
names = self.names[:-1] if mode == 'lastIsName' else self.names
|
||||
# If lastIsName, then wrap all of the prefix in a desc_addname,
|
||||
# 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.
|
||||
dest = signode
|
||||
if mode == 'lastIsName':
|
||||
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)):
|
||||
nne = names[i]
|
||||
template = self.templates[i]
|
||||
@ -3722,8 +3728,8 @@ class LookupKey:
|
||||
class Symbol:
|
||||
debug_indent = 0
|
||||
debug_indent_string = " "
|
||||
debug_lookup = False
|
||||
debug_show_tree = False
|
||||
debug_lookup = False # overridden by the corresponding config value
|
||||
debug_show_tree = False # overridden by the corresponding config value
|
||||
|
||||
@staticmethod
|
||||
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_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 {
|
||||
'version': 'builtin',
|
||||
'env_version': 2,
|
||||
'env_version': 3,
|
||||
'parallel_read_safe': True,
|
||||
'parallel_write_safe': True,
|
||||
}
|
||||
|
@ -116,7 +116,8 @@ class PycodeError(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
|
||||
|
||||
|
||||
|
@ -13,15 +13,16 @@
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
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.errors import ExtensionError
|
||||
from sphinx.errors import ExtensionError, SphinxError
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Type # for python3.5.1
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
|
||||
@ -88,7 +89,8 @@ class EventManager:
|
||||
if listener.id == listener_id:
|
||||
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."""
|
||||
try:
|
||||
logger.debug('[app] emitting event: %r%s', name, repr(args)[:100])
|
||||
@ -100,19 +102,29 @@ class EventManager:
|
||||
results = []
|
||||
listeners = sorted(self.listeners[name], key=attrgetter("priority"))
|
||||
for listener in listeners:
|
||||
if self.app is None:
|
||||
# for compatibility; RemovedInSphinx40Warning
|
||||
results.append(listener.handler(*args))
|
||||
else:
|
||||
results.append(listener.handler(self.app, *args))
|
||||
try:
|
||||
if self.app is None:
|
||||
# for compatibility; RemovedInSphinx40Warning
|
||||
results.append(listener.handler(*args))
|
||||
else:
|
||||
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
|
||||
|
||||
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.
|
||||
|
||||
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:
|
||||
return result
|
||||
return None
|
||||
|
@ -15,14 +15,15 @@ import re
|
||||
import warnings
|
||||
from inspect import Parameter
|
||||
from types import ModuleType
|
||||
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
||||
from unittest.mock import patch
|
||||
from typing import (
|
||||
Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
||||
)
|
||||
|
||||
from docutils.statemachine import StringList
|
||||
|
||||
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.environment import BuildEnvironment
|
||||
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
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
@ -427,8 +438,15 @@ class Documenter:
|
||||
directive = getattr(self, 'directivetype', self.objtype)
|
||||
name = self.format_name()
|
||||
sourcename = self.get_sourcename()
|
||||
self.add_line('.. %s:%s:: %s%s' % (domain, directive, name, sig),
|
||||
sourcename)
|
||||
|
||||
# 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)
|
||||
if i == 0:
|
||||
prefix = " " * len(prefix)
|
||||
|
||||
if self.options.noindex:
|
||||
self.add_line(' :noindex:', sourcename)
|
||||
if self.objpath:
|
||||
@ -521,12 +539,12 @@ class Documenter:
|
||||
else:
|
||||
logger.warning(__('missing attribute %s in object %s') %
|
||||
(name, self.fullname), type='autodoc')
|
||||
return False, sorted(selected)
|
||||
return False, selected
|
||||
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:
|
||||
return False, sorted((m.name, m.value) for m in members.values()
|
||||
if m.directly_defined)
|
||||
return False, [(m.name, m.value) for m in members.values()
|
||||
if m.directly_defined]
|
||||
|
||||
def filter_members(self, members: List[Tuple[str, Any]], want_all: bool
|
||||
) -> List[Tuple[str, Any, bool]]:
|
||||
@ -699,17 +717,26 @@ class Documenter:
|
||||
member_order = self.options.member_order or \
|
||||
self.env.config.autodoc_member_order
|
||||
if member_order == 'groupwise':
|
||||
# sort by group; relies on stable sort to keep items in the
|
||||
# same group sorted alphabetically
|
||||
memberdocumenters.sort(key=lambda e: e[0].member_order)
|
||||
elif member_order == 'bysource' and self.analyzer:
|
||||
# sort by source order, by virtue of the module analyzer
|
||||
tagorder = self.analyzer.tagorder
|
||||
# sort by group; alphabetically within groups
|
||||
memberdocumenters.sort(key=lambda e: (e[0].member_order, e[0].name))
|
||||
elif member_order == 'bysource':
|
||||
if self.analyzer:
|
||||
# sort by source order, by virtue of the module analyzer
|
||||
tagorder = self.analyzer.tagorder
|
||||
|
||||
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
|
||||
fullname = entry[0].name.split('::')[1]
|
||||
return tagorder.get(fullname, len(tagorder))
|
||||
memberdocumenters.sort(key=keyfunc)
|
||||
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
|
||||
fullname = entry[0].name.split('::')[1]
|
||||
return tagorder.get(fullname, len(tagorder))
|
||||
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:
|
||||
documenter.generate(
|
||||
@ -817,7 +844,7 @@ class ModuleDocumenter(Documenter):
|
||||
'noindex': bool_option, 'inherited-members': inherited_members_option,
|
||||
'show-inheritance': bool_option, 'synopsis': identity,
|
||||
'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,
|
||||
'imported-members': bool_option, 'ignore-module-all': bool_option
|
||||
} # type: Dict[str, Callable]
|
||||
@ -948,7 +975,7 @@ class ClassLevelDocumenter(Documenter):
|
||||
|
||||
try:
|
||||
modname, qualname = split_full_qualified_name(mod_cls)
|
||||
parents = qualname.split(".")
|
||||
parents = qualname.split(".") if qualname else []
|
||||
except ImportError:
|
||||
parents = mod_cls.split(".")
|
||||
|
||||
@ -1057,12 +1084,18 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
unwrapped = inspect.unwrap(self.object)
|
||||
try:
|
||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
||||
sig = inspect.signature(unwrapped)
|
||||
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||
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)
|
||||
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 = ''
|
||||
|
||||
if self.env.config.strip_signature_backslash:
|
||||
@ -1075,41 +1108,28 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
|
||||
def add_directive_header(self, sig: str) -> None:
|
||||
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):
|
||||
self.add_line(' :async:', sourcename)
|
||||
|
||||
def add_singledispatch_directive_header(self, sig: str) -> None:
|
||||
sourcename = self.get_sourcename()
|
||||
def format_signature(self, **kwargs: Any) -> str:
|
||||
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)
|
||||
if inspect.is_singledispatch_function(self.object):
|
||||
# append signature of singledispatch'ed functions
|
||||
for typ, func in self.object.registry.items():
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
|
||||
# output first line of header
|
||||
self.add_line(*add_line.call_args_list[0][0])
|
||||
documenter = FunctionDocumenter(self.directive, '')
|
||||
documenter.object = func
|
||||
sigs.append(documenter.format_signature())
|
||||
|
||||
# inserts signature of singledispatch'ed functions
|
||||
for typ, func in self.object.registry.items():
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
|
||||
documenter = FunctionDocumenter(self.directive, '')
|
||||
documenter.object = func
|
||||
self.add_line(' %s%s' % (self.format_name(),
|
||||
documenter.format_signature()),
|
||||
sourcename)
|
||||
|
||||
# output remains of directive header
|
||||
for call in add_line.call_args_list[1:]:
|
||||
self.add_line(*call[0])
|
||||
return "\n".join(sigs)
|
||||
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||
"""Annotate type hint to the first argument of function if needed."""
|
||||
@ -1157,7 +1177,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
||||
option_spec = {
|
||||
'members': members_option, 'undoc-members': bool_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,
|
||||
'private-members': bool_option, 'special-members': members_option,
|
||||
} # type: Dict[str, Callable]
|
||||
@ -1453,7 +1473,6 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
unwrapped = inspect.unwrap(self.object)
|
||||
try:
|
||||
if self.object == object.__init__ and self.parent != object:
|
||||
# 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.
|
||||
args = '()'
|
||||
else:
|
||||
if inspect.isstaticmethod(unwrapped, cls=self.parent, name=self.object_name):
|
||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, False)
|
||||
sig = inspect.signature(unwrapped, bound_method=False)
|
||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||
sig = inspect.signature(self.object, bound_method=False)
|
||||
else:
|
||||
self.env.app.emit('autodoc-before-process-signature', unwrapped, True)
|
||||
sig = inspect.signature(unwrapped, bound_method=True)
|
||||
self.env.app.emit('autodoc-before-process-signature', self.object, True)
|
||||
|
||||
meth = self.parent.__dict__.get(self.objpath[-1], None)
|
||||
if meth and inspect.is_singledispatch_method(meth):
|
||||
sig = inspect.signature(self.object, bound_method=True,
|
||||
follow_wrapped=True)
|
||||
else:
|
||||
sig = inspect.signature(self.object, bound_method=True)
|
||||
args = stringify_signature(sig, **kwargs)
|
||||
except TypeError as exc:
|
||||
logger.warning(__("Failed to get a method signature for %s: %s"),
|
||||
self.fullname, exc)
|
||||
return None
|
||||
except ValueError:
|
||||
args = ''
|
||||
|
||||
@ -1478,11 +1507,7 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
return args
|
||||
|
||||
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()
|
||||
obj = self.parent.__dict__.get(self.object_name, self.object)
|
||||
@ -1500,34 +1525,26 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
def document_members(self, all_members: bool = False) -> None:
|
||||
pass
|
||||
|
||||
def add_singledispatch_directive_header(self, sig: str) -> None:
|
||||
sourcename = self.get_sourcename()
|
||||
def format_signature(self, **kwargs: Any) -> str:
|
||||
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])
|
||||
for typ, func in meth.dispatcher.registry.items():
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
if inspect.is_singledispatch_method(meth):
|
||||
# append signature of singledispatch'ed functions
|
||||
for typ, func in meth.dispatcher.registry.items():
|
||||
if typ is object:
|
||||
pass # default implementation. skipped.
|
||||
else:
|
||||
self.annotate_to_first_argument(func, typ)
|
||||
|
||||
documenter = MethodDocumenter(self.directive, '')
|
||||
documenter.object = func
|
||||
self.add_line(' %s%s' % (self.format_name(),
|
||||
documenter.format_signature()),
|
||||
sourcename)
|
||||
documenter = MethodDocumenter(self.directive, '')
|
||||
documenter.parent = self.parent
|
||||
documenter.object = func
|
||||
documenter.objpath = [None]
|
||||
sigs.append(documenter.format_signature())
|
||||
|
||||
# output remains of directive header
|
||||
for call in add_line.call_args_list[1:]:
|
||||
self.add_line(*call[0])
|
||||
return "\n".join(sigs)
|
||||
|
||||
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
|
||||
"""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)
|
||||
|
||||
|
||||
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]:
|
||||
app.add_autodocumenter(ModuleDocumenter)
|
||||
app.add_autodocumenter(ClassDocumenter)
|
||||
@ -1779,7 +1804,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
app.add_autodocumenter(SlotsAttributeDocumenter)
|
||||
|
||||
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_docstring_signature', True, 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-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.typehints')
|
||||
|
||||
|
@ -15,6 +15,7 @@ from docutils import nodes
|
||||
from docutils.nodes import Node
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.domains.std import StandardDomain
|
||||
from sphinx.locale import __
|
||||
from sphinx.util import logging
|
||||
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:
|
||||
labels = app.env.domaindata['std']['labels']
|
||||
anonlabels = app.env.domaindata['std']['anonlabels']
|
||||
domain = cast(StandardDomain, app.env.get_domain('std'))
|
||||
for node in document.traverse(nodes.section):
|
||||
if (app.config.autosectionlabel_maxdepth and
|
||||
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)
|
||||
sectname = clean_astext(title)
|
||||
|
||||
if name in labels:
|
||||
if name in domain.labels:
|
||||
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)
|
||||
|
||||
anonlabels[name] = docname, labelid
|
||||
labels[name] = docname, labelid, sectname
|
||||
domain.anonlabels[name] = docname, labelid
|
||||
domain.labels[name] = docname, labelid, sectname
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
|
@ -302,8 +302,7 @@ class Autosummary(SphinxDirective):
|
||||
with mock(self.config.autosummary_mock_imports):
|
||||
real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes)
|
||||
except ImportError:
|
||||
logger.warning(__('failed to import %s'), name)
|
||||
items.append((name, '', '', name))
|
||||
logger.warning(__('autosummary: failed to import %s'), name)
|
||||
continue
|
||||
|
||||
self.bridge.result = StringList() # initialize for each documenter
|
||||
|
@ -18,6 +18,7 @@
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import inspect
|
||||
import locale
|
||||
import os
|
||||
import pkgutil
|
||||
@ -42,6 +43,7 @@ from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warnin
|
||||
from sphinx.ext.autodoc import Documenter
|
||||
from sphinx.ext.autosummary import import_by_name, get_documenter
|
||||
from sphinx.locale import __
|
||||
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||
from sphinx.registry import SphinxComponentRegistry
|
||||
from sphinx.util import logging
|
||||
from sphinx.util import rst
|
||||
@ -175,6 +177,56 @@ class AutosummaryRenderer:
|
||||
# -- 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,
|
||||
template: AutosummaryRenderer, template_name: str,
|
||||
imported_members: bool, app: Any,
|
||||
@ -218,6 +270,21 @@ def generate_autosummary_content(name: str, obj: Any, parent: Any,
|
||||
public.append(name)
|
||||
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]]:
|
||||
items = [] # type: List[str]
|
||||
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)
|
||||
|
||||
if doc.objtype == 'module':
|
||||
ns['members'] = dir(obj)
|
||||
scanner = ModuleScanner(app, obj)
|
||||
ns['members'] = scanner.scan(imported_members)
|
||||
ns['functions'], ns['all_functions'] = \
|
||||
get_members(obj, {'function'}, imported=imported_members)
|
||||
ns['classes'], ns['all_classes'] = \
|
||||
get_members(obj, {'class'}, imported=imported_members)
|
||||
ns['exceptions'], ns['all_exceptions'] = \
|
||||
get_members(obj, {'exception'}, imported=imported_members)
|
||||
ns['attributes'], ns['all_attributes'] = \
|
||||
get_module_attrs(ns['members'])
|
||||
ispackage = hasattr(obj, '__path__')
|
||||
if ispackage and recursive:
|
||||
ns['modules'], ns['all_modules'] = get_modules(obj)
|
||||
|
@ -2,6 +2,17 @@
|
||||
|
||||
.. automodule:: {{ fullname }}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Module Attributes
|
||||
|
||||
.. autosummary::
|
||||
{% for item in attributes %}
|
||||
{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block functions %}
|
||||
{% if 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
|
||||
) -> Node:
|
||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||
if node['reftype'] == 'viewcode':
|
||||
if app.builder.format != 'html':
|
||||
return None
|
||||
elif node['reftype'] == 'viewcode':
|
||||
# resolve our "viewcode" reference nodes -- they need special treatment
|
||||
return make_refnode(app.builder, node['refdoc'], node['reftarget'],
|
||||
node['refid'], contnode)
|
||||
|
||||
|
@ -27,9 +27,9 @@ if "%1" == "help" (
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
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. 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. latexpdf to make LaTeX files and then PDFs out of them
|
||||
echo. text to make text files
|
||||
@ -69,7 +69,7 @@ if errorlevel 9009 (
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
echo.https://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
|
@ -120,6 +120,7 @@
|
||||
{%- else %}
|
||||
<meta http-equiv="Content-Type" content="text/html; charset={{ encoding }}" />
|
||||
{%- endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{{- metatags }}
|
||||
{%- block htmltitle %}
|
||||
<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
|
||||
if newnode is None:
|
||||
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
|
||||
# we are in nit-picky mode
|
||||
if newnode is None:
|
||||
|
@ -408,13 +408,20 @@ def is_builtin_class_method(obj: Any, attr_name: str) -> bool:
|
||||
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*.
|
||||
|
||||
: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:
|
||||
signature = inspect.signature(subject)
|
||||
try:
|
||||
signature = inspect.signature(subject, follow_wrapped=follow_wrapped)
|
||||
except ValueError:
|
||||
# follow built-in wrappers up (ex. functools.lru_cache)
|
||||
signature = inspect.signature(subject)
|
||||
parameters = list(signature.parameters.values())
|
||||
return_annotation = signature.return_annotation
|
||||
except IndexError:
|
||||
|
@ -1,5 +1,9 @@
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def deco1(func):
|
||||
"""docstring for deco1"""
|
||||
@wraps(func)
|
||||
def wrapper():
|
||||
return func()
|
||||
|
||||
@ -14,3 +18,14 @@ def deco2(condition, message):
|
||||
|
||||
return wrapper
|
||||
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
|
||||
|
||||
|
||||
@ -17,5 +17,25 @@ class Foo:
|
||||
pass
|
||||
|
||||
|
||||
def bar(x: Union[int, str], y: int = 1):
|
||||
class _Baz:
|
||||
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.Bar
|
||||
autosummary_dummy_module.bar
|
||||
autosummary_dummy_module.qux
|
||||
autosummary_importfail
|
||||
|
@ -27,15 +27,29 @@ def parse(name, string):
|
||||
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
|
||||
ast = parse(name, input)
|
||||
ast = parse(name, inputActual)
|
||||
res = str(ast)
|
||||
if res != output:
|
||||
if res != outputAst:
|
||||
print("")
|
||||
print("Input: ", input)
|
||||
print("Result: ", res)
|
||||
print("Expected: ", output)
|
||||
print("Expected: ", outputAst)
|
||||
raise DefinitionError("")
|
||||
rootSymbol = Symbol(None, None, None, None)
|
||||
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
||||
@ -43,6 +57,13 @@ def _check(name, input, idDict, output):
|
||||
signode = addnodes.desc_signature(input, '')
|
||||
parentNode += signode
|
||||
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]
|
||||
for i in range(1, _max_id + 1):
|
||||
@ -75,14 +96,15 @@ def _check(name, input, idDict, output):
|
||||
raise DefinitionError("")
|
||||
|
||||
|
||||
def check(name, input, idDict, output=None):
|
||||
def check(name, input, idDict, output=None, key=None, asTextOutput=None):
|
||||
if output is None:
|
||||
output = input
|
||||
# First, check without semicolon
|
||||
_check(name, input, idDict, output)
|
||||
_check(name, input, idDict, output, key, asTextOutput)
|
||||
if name != 'macro':
|
||||
# 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():
|
||||
@ -234,24 +256,24 @@ def test_expressions():
|
||||
|
||||
|
||||
def test_type_definitions():
|
||||
check('type', "T", {1: "T"})
|
||||
check('type', "{key}T", {1: "T"})
|
||||
|
||||
check('type', "bool *b", {1: 'b'})
|
||||
check('type', "bool *const b", {1: 'b'})
|
||||
check('type', "bool *const *b", {1: 'b'})
|
||||
check('type', "bool *volatile *b", {1: 'b'})
|
||||
check('type', "bool *restrict *b", {1: 'b'})
|
||||
check('type', "bool *volatile const b", {1: 'b'})
|
||||
check('type', "bool *volatile const b", {1: 'b'})
|
||||
check('type', "bool *volatile const *b", {1: 'b'})
|
||||
check('type', "bool b[]", {1: 'b'})
|
||||
check('type', "long long int foo", {1: 'foo'})
|
||||
check('type', "{key}bool *b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *const b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *const *b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *volatile *b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *restrict *b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool *volatile const *b", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}bool b[]", {1: 'b'}, key='typedef')
|
||||
check('type', "{key}long long int foo", {1: 'foo'}, key='typedef')
|
||||
# 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
|
||||
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||
{1: 'gpio_callback_t'})
|
||||
check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||
{1: 'gpio_callback_t'}, key='typedef')
|
||||
|
||||
|
||||
def test_macro_definitions():
|
||||
@ -378,28 +400,34 @@ def test_function_definitions():
|
||||
output='void f(int arr[static volatile const 42])')
|
||||
|
||||
|
||||
def test_union_definitions():
|
||||
check('struct', 'A', {1: 'A'})
|
||||
class test_nested_name():
|
||||
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():
|
||||
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():
|
||||
check('enum', 'A', {1: 'A'})
|
||||
check('enum', '{key}A', {1: 'A'})
|
||||
|
||||
check('enumerator', 'A', {1: 'A'})
|
||||
check('enumerator', 'A = 42', {1: 'A'})
|
||||
check('enumerator', '{key}A', {1: 'A'})
|
||||
check('enumerator', '{key}A = 42', {1: 'A'})
|
||||
|
||||
|
||||
def test_anon_definitions():
|
||||
return # TODO
|
||||
check('class', '@a', {3: "Ut1_a"})
|
||||
check('union', '@a', {3: "Ut1_a"})
|
||||
check('enum', '@a', {3: "Ut1_a"})
|
||||
check('class', '@1', {3: "Ut1_1"})
|
||||
check('class', '@a::A', {3: "NUt1_a1AE"})
|
||||
check('struct', '@a', {1: "@a"}, asTextOutput='struct [anonymous]')
|
||||
check('union', '@a', {1: "@a"}, asTextOutput='union [anonymous]')
|
||||
check('enum', '@a', {1: "@a"}, asTextOutput='enum [anonymous]')
|
||||
check('struct', '@1', {1: "@1"}, asTextOutput='struct [anonymous]')
|
||||
check('struct', '@a.A', {1: "@a.A"}, asTextOutput='struct [anonymous].A')
|
||||
|
||||
|
||||
def test_initializers():
|
||||
|
@ -33,15 +33,29 @@ def parse(name, string):
|
||||
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
|
||||
ast = parse(name, input)
|
||||
ast = parse(name, inputActual)
|
||||
res = str(ast)
|
||||
if res != output:
|
||||
if res != outputAst:
|
||||
print("")
|
||||
print("Input: ", input)
|
||||
print("Result: ", res)
|
||||
print("Expected: ", output)
|
||||
print("Expected: ", outputAst)
|
||||
raise DefinitionError("")
|
||||
rootSymbol = Symbol(None, None, None, None, None, None)
|
||||
symbol = rootSymbol.add_declaration(ast, docname="TestDoc")
|
||||
@ -49,6 +63,13 @@ def _check(name, input, idDict, output):
|
||||
signode = addnodes.desc_signature(input, '')
|
||||
parentNode += signode
|
||||
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]
|
||||
for i in range(1, _max_id + 1):
|
||||
@ -81,13 +102,14 @@ def _check(name, input, idDict, output):
|
||||
raise DefinitionError("")
|
||||
|
||||
|
||||
def check(name, input, idDict, output=None):
|
||||
def check(name, input, idDict, output=None, key=None, asTextOutput=None):
|
||||
if output is None:
|
||||
output = input
|
||||
# First, check without semicolon
|
||||
_check(name, input, idDict, output)
|
||||
_check(name, input, idDict, output, key, asTextOutput)
|
||||
# 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():
|
||||
@ -113,10 +135,11 @@ def test_fundamental_types():
|
||||
def test_expressions():
|
||||
def exprCheck(expr, id, id4=None):
|
||||
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:
|
||||
idDict[4] = ids % id4
|
||||
check('class', 'template<> C<a[%s]>' % expr, idDict)
|
||||
check('class', 'template<> {key}C<a[%s]>' % expr, idDict)
|
||||
|
||||
class Config:
|
||||
cpp_id_attributes = ["id_attr"]
|
||||
@ -229,8 +252,8 @@ def test_expressions():
|
||||
exprCheck('new int()', 'nw_ipiE')
|
||||
exprCheck('new int(5, 42)', 'nw_ipiL5EL42EE')
|
||||
exprCheck('::new int', 'nw_iE')
|
||||
exprCheck('new int{}', 'nw_iilE')
|
||||
exprCheck('new int{5, 42}', 'nw_iilL5EL42EE')
|
||||
exprCheck('new int{{}}', 'nw_iilE')
|
||||
exprCheck('new int{{5, 42}}', 'nw_iilL5EL42EE')
|
||||
# delete-expression
|
||||
exprCheck('delete p', 'dl1p')
|
||||
exprCheck('delete [] p', 'da1p')
|
||||
@ -291,7 +314,7 @@ def test_expressions():
|
||||
exprCheck('a xor_eq 5', 'eO1aL5E')
|
||||
exprCheck('a |= 5', 'oR1aL5E')
|
||||
exprCheck('a or_eq 5', 'oR1aL5E')
|
||||
exprCheck('a = {1, 2, 3}', 'aS1ailL1EL2EL3EE')
|
||||
exprCheck('a = {{1, 2, 3}}', 'aS1ailL1EL2EL3EE')
|
||||
# comma operator
|
||||
exprCheck('a, 5', 'cm1aL5E')
|
||||
|
||||
@ -301,8 +324,8 @@ def test_expressions():
|
||||
check('function', 'template<> void f(A<B, 2> &v)',
|
||||
{2: "IE1fR1AI1BX2EE", 3: "IE1fR1AI1BXL2EEE", 4: "IE1fvR1AI1BXL2EEE"})
|
||||
exprCheck('A<1>::value', 'N1AIXL1EEE5valueE')
|
||||
check('class', "template<int T = 42> A", {2: "I_iE1A"})
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||
check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
|
||||
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||
|
||||
exprCheck('operator()()', 'clclE')
|
||||
exprCheck('operator()<int>()', 'clclIiEE')
|
||||
@ -312,58 +335,59 @@ def test_expressions():
|
||||
|
||||
|
||||
def test_type_definitions():
|
||||
check("type", "public bool b", {1: "b", 2: "1b"}, "bool b")
|
||||
check("type", "bool A::b", {1: "A::b", 2: "N1A1bE"})
|
||||
check("type", "bool *b", {1: "b", 2: "1b"})
|
||||
check("type", "bool *const b", {1: "b", 2: "1b"})
|
||||
check("type", "bool *volatile const b", {1: "b", 2: "1b"})
|
||||
check("type", "bool *volatile const b", {1: "b", 2: "1b"})
|
||||
check("type", "bool *volatile const *b", {1: "b", 2: "1b"})
|
||||
check("type", "bool &b", {1: "b", 2: "1b"})
|
||||
check("type", "bool b[]", {1: "b", 2: "1b"})
|
||||
check("type", "std::pair<int, int> coord", {1: "coord", 2: "5coord"})
|
||||
check("type", "long long int foo", {1: "foo", 2: "3foo"})
|
||||
check("type", 'std::vector<std::pair<std::string, long long>> module::blah',
|
||||
{1: "module::blah", 2: "N6module4blahE"})
|
||||
check("type", "std::function<void()> F", {1: "F", 2: "1F"})
|
||||
check("type", "std::function<R(A1, A2)> F", {1: "F", 2: "1F"})
|
||||
check("type", "std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"})
|
||||
check("type", "std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"})
|
||||
check("type", "MyContainer::const_iterator",
|
||||
check("type", "public bool b", {1: "b", 2: "1b"}, "{key}bool b", key='typedef')
|
||||
check("type", "{key}bool A::b", {1: "A::b", 2: "N1A1bE"}, key='typedef')
|
||||
check("type", "{key}bool *b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool *const b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool *volatile const b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool *volatile const *b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool &b", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}bool b[]", {1: "b", 2: "1b"}, key='typedef')
|
||||
check("type", "{key}std::pair<int, int> coord", {1: "coord", 2: "5coord"}, key='typedef')
|
||||
check("type", "{key}long long int foo", {1: "foo", 2: "3foo"}, key='typedef')
|
||||
check("type", '{key}std::vector<std::pair<std::string, long long>> module::blah',
|
||||
{1: "module::blah", 2: "N6module4blahE"}, key='typedef')
|
||||
check("type", "{key}std::function<void()> F", {1: "F", 2: "1F"}, key='typedef')
|
||||
check("type", "{key}std::function<R(A1, A2)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||
check("type", "{key}std::function<R(A1, A2, A3)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||
check("type", "{key}std::function<R(A1, A2, A3, As...)> F", {1: "F", 2: "1F"}, key='typedef')
|
||||
check("type", "{key}MyContainer::const_iterator",
|
||||
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"})
|
||||
check("type",
|
||||
"public MyContainer::const_iterator",
|
||||
{1: "MyContainer::const_iterator", 2: "N11MyContainer14const_iteratorE"},
|
||||
output="MyContainer::const_iterator")
|
||||
output="{key}MyContainer::const_iterator")
|
||||
# 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
|
||||
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', 'A = decltype(b)', {2: '1A'})
|
||||
check('type', '{key}A = B', {2: '1A'}, key='using')
|
||||
check('type', '{key}A = decltype(b)', {2: '1A'}, key='using')
|
||||
|
||||
# from breathe#267 (named function parameters for function pointers
|
||||
check('type', 'void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||
{1: 'gpio_callback_t', 2: '15gpio_callback_t'})
|
||||
check('type', 'void (*f)(std::function<void(int i)> g)', {1: 'f', 2: '1f'})
|
||||
check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)',
|
||||
{1: 'gpio_callback_t', 2: '15gpio_callback_t'}, key='typedef')
|
||||
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', 'T = Q<A::operator()<int>>', {2: '1T'})
|
||||
check('type', 'T = Q<A::operator bool>', {2: '1T'})
|
||||
check('type', '{key}T = Q<A::operator()>', {2: '1T'}, key='using')
|
||||
check('type', '{key}T = Q<A::operator()<int>>', {2: '1T'}, key='using')
|
||||
check('type', '{key}T = Q<A::operator bool>', {2: '1T'}, key='using')
|
||||
|
||||
|
||||
def test_concept_definitions():
|
||||
check('concept', 'template<typename Param> A::B::Concept',
|
||||
check('concept', 'template<typename Param> {key}A::B::Concept',
|
||||
{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'})
|
||||
with pytest.raises(DefinitionError):
|
||||
parse('concept', 'Foo')
|
||||
parse('concept', '{key}Foo')
|
||||
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():
|
||||
@ -639,95 +663,102 @@ def test_operators():
|
||||
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():
|
||||
check('class', 'public A', {1: "A", 2: "1A"}, output='A')
|
||||
check('class', 'private A', {1: "A", 2: "1A"})
|
||||
check('class', 'A final', {1: 'A', 2: '1A'})
|
||||
check('class', 'public A', {1: "A", 2: "1A"}, output='{key}A')
|
||||
check('class', 'private {key}A', {1: "A", 2: "1A"})
|
||||
check('class', '{key}A final', {1: 'A', 2: '1A'})
|
||||
|
||||
# test bases
|
||||
check('class', 'A', {1: "A", 2: "1A"})
|
||||
check('class', 'A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
|
||||
check('class', 'A : B', {1: "A", 2: "1A"})
|
||||
check('class', 'A : private B', {1: "A", 2: "1A"})
|
||||
check('class', 'A : public B', {1: "A", 2: "1A"})
|
||||
check('class', 'A : B, C', {1: "A", 2: "1A"})
|
||||
check('class', '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 : private virtual B', {1: 'A', 2: '1A'})
|
||||
check('class', 'A : B, virtual C', {1: 'A', 2: '1A'})
|
||||
check('class', 'A : public virtual B', {1: 'A', 2: '1A'})
|
||||
check('class', 'A : B, C...', {1: 'A', 2: '1A'})
|
||||
check('class', 'A : B..., C', {1: 'A', 2: '1A'})
|
||||
check('class', '{key}A', {1: "A", 2: "1A"})
|
||||
check('class', '{key}A::B::C', {1: "A::B::C", 2: "N1A1B1CE"})
|
||||
check('class', '{key}A : B', {1: "A", 2: "1A"})
|
||||
check('class', '{key}A : private B', {1: "A", 2: "1A"})
|
||||
check('class', '{key}A : public B', {1: "A", 2: "1A"})
|
||||
check('class', '{key}A : B, C', {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='{key}A : private virtual B')
|
||||
check('class', '{key}A : private virtual B', {1: 'A', 2: '1A'})
|
||||
check('class', '{key}A : B, virtual C', {1: 'A', 2: '1A'})
|
||||
check('class', '{key}A : public virtual B', {1: 'A', 2: '1A'})
|
||||
check('class', '{key}A : B, C...', {1: 'A', 2: '1A'})
|
||||
check('class', '{key}A : B..., C', {1: 'A', 2: '1A'})
|
||||
|
||||
# from #4094
|
||||
check('class', 'template<class, class = std::void_t<>> has_var', {2: 'I00E7has_var'})
|
||||
check('class', 'template<class T> has_var<T, std::void_t<decltype(&T::var)>>',
|
||||
check('class', 'template<class, class = std::void_t<>> {key}has_var', {2: 'I00E7has_var'})
|
||||
check('class', 'template<class T> {key}has_var<T, std::void_t<decltype(&T::var)>>',
|
||||
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
|
||||
|
||||
|
||||
check('class', 'template<typename ...Ts> T<int (*)(Ts)...>',
|
||||
check('class', 'template<typename ...Ts> {key}T<int (*)(Ts)...>',
|
||||
{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'})
|
||||
|
||||
|
||||
def test_union_definitions():
|
||||
check('union', 'A', {2: "1A"})
|
||||
check('union', '{key}A', {2: "1A"})
|
||||
|
||||
|
||||
def test_enum_definitions():
|
||||
check('enum', 'A', {2: "1A"})
|
||||
check('enum', 'A : std::underlying_type<B>::type', {2: "1A"})
|
||||
check('enum', 'A : unsigned int', {2: "1A"})
|
||||
check('enum', 'public A', {2: "1A"}, output='A')
|
||||
check('enum', 'private A', {2: "1A"})
|
||||
check('enum', '{key}A', {2: "1A"})
|
||||
check('enum', '{key}A : std::underlying_type<B>::type', {2: "1A"})
|
||||
check('enum', '{key}A : unsigned int', {2: "1A"})
|
||||
check('enum', 'public A', {2: "1A"}, output='{key}A')
|
||||
check('enum', 'private {key}A', {2: "1A"})
|
||||
|
||||
check('enumerator', 'A', {2: "1A"})
|
||||
check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||
check('enumerator', '{key}A', {2: "1A"})
|
||||
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
|
||||
|
||||
|
||||
def test_anon_definitions():
|
||||
check('class', '@a', {3: "Ut1_a"})
|
||||
check('union', '@a', {3: "Ut1_a"})
|
||||
check('enum', '@a', {3: "Ut1_a"})
|
||||
check('class', '@1', {3: "Ut1_1"})
|
||||
check('class', '@a::A', {3: "NUt1_a1AE"})
|
||||
check('class', '@a', {3: "Ut1_a"}, asTextOutput='class [anonymous]')
|
||||
check('union', '@a', {3: "Ut1_a"}, asTextOutput='union [anonymous]')
|
||||
check('enum', '@a', {3: "Ut1_a"}, asTextOutput='enum [anonymous]')
|
||||
check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]')
|
||||
check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A')
|
||||
|
||||
|
||||
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
|
||||
check('class', "template<> A", {2: "IE1A"})
|
||||
check('class', "template<> {key}A", {2: "IE1A"})
|
||||
check('function', "template<> void A()", {2: "IE1Av", 4: "IE1Avv"})
|
||||
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):
|
||||
parse('enum', "template<> A")
|
||||
with pytest.raises(DefinitionError):
|
||||
parse('enumerator', "template<> A")
|
||||
# then all the real tests
|
||||
check('class', "template<typename T1, typename T2> A", {2: "I00E1A"})
|
||||
check('type', "template<> a", {2: "IE1a"})
|
||||
check('class', "template<typename T1, typename T2> {key}A", {2: "I00E1A"})
|
||||
check('type', "template<> {key}a", {2: "IE1a"}, key='using')
|
||||
|
||||
check('class', "template<typename T> A", {2: "I0E1A"})
|
||||
check('class', "template<class T> A", {2: "I0E1A"})
|
||||
check('class', "template<typename ...T> A", {2: "IDpE1A"})
|
||||
check('class', "template<typename...> A", {2: "IDpE1A"})
|
||||
check('class', "template<typename = Test> A", {2: "I0E1A"})
|
||||
check('class', "template<typename T = Test> A", {2: "I0E1A"})
|
||||
check('class', "template<typename T> {key}A", {2: "I0E1A"})
|
||||
check('class', "template<class T> {key}A", {2: "I0E1A"})
|
||||
check('class', "template<typename ...T> {key}A", {2: "IDpE1A"})
|
||||
check('class', "template<typename...> {key}A", {2: "IDpE1A"})
|
||||
check('class', "template<typename = Test> {key}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> A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> typename ...T> A", {2: "II0EDpE1A"})
|
||||
check('class', "template<template<typename> typename...> A", {2: "II0EDpE1A"})
|
||||
check('class', "template<template<typename> typename T> {key}A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> typename ...T> {key}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 T> A", {2: "I_iE1A"})
|
||||
check('class', "template<int... T> A", {2: "I_DpiE1A"})
|
||||
check('class', "template<int T = 42> A", {2: "I_iE1A"})
|
||||
check('class', "template<int = 42> A", {2: "I_iE1A"})
|
||||
check('class', "template<int> {key}A", {2: "I_iE1A"})
|
||||
check('class', "template<int T> {key}A", {2: "I_iE1A"})
|
||||
check('class', "template<int... T> {key}A", {2: "I_DpiE1A"})
|
||||
check('class', "template<int T = 42> {key}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
|
||||
check('function',
|
||||
@ -747,21 +778,21 @@ def test_templates():
|
||||
parse('enum', 'abc::ns::foo{id_0, id_1, id_2} A')
|
||||
with pytest.raises(DefinitionError):
|
||||
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'})
|
||||
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'})
|
||||
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'})
|
||||
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'})
|
||||
|
||||
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',
|
||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
||||
check('type', 'abc::ns::foo{id_0, id_1, ...id_2} xyz::bar = ghi::qux',
|
||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
||||
check('type', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar = ghi::qux',
|
||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}, key='using')
|
||||
check('type', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar = ghi::qux',
|
||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}, key='using')
|
||||
check('function', 'abc::ns::foo{id_0, id_1, id_2} void xyz::bar()',
|
||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEv',
|
||||
4: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barEvv'})
|
||||
@ -772,8 +803,8 @@ def test_templates():
|
||||
{2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'})
|
||||
check('member', 'abc::ns::foo{id_0, id_1, ...id_2} ghi::qux xyz::bar',
|
||||
{2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'})
|
||||
check('concept', 'Iterator{T, U} Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
|
||||
check('concept', 'template<typename ...Pack> Numerics = (... && Numeric<Pack>)',
|
||||
check('concept', 'Iterator{{T, U}} {key}Another', {2: 'I00EX8IteratorI1T1UEE7Another'})
|
||||
check('concept', 'template<typename ...Pack> {key}Numerics = (... && Numeric<Pack>)',
|
||||
{2: 'IDpE8Numerics'})
|
||||
|
||||
# explicit specializations of members
|
||||
@ -785,7 +816,7 @@ def test_templates():
|
||||
output='template<> template<> int A<int>::B<int>::b') # same as above
|
||||
|
||||
# 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():
|
||||
@ -797,9 +828,10 @@ def test_template_args():
|
||||
3: "I0E5allowP1FN4funcI1F1BXne1GL1EEE4typeE",
|
||||
4: "I0E5allowvP1FN4funcI1F1BXne1GL1EEE4typeE"})
|
||||
# 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>",
|
||||
{2: "I0E21enable_if_not_array_t"})
|
||||
{2: "I0E21enable_if_not_array_t"},
|
||||
key='using')
|
||||
|
||||
|
||||
def test_initializers():
|
||||
|
@ -40,22 +40,22 @@ def parse(sig):
|
||||
|
||||
def test_function_signatures():
|
||||
rv = parse('func(a=1) -> int object')
|
||||
assert rv == 'a=1'
|
||||
assert rv == '(a=1)'
|
||||
|
||||
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])')
|
||||
assert rv == 'a=1, [b=None]'
|
||||
assert rv == '(a=1, [b=None])'
|
||||
|
||||
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])')
|
||||
assert rv == 'a=[], [b=None]'
|
||||
assert rv == '(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')
|
||||
@ -453,8 +453,8 @@ def test_pyobject_prefix(app):
|
||||
desc,
|
||||
addnodes.index,
|
||||
desc)])]))
|
||||
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][1].astext().strip() == 'say()' # prefix is stripped
|
||||
assert doctree[1][1][3].astext().strip() == 'FooBar.say()' # not stripped
|
||||
|
||||
|
||||
def test_pydata(app):
|
||||
|
@ -8,6 +8,9 @@
|
||||
:license: BSD, see LICENSE for details.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sphinx.errors import ExtensionError
|
||||
from sphinx.events import EventManager
|
||||
|
||||
|
||||
@ -22,3 +25,19 @@ def test_event_priority():
|
||||
|
||||
events.emit('builder-inited')
|
||||
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 = '',
|
||||
deprecated = False,
|
||||
members = [],
|
||||
member_order = 'alphabetic',
|
||||
member_order = 'alphabetical',
|
||||
exclude_members = set(),
|
||||
ignore_module_all = False,
|
||||
)
|
||||
@ -142,6 +142,7 @@ def test_format_signature(app):
|
||||
inst = app.registry.documenters[objtype](directive, name)
|
||||
inst.fullname = name
|
||||
inst.doc_as_attr = False # for class objtype
|
||||
inst.parent = object # dummy
|
||||
inst.object = obj
|
||||
inst.objpath = [name]
|
||||
inst.args = args
|
||||
@ -271,6 +272,7 @@ def test_get_doc(app):
|
||||
|
||||
def getdocl(objtype, obj):
|
||||
inst = app.registry.documenters[objtype](directive, 'tmp')
|
||||
inst.parent = object # dummy
|
||||
inst.object = obj
|
||||
inst.objpath = [obj.__name__]
|
||||
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')
|
||||
def test_automethod_for_builtin(app):
|
||||
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')
|
||||
def test_abstractmethods(app):
|
||||
options = {"members": None,
|
||||
@ -1435,7 +1459,7 @@ def test_coroutine(app):
|
||||
actual = do_autodoc(app, 'function', 'target.coroutine.sync_func')
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: sync_func()',
|
||||
'.. py:function:: sync_func(*args, **kwargs)',
|
||||
' :module: target.coroutine',
|
||||
'',
|
||||
]
|
||||
@ -1655,8 +1679,8 @@ def test_singledispatch(app):
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: func(arg, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatch',
|
||||
'',
|
||||
' A function for general use.',
|
||||
@ -1671,8 +1695,8 @@ def test_singledispatch_autofunction(app):
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:function:: func(arg, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' func(arg: int, kwarg=None)',
|
||||
' func(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatch',
|
||||
'',
|
||||
' A function for general use.',
|
||||
@ -1698,8 +1722,8 @@ def test_singledispatchmethod(app):
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Foo.meth(arg, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatchmethod',
|
||||
'',
|
||||
' A method for general use.',
|
||||
@ -1716,8 +1740,8 @@ def test_singledispatchmethod_automethod(app):
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:method:: Foo.meth(arg, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' Foo.meth(arg: int, kwarg=None)',
|
||||
' Foo.meth(arg: str, kwarg=None)',
|
||||
' :module: target.singledispatchmethod',
|
||||
'',
|
||||
' A method for general use.',
|
||||
@ -1795,7 +1819,7 @@ def test_autodoc(app, status, warning):
|
||||
|
||||
content = app.env.get_doctree('index')
|
||||
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.*'
|
||||
|
||||
# issue sphinx-doc/sphinx#2437
|
||||
|
@ -19,7 +19,10 @@ from sphinx import addnodes
|
||||
from sphinx.ext.autosummary import (
|
||||
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.util.docutils import new_document
|
||||
from sphinx.util.osutil import cd
|
||||
@ -189,6 +192,83 @@ def test_escaping(app, status, warning):
|
||||
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')
|
||||
def test_autosummary_generate(app, status, warning):
|
||||
app.builder.build_all()
|
||||
@ -209,11 +289,12 @@ def test_autosummary_generate(app, status, warning):
|
||||
nodes.row)])])
|
||||
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][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][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()
|
||||
assert (' .. autosummary::\n'
|
||||
@ -238,6 +319,11 @@ def test_autosummary_generate(app, status, warning):
|
||||
'\n'
|
||||
'.. 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',
|
||||
confoverrides={'autosummary_generate_overwrite': False})
|
||||
@ -397,7 +483,7 @@ def test_autosummary_template(app):
|
||||
confoverrides={'autosummary_generate': []})
|
||||
def test_empty_autosummary_generate(app, status, warning):
|
||||
app.build()
|
||||
assert ("WARNING: autosummary: stub file not found 'autosummary_importfail'"
|
||||
assert ("WARNING: autosummary: failed to import autosummary_importfail"
|
||||
in warning.getvalue())
|
||||
|
||||
|
||||
|
@ -74,7 +74,7 @@ def test_js_source(app, status, warning):
|
||||
|
||||
app.builder.build(['contents'])
|
||||
|
||||
v = '3.4.1'
|
||||
v = '3.5.1'
|
||||
msg = 'jquery.js version does not match to {v}'.format(v=v)
|
||||
jquery_min = (app.outdir / '_static' / 'jquery.js').read_text()
|
||||
assert 'jQuery v{v}'.format(v=v) in jquery_min, msg
|
||||
|
@ -97,7 +97,7 @@ def test_signature_methods():
|
||||
|
||||
# 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():
|
||||
|
Loading…
Reference in New Issue
Block a user