mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x'
This commit is contained in:
commit
90fb44ee43
39
CHANGES
39
CHANGES
@ -29,8 +29,29 @@ Bugs fixed
|
||||
Testing
|
||||
--------
|
||||
|
||||
Release 3.0.0 (in development)
|
||||
==============================
|
||||
Release 3.0.0 beta2 (in development)
|
||||
====================================
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Incompatible changes
|
||||
--------------------
|
||||
|
||||
Deprecated
|
||||
----------
|
||||
|
||||
Features added
|
||||
--------------
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
Release 3.0.0 beta1 (released Mar 23, 2020)
|
||||
===========================================
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
@ -85,6 +106,7 @@ Deprecated
|
||||
* ``sphinx.domains.std.StandardDomain.add_object()``
|
||||
* ``sphinx.domains.python.PyDecoratorMixin``
|
||||
* ``sphinx.ext.autodoc.get_documenters()``
|
||||
* ``sphinx.ext.autosummary.process_autosummary_toc()``
|
||||
* ``sphinx.parsers.Parser.app``
|
||||
* ``sphinx.testing.path.Path.text()``
|
||||
* ``sphinx.testing.path.Path.bytes()``
|
||||
@ -112,6 +134,9 @@ Features added
|
||||
* #6895: py domain: Do not emit nitpicky warnings for built-in types
|
||||
* py domain: Support lambda functions in function signature
|
||||
* #6417: py domain: Allow to make a style for arguments of functions and methods
|
||||
* #7238, #7239: py domain: Emit a warning on describing a python object if the
|
||||
entry is already added as the same name
|
||||
* #7341: py domain: type annotations in singature are converted to cross refs
|
||||
* Support priority of event handlers. For more detail, see
|
||||
:py:meth:`.Sphinx.connect()`
|
||||
* #3077: Implement the scoping for :rst:dir:`productionlist` as indicated
|
||||
@ -143,6 +168,8 @@ Features added
|
||||
|
||||
* Added ``SphinxDirective.get_source_info()``
|
||||
and ``SphinxRole.get_source_info()``.
|
||||
* #7324: sphinx-build: Emit a warning if multiple files having different file
|
||||
extensions for same document found
|
||||
|
||||
Bugs fixed
|
||||
----------
|
||||
@ -157,9 +184,11 @@ Bugs fixed
|
||||
* #7267: autodoc: error message for invalid directive options has wrong location
|
||||
* #7329: autodoc: info-field-list is wrongly generated from type hints into the
|
||||
class description even if ``autoclass_content='class'`` set
|
||||
* #7331: autodoc: a cython-function is not recognized as a function
|
||||
* #5637: inheritance_diagram: Incorrect handling of nested class names
|
||||
* #7139: ``code-block:: guess`` does not work
|
||||
* #7325: html: source_suffix containing dot leads to wrong source link
|
||||
* #7357: html: Resizing SVG image fails with ValueError
|
||||
* #7278: html search: Fix use of ``html_file_suffix`` instead of
|
||||
``html_link_suffix`` in search results
|
||||
* #7297: html theme: ``bizstyle`` does not support ``sidebarwidth``
|
||||
@ -170,9 +199,7 @@ Bugs fixed
|
||||
* #2377: C, parse function pointers even in complex types.
|
||||
* #7345: sphinx-build: Sphinx crashes if output directory exists as a file
|
||||
* #7290: sphinx-build: Ignore bdb.BdbQuit when handling exceptions
|
||||
|
||||
Testing
|
||||
--------
|
||||
* #6240: napoleon: Attributes and Methods sections ignore :noindex: option
|
||||
|
||||
Release 2.4.5 (in development)
|
||||
==============================
|
||||
@ -192,6 +219,8 @@ Features added
|
||||
Bugs fixed
|
||||
----------
|
||||
|
||||
* #7343: Sphinx builds has been slower since 2.4.0 on debug mode
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
||||
|
@ -81,6 +81,11 @@ The following is a list of deprecated interfaces.
|
||||
- 5.0
|
||||
- ``sphinx.registry.documenters``
|
||||
|
||||
* - ``sphinx.ext.autosummary.process_autosummary_toc()``
|
||||
- 3.0
|
||||
- 5.0
|
||||
- N/A
|
||||
|
||||
* - ``sphinx.parsers.Parser.app``
|
||||
- 3.0
|
||||
- 5.0
|
||||
|
@ -48,6 +48,12 @@ tables of contents. The ``toctree`` directive is the central element.
|
||||
to the source directory. A numeric ``maxdepth`` option may be given to
|
||||
indicate the depth of the tree; by default, all levels are included. [#]_
|
||||
|
||||
The representation of "TOC tree" is changed in each output format. The
|
||||
builders that output multiple files (ex. HTML) treat it as a collection of
|
||||
hyperlinks. On the other hand, the builders that output a single file (ex.
|
||||
LaTeX, man page, etc.) replace it with the content of the documents on the
|
||||
TOC tree.
|
||||
|
||||
Consider this example (taken from the Python docs' library reference index)::
|
||||
|
||||
.. toctree::
|
||||
|
1
setup.py
1
setup.py
@ -52,6 +52,7 @@ extras_require = {
|
||||
'pytest-cov',
|
||||
'html5lib',
|
||||
'typed_ast', # for py35-37
|
||||
'cython',
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,11 @@
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import bdb
|
||||
import locale
|
||||
import multiprocessing
|
||||
import os
|
||||
import pdb
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Any, IO, List
|
||||
@ -29,13 +31,10 @@ from sphinx.util.docutils import docutils_namespace, patch_docutils
|
||||
|
||||
|
||||
def handle_exception(app: Sphinx, args: Any, exception: BaseException, stderr: IO = sys.stderr) -> None: # NOQA
|
||||
import bdb
|
||||
|
||||
if isinstance(exception, bdb.BdbQuit):
|
||||
return
|
||||
|
||||
if args.pdb:
|
||||
import pdb
|
||||
print(red(__('Exception occurred while building, starting debugger:')),
|
||||
file=stderr)
|
||||
traceback.print_exc()
|
||||
|
@ -30,6 +30,7 @@ from sphinx.directives import ObjectDescription
|
||||
from sphinx.domains import Domain, ObjType, Index, IndexEntry
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.locale import _, __
|
||||
from sphinx.pycode.ast import ast, parse as ast_parse
|
||||
from sphinx.roles import XRefRole
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docfields import Field, GroupedField, TypedField
|
||||
@ -63,6 +64,58 @@ pairindextypes = {
|
||||
}
|
||||
|
||||
|
||||
def _parse_annotation(annotation: str) -> List[Node]:
|
||||
"""Parse type annotation."""
|
||||
def make_xref(text: str) -> addnodes.pending_xref:
|
||||
return pending_xref('', nodes.Text(text),
|
||||
refdomain='py', reftype='class', reftarget=text)
|
||||
|
||||
def unparse(node: ast.AST) -> List[Node]:
|
||||
if isinstance(node, ast.Attribute):
|
||||
return [nodes.Text("%s.%s" % (unparse(node.value)[0], node.attr))]
|
||||
elif isinstance(node, ast.Expr):
|
||||
return unparse(node.value)
|
||||
elif isinstance(node, ast.Index):
|
||||
return unparse(node.value)
|
||||
elif isinstance(node, ast.List):
|
||||
result = [addnodes.desc_sig_punctuation('', '[')] # type: List[Node]
|
||||
for elem in node.elts:
|
||||
result.extend(unparse(elem))
|
||||
result.append(addnodes.desc_sig_punctuation('', ', '))
|
||||
result.pop()
|
||||
result.append(addnodes.desc_sig_punctuation('', ']'))
|
||||
return result
|
||||
elif isinstance(node, ast.Module):
|
||||
return sum((unparse(e) for e in node.body), [])
|
||||
elif isinstance(node, ast.Name):
|
||||
return [nodes.Text(node.id)]
|
||||
elif isinstance(node, ast.Subscript):
|
||||
result = unparse(node.value)
|
||||
result.append(addnodes.desc_sig_punctuation('', '['))
|
||||
result.extend(unparse(node.slice))
|
||||
result.append(addnodes.desc_sig_punctuation('', ']'))
|
||||
return result
|
||||
elif isinstance(node, ast.Tuple):
|
||||
result = []
|
||||
for elem in node.elts:
|
||||
result.extend(unparse(elem))
|
||||
result.append(addnodes.desc_sig_punctuation('', ', '))
|
||||
result.pop()
|
||||
return result
|
||||
else:
|
||||
raise SyntaxError # unsupported syntax
|
||||
|
||||
try:
|
||||
tree = ast_parse(annotation)
|
||||
result = unparse(tree)
|
||||
for i, node in enumerate(result):
|
||||
if isinstance(node, nodes.Text):
|
||||
result[i] = make_xref(str(node))
|
||||
return result
|
||||
except SyntaxError:
|
||||
return [make_xref(annotation)]
|
||||
|
||||
|
||||
def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||
"""Parse a list of arguments using AST parser"""
|
||||
params = addnodes.desc_parameterlist(arglist)
|
||||
@ -89,9 +142,10 @@ def _parse_arglist(arglist: str) -> addnodes.desc_parameterlist:
|
||||
node += addnodes.desc_sig_name('', param.name)
|
||||
|
||||
if param.annotation is not param.empty:
|
||||
children = _parse_annotation(param.annotation)
|
||||
node += addnodes.desc_sig_punctuation('', ':')
|
||||
node += nodes.Text(' ')
|
||||
node += addnodes.desc_sig_name('', param.annotation)
|
||||
node += addnodes.desc_sig_name('', '', *children) # type: ignore
|
||||
if param.default is not param.empty:
|
||||
if param.annotation is not param.empty:
|
||||
node += nodes.Text(' ')
|
||||
@ -350,7 +404,8 @@ class PyObject(ObjectDescription):
|
||||
signode += addnodes.desc_parameterlist()
|
||||
|
||||
if retann:
|
||||
signode += addnodes.desc_returns(retann, retann)
|
||||
children = _parse_annotation(retann)
|
||||
signode += addnodes.desc_returns(retann, '', *children)
|
||||
|
||||
anno = self.options.get('annotation')
|
||||
if anno:
|
||||
@ -366,7 +421,7 @@ class PyObject(ObjectDescription):
|
||||
signode: desc_signature) -> None:
|
||||
modname = self.options.get('module', self.env.ref_context.get('py:module'))
|
||||
fullname = (modname + '.' if modname else '') + name_cls[0]
|
||||
node_id = make_id(self.env, self.state.document, modname or '', name_cls[0])
|
||||
node_id = make_id(self.env, self.state.document, '', fullname)
|
||||
signode['ids'].append(node_id)
|
||||
|
||||
# Assign old styled node_id(fullname) not to break old hyperlinks (if possible)
|
||||
|
@ -269,7 +269,10 @@ class Documenter:
|
||||
|
||||
def add_line(self, line: str, source: str, *lineno: int) -> None:
|
||||
"""Append one line of generated reST to the output."""
|
||||
if line.strip(): # not a blank line
|
||||
self.directive.result.append(self.indent + line, source, *lineno)
|
||||
else:
|
||||
self.directive.result.append('', source, *lineno)
|
||||
|
||||
def resolve_name(self, modname: str, parents: Any, path: str, base: Any
|
||||
) -> Tuple[str, List[str]]:
|
||||
@ -1007,7 +1010,8 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
if ((inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object)) and
|
||||
not inspect.is_cython_function_or_method(self.object)):
|
||||
# cannot introspect arguments of a C function or method
|
||||
return None
|
||||
try:
|
||||
@ -1426,7 +1430,8 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
||||
if self.env.config.autodoc_typehints == 'none':
|
||||
kwargs.setdefault('show_annotation', False)
|
||||
|
||||
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
|
||||
if ((inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object)) and
|
||||
not inspect.is_cython_function_or_method(self.object)):
|
||||
# can never get arguments of a C function or method
|
||||
return None
|
||||
if inspect.isstaticmethod(self.object, cls=self.parent, name=self.object_name):
|
||||
|
@ -72,7 +72,7 @@ from docutils.statemachine import StringList
|
||||
import sphinx
|
||||
from sphinx import addnodes
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
||||
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||
from sphinx.environment import BuildEnvironment
|
||||
from sphinx.environment.adapters.toctree import TocTree
|
||||
from sphinx.ext.autodoc import Documenter
|
||||
@ -106,6 +106,8 @@ def process_autosummary_toc(app: Sphinx, doctree: nodes.document) -> None:
|
||||
"""Insert items described in autosummary:: to the TOC tree, but do
|
||||
not generate the toctree:: list.
|
||||
"""
|
||||
warnings.warn('process_autosummary_toc() is deprecated',
|
||||
RemovedInSphinx50Warning, stacklevel=2)
|
||||
env = app.builder.env
|
||||
crawled = {}
|
||||
|
||||
@ -762,7 +764,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
||||
texinfo=(autosummary_noop, autosummary_noop))
|
||||
app.add_directive('autosummary', Autosummary)
|
||||
app.add_role('autolink', AutoLink())
|
||||
app.connect('doctree-read', process_autosummary_toc)
|
||||
app.connect('builder-inited', process_generate_options)
|
||||
app.add_config_value('autosummary_generate', [], True, [bool])
|
||||
app.add_config_value('autosummary_generate_overwrite', True, False)
|
||||
|
@ -579,7 +579,11 @@ class GoogleDocstring:
|
||||
if _type:
|
||||
lines.append(':vartype %s: %s' % (_name, _type))
|
||||
else:
|
||||
lines.extend(['.. attribute:: ' + _name, ''])
|
||||
lines.append('.. attribute:: ' + _name)
|
||||
if self._opt and 'noindex' in self._opt:
|
||||
lines.append(' :noindex:')
|
||||
lines.append('')
|
||||
|
||||
fields = self._format_field('', '', _desc)
|
||||
lines.extend(self._indent(fields, 3))
|
||||
if _type:
|
||||
@ -637,6 +641,8 @@ class GoogleDocstring:
|
||||
lines = [] # type: List[str]
|
||||
for _name, _type, _desc in self._consume_fields(parse_type=False):
|
||||
lines.append('.. method:: %s' % _name)
|
||||
if self._opt and 'noindex' in self._opt:
|
||||
lines.append(' :noindex:')
|
||||
if _desc:
|
||||
lines.extend([''] + self._indent(_desc, 3))
|
||||
lines.append('')
|
||||
|
@ -9,6 +9,7 @@
|
||||
"""
|
||||
|
||||
import os
|
||||
from glob import glob
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sphinx.locale import __
|
||||
@ -55,7 +56,13 @@ class Project:
|
||||
for filename in get_matching_files(self.srcdir, excludes): # type: ignore
|
||||
docname = self.path2doc(filename)
|
||||
if docname:
|
||||
if os.access(os.path.join(self.srcdir, filename), os.R_OK):
|
||||
if docname in self.docnames:
|
||||
pattern = os.path.join(self.srcdir, docname) + '.*'
|
||||
files = [relpath(f, self.srcdir) for f in glob(pattern)]
|
||||
logger.warning(__('multiple files found for the document "%s": %r\n'
|
||||
'Use %r for the build.'),
|
||||
docname, files, self.doc2path(docname), once=True)
|
||||
elif os.access(os.path.join(self.srcdir, filename), os.R_OK):
|
||||
self.docnames.add(docname)
|
||||
else:
|
||||
logger.warning(__("document not readable. Ignored."), location=docname)
|
||||
|
@ -63,7 +63,7 @@ def assert_node(node: Node, cls: Any = None, xpath: str = "", **kwargs: Any) ->
|
||||
'The node%s has %d child nodes, not one' % (xpath, len(node))
|
||||
assert_node(node[0], cls[1:], xpath=xpath + "[0]", **kwargs)
|
||||
elif isinstance(cls, tuple):
|
||||
assert isinstance(node, nodes.Element), \
|
||||
assert isinstance(node, (list, nodes.Element)), \
|
||||
'The node%s does not have any items' % xpath
|
||||
assert len(node) == len(cls), \
|
||||
'The node%s has %d child nodes, not %r' % (xpath, len(node), len(cls))
|
||||
|
@ -459,8 +459,6 @@ class SphinxTranslator(nodes.NodeVisitor):
|
||||
for node_class in node.__class__.__mro__:
|
||||
method = getattr(self, 'visit_%s' % (node_class.__name__), None)
|
||||
if method:
|
||||
logger.debug('SphinxTranslator.dispatch_visit calling %s for %s',
|
||||
method.__name__, node)
|
||||
method(node)
|
||||
break
|
||||
else:
|
||||
@ -478,8 +476,6 @@ class SphinxTranslator(nodes.NodeVisitor):
|
||||
for node_class in node.__class__.__mro__:
|
||||
method = getattr(self, 'depart_%s' % (node_class.__name__), None)
|
||||
if method:
|
||||
logger.debug('SphinxTranslator.dispatch_departure calling %s for %s',
|
||||
method.__name__, node)
|
||||
method(node)
|
||||
break
|
||||
else:
|
||||
|
@ -197,6 +197,14 @@ def isabstractmethod(obj: Any) -> bool:
|
||||
return safe_getattr(obj, '__isabstractmethod__', False) is True
|
||||
|
||||
|
||||
def is_cython_function_or_method(obj: Any) -> bool:
|
||||
"""Check if the object is a function or method in cython."""
|
||||
try:
|
||||
return obj.__class__.__name__ == 'cython_function_or_method'
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
|
||||
def isattributedescriptor(obj: Any) -> bool:
|
||||
"""Check if the object is an attribute like descriptor."""
|
||||
if inspect.isdatadescriptor(object):
|
||||
@ -207,6 +215,9 @@ def isattributedescriptor(obj: Any) -> bool:
|
||||
if isfunction(obj) or isbuiltin(obj) or inspect.ismethod(obj):
|
||||
# attribute must not be either function, builtin and method
|
||||
return False
|
||||
elif is_cython_function_or_method(obj):
|
||||
# attribute must not be either function and method (for cython)
|
||||
return False
|
||||
elif inspect.isclass(obj):
|
||||
# attribute must not be a class
|
||||
return False
|
||||
|
@ -117,6 +117,7 @@ class SphinxWarningLogRecord(SphinxLogRecord):
|
||||
|
||||
class SphinxLoggerAdapter(logging.LoggerAdapter):
|
||||
"""LoggerAdapter allowing ``type`` and ``subtype`` keywords."""
|
||||
KEYWORDS = ['type', 'subtype', 'location', 'nonl', 'color', 'once']
|
||||
|
||||
def log(self, level: Union[int, str], msg: str, *args: Any, **kwargs: Any) -> None:
|
||||
if isinstance(level, int):
|
||||
@ -130,16 +131,9 @@ class SphinxLoggerAdapter(logging.LoggerAdapter):
|
||||
|
||||
def process(self, msg: str, kwargs: Dict) -> Tuple[str, Dict]: # type: ignore
|
||||
extra = kwargs.setdefault('extra', {})
|
||||
if 'type' in kwargs:
|
||||
extra['type'] = kwargs.pop('type')
|
||||
if 'subtype' in kwargs:
|
||||
extra['subtype'] = kwargs.pop('subtype')
|
||||
if 'location' in kwargs:
|
||||
extra['location'] = kwargs.pop('location')
|
||||
if 'nonl' in kwargs:
|
||||
extra['nonl'] = kwargs.pop('nonl')
|
||||
if 'color' in kwargs:
|
||||
extra['color'] = kwargs.pop('color')
|
||||
for keyword in self.KEYWORDS:
|
||||
if keyword in kwargs:
|
||||
extra[keyword] = kwargs.pop(keyword)
|
||||
|
||||
return msg, kwargs
|
||||
|
||||
@ -445,6 +439,26 @@ class MessagePrefixFilter(logging.Filter):
|
||||
return True
|
||||
|
||||
|
||||
class OnceFilter(logging.Filter):
|
||||
"""Show the message only once."""
|
||||
|
||||
def __init__(self, name: str = '') -> None:
|
||||
super().__init__(name)
|
||||
self.messages = {} # type: Dict[str, List]
|
||||
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
once = getattr(record, 'once', '')
|
||||
if not once:
|
||||
return True
|
||||
else:
|
||||
params = self.messages.setdefault(record.msg, [])
|
||||
if record.args in params:
|
||||
return False
|
||||
|
||||
params.append(record.args)
|
||||
return True
|
||||
|
||||
|
||||
class SphinxLogRecordTranslator(logging.Filter):
|
||||
"""Converts a log record to one Sphinx expects
|
||||
|
||||
@ -562,6 +576,7 @@ def setup(app: "Sphinx", status: IO, warning: IO) -> None:
|
||||
warning_handler.addFilter(WarningSuppressor(app))
|
||||
warning_handler.addFilter(WarningLogRecordTranslator(app))
|
||||
warning_handler.addFilter(WarningIsErrorFilter(app))
|
||||
warning_handler.addFilter(OnceFilter())
|
||||
warning_handler.setLevel(logging.WARNING)
|
||||
warning_handler.setFormatter(ColorizeFormatter())
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
"""
|
||||
|
||||
import re
|
||||
import unicodedata
|
||||
import warnings
|
||||
from typing import Any, Callable, Iterable, List, Set, Tuple, Type
|
||||
from typing import TYPE_CHECKING, cast
|
||||
@ -434,6 +435,79 @@ def inline_all_toctrees(builder: "Builder", docnameset: Set[str], docname: str,
|
||||
return tree
|
||||
|
||||
|
||||
def _make_id(string: str) -> str:
|
||||
"""Convert `string` into an identifier and return it.
|
||||
|
||||
This function is a modified version of ``docutils.nodes.make_id()`` of
|
||||
docutils-0.16.
|
||||
|
||||
Changes:
|
||||
|
||||
* Allow to use dots (".") and underscores ("_") for an identifier
|
||||
without a leading character.
|
||||
|
||||
# Author: David Goodger <goodger@python.org>
|
||||
# Maintainer: docutils-develop@lists.sourceforge.net
|
||||
# Copyright: This module has been placed in the public domain.
|
||||
"""
|
||||
id = string.lower()
|
||||
id = id.translate(_non_id_translate_digraphs)
|
||||
id = id.translate(_non_id_translate)
|
||||
# get rid of non-ascii characters.
|
||||
# 'ascii' lowercase to prevent problems with turkish locale.
|
||||
id = unicodedata.normalize('NFKD', id).encode('ascii', 'ignore').decode('ascii')
|
||||
# shrink runs of whitespace and replace by hyphen
|
||||
id = _non_id_chars.sub('-', ' '.join(id.split()))
|
||||
id = _non_id_at_ends.sub('', id)
|
||||
return str(id)
|
||||
|
||||
|
||||
_non_id_chars = re.compile('[^a-z0-9._]+')
|
||||
_non_id_at_ends = re.compile('^[-0-9._]+|-+$')
|
||||
_non_id_translate = {
|
||||
0x00f8: u'o', # o with stroke
|
||||
0x0111: u'd', # d with stroke
|
||||
0x0127: u'h', # h with stroke
|
||||
0x0131: u'i', # dotless i
|
||||
0x0142: u'l', # l with stroke
|
||||
0x0167: u't', # t with stroke
|
||||
0x0180: u'b', # b with stroke
|
||||
0x0183: u'b', # b with topbar
|
||||
0x0188: u'c', # c with hook
|
||||
0x018c: u'd', # d with topbar
|
||||
0x0192: u'f', # f with hook
|
||||
0x0199: u'k', # k with hook
|
||||
0x019a: u'l', # l with bar
|
||||
0x019e: u'n', # n with long right leg
|
||||
0x01a5: u'p', # p with hook
|
||||
0x01ab: u't', # t with palatal hook
|
||||
0x01ad: u't', # t with hook
|
||||
0x01b4: u'y', # y with hook
|
||||
0x01b6: u'z', # z with stroke
|
||||
0x01e5: u'g', # g with stroke
|
||||
0x0225: u'z', # z with hook
|
||||
0x0234: u'l', # l with curl
|
||||
0x0235: u'n', # n with curl
|
||||
0x0236: u't', # t with curl
|
||||
0x0237: u'j', # dotless j
|
||||
0x023c: u'c', # c with stroke
|
||||
0x023f: u's', # s with swash tail
|
||||
0x0240: u'z', # z with swash tail
|
||||
0x0247: u'e', # e with stroke
|
||||
0x0249: u'j', # j with stroke
|
||||
0x024b: u'q', # q with hook tail
|
||||
0x024d: u'r', # r with stroke
|
||||
0x024f: u'y', # y with stroke
|
||||
}
|
||||
_non_id_translate_digraphs = {
|
||||
0x00df: u'sz', # ligature sz
|
||||
0x00e6: u'ae', # ae
|
||||
0x0153: u'oe', # ligature oe
|
||||
0x0238: u'db', # db digraph
|
||||
0x0239: u'qp', # qp digraph
|
||||
}
|
||||
|
||||
|
||||
def make_id(env: "BuildEnvironment", document: nodes.document,
|
||||
prefix: str = '', term: str = None) -> str:
|
||||
"""Generate an appropriate node_id for given *prefix* and *term*."""
|
||||
@ -445,12 +519,12 @@ def make_id(env: "BuildEnvironment", document: nodes.document,
|
||||
|
||||
# try to generate node_id by *term*
|
||||
if prefix and term:
|
||||
node_id = nodes.make_id(idformat % term)
|
||||
node_id = _make_id(idformat % term)
|
||||
if node_id == prefix:
|
||||
# *term* is not good to generate a node_id.
|
||||
node_id = None
|
||||
elif term:
|
||||
node_id = nodes.make_id(term)
|
||||
node_id = _make_id(term)
|
||||
if node_id == '':
|
||||
node_id = None # fallback to None
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
import copy
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
import warnings
|
||||
from typing import Any, Iterable, Tuple
|
||||
from typing import TYPE_CHECKING, cast
|
||||
@ -37,6 +38,19 @@ logger = logging.getLogger(__name__)
|
||||
# http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
|
||||
|
||||
|
||||
def multiply_length(length: str, scale: int) -> str:
|
||||
"""Multiply *length* (width or height) by *scale*."""
|
||||
matched = re.match(r'^(\d*\.?\d*)\s*(\S*)$', length)
|
||||
if not matched:
|
||||
return length
|
||||
elif scale == 100:
|
||||
return length
|
||||
else:
|
||||
amount, unit = matched.groups()
|
||||
result = float(amount) * scale / 100
|
||||
return "%s%s" % (int(result), unit)
|
||||
|
||||
|
||||
class HTMLWriter(Writer):
|
||||
|
||||
# override embed-stylesheet default value to 0.
|
||||
@ -596,11 +610,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator):
|
||||
if 'height' in node:
|
||||
atts['height'] = node['height']
|
||||
if 'scale' in node:
|
||||
scale = node['scale'] / 100.0
|
||||
if 'width' in atts:
|
||||
atts['width'] = int(atts['width']) * scale
|
||||
atts['width'] = multiply_length(atts['width'], node['scale'])
|
||||
if 'height' in atts:
|
||||
atts['height'] = int(atts['height']) * scale
|
||||
atts['height'] = multiply_length(atts['height'], node['scale'])
|
||||
atts['alt'] = node.get('alt', uri)
|
||||
if 'align' in node:
|
||||
atts['class'] = 'align-%s' % node['align']
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
import warnings
|
||||
from typing import Any, Iterable, Tuple
|
||||
from typing import TYPE_CHECKING, cast
|
||||
@ -36,6 +37,19 @@ logger = logging.getLogger(__name__)
|
||||
# http://www.arnebrodowski.de/blog/write-your-own-restructuredtext-writer.html
|
||||
|
||||
|
||||
def multiply_length(length: str, scale: int) -> str:
|
||||
"""Multiply *length* (width or height) by *scale*."""
|
||||
matched = re.match(r'^(\d*\.?\d*)\s*(\S*)$', length)
|
||||
if not matched:
|
||||
return length
|
||||
elif scale == 100:
|
||||
return length
|
||||
else:
|
||||
amount, unit = matched.groups()
|
||||
result = float(amount) * scale / 100
|
||||
return "%s%s" % (int(result), unit)
|
||||
|
||||
|
||||
class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
"""
|
||||
Our custom HTML translator.
|
||||
@ -537,11 +551,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator):
|
||||
if 'height' in node:
|
||||
atts['height'] = node['height']
|
||||
if 'scale' in node:
|
||||
scale = node['scale'] / 100.0
|
||||
if 'width' in atts:
|
||||
atts['width'] = int(atts['width']) * scale
|
||||
atts['width'] = multiply_length(atts['width'], node['scale'])
|
||||
if 'height' in atts:
|
||||
atts['height'] = int(atts['height']) * scale
|
||||
atts['height'] = multiply_length(atts['height'], node['scale'])
|
||||
atts['alt'] = node.get('alt', uri)
|
||||
if 'align' in node:
|
||||
atts['class'] = 'align-%s' % node['align']
|
||||
|
12
tests/roots/test-ext-autodoc/target/cython.pyx
Normal file
12
tests/roots/test-ext-autodoc/target/cython.pyx
Normal file
@ -0,0 +1,12 @@
|
||||
# cython: binding=True
|
||||
|
||||
def foo(*args, **kwargs):
|
||||
"""Docstring."""
|
||||
|
||||
|
||||
class Class:
|
||||
"""Docstring."""
|
||||
|
||||
def meth(self, name: str, age: int = 0) -> None:
|
||||
"""Docstring."""
|
||||
pass
|
@ -22,6 +22,13 @@ from sphinx.testing.util import SphinxTestApp, Struct # NOQA
|
||||
from sphinx.util import logging
|
||||
from sphinx.util.docutils import LoggingReporter
|
||||
|
||||
try:
|
||||
# Enable pyximport to test cython module
|
||||
import pyximport
|
||||
pyximport.install()
|
||||
except ImportError:
|
||||
pyximport = None
|
||||
|
||||
app = None
|
||||
|
||||
|
||||
@ -361,7 +368,7 @@ def test_new_documenter(app):
|
||||
' :module: target',
|
||||
'',
|
||||
' documentation for the integer',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -420,7 +427,7 @@ def test_py_module(app, warning):
|
||||
' :module: target',
|
||||
'',
|
||||
' Function.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
assert ("don't know which module to import for autodocumenting 'Class.meth'"
|
||||
not in warning.getvalue())
|
||||
@ -435,7 +442,7 @@ def test_autodoc_decorator(app):
|
||||
' :module: target.decorator',
|
||||
'',
|
||||
' docstring for deco1',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
actual = do_autodoc(app, 'decorator', 'target.decorator.deco2')
|
||||
@ -445,7 +452,7 @@ def test_autodoc_decorator(app):
|
||||
' :module: target.decorator',
|
||||
'',
|
||||
' docstring for deco2',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -458,7 +465,7 @@ def test_autodoc_exception(app):
|
||||
' :module: target',
|
||||
'',
|
||||
' My custom exception.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -726,7 +733,7 @@ def test_autodoc_subclass_of_builtin_class(app):
|
||||
' :module: target',
|
||||
'',
|
||||
' Docstring.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -785,7 +792,7 @@ def test_autodoc_inner_class(app):
|
||||
' Bases: :class:`target.Outer.Inner`',
|
||||
'',
|
||||
' InnerChild docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -799,7 +806,7 @@ def test_autodoc_classmethod(app):
|
||||
' :classmethod:',
|
||||
'',
|
||||
' Inherited class method.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -813,7 +820,7 @@ def test_autodoc_staticmethod(app):
|
||||
' :staticmethod:',
|
||||
'',
|
||||
' Inherited static method.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -854,7 +861,7 @@ def test_autodoc_c_module(app):
|
||||
" Convert a time tuple to a string, e.g. 'Sat Jun 06 16:26:11 1998'.",
|
||||
' When the time tuple is not present, current time as returned by localtime()',
|
||||
' is used.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -946,7 +953,7 @@ def test_autodoc_module_scope(app):
|
||||
' :value: <_io.StringIO object>',
|
||||
'',
|
||||
' should be documented as well - süß',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -962,7 +969,7 @@ def test_autodoc_class_scope(app):
|
||||
' :value: <_io.StringIO object>',
|
||||
'',
|
||||
' should be documented as well - süß',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1190,7 +1197,7 @@ def test_descriptor_class(app):
|
||||
' :module: target.descriptor',
|
||||
'',
|
||||
' Descriptor class with custom metaclass docstring.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1203,7 +1210,7 @@ def test_autofunction_for_callable(app):
|
||||
' :module: target.callable',
|
||||
'',
|
||||
' A callable object that behaves like a function.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1216,7 +1223,7 @@ def test_autofunction_for_method(app):
|
||||
' :module: target.callable',
|
||||
'',
|
||||
' docstring of Callable.method().',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1265,7 +1272,7 @@ def test_abstractmethods():
|
||||
' :module: target.abstractmethods',
|
||||
' :abstractmethod:',
|
||||
' :staticmethod:',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1300,7 +1307,7 @@ def test_partialfunction():
|
||||
' :module: target.partialfunction',
|
||||
'',
|
||||
' docstring of func3',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1520,7 +1527,7 @@ def test_autodoc_typed_instance_variables(app):
|
||||
" :value: ''",
|
||||
'',
|
||||
' attr3',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1538,7 +1545,7 @@ def test_autodoc_Annotated(app):
|
||||
' :module: target.annotated',
|
||||
'',
|
||||
' docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1580,7 +1587,7 @@ def test_singledispatch():
|
||||
' :module: target.singledispatch',
|
||||
'',
|
||||
' A function for general use.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -1607,5 +1614,36 @@ def test_singledispatchmethod():
|
||||
' :module: target.singledispatchmethod',
|
||||
'',
|
||||
' A method for general use.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_test')
|
||||
@pytest.mark.skipif(pyximport is None, reason='cython is not installed')
|
||||
def test_cython():
|
||||
options = {"members": None,
|
||||
"undoc-members": None}
|
||||
actual = do_autodoc(app, 'module', 'target.cython', options)
|
||||
assert list(actual) == [
|
||||
'',
|
||||
'.. py:module:: target.cython',
|
||||
'',
|
||||
'',
|
||||
'.. py:class:: Class',
|
||||
' :module: target.cython',
|
||||
'',
|
||||
' Docstring.',
|
||||
'',
|
||||
'',
|
||||
' .. py:method:: Class.meth(name: str, age: int = 0) -> None',
|
||||
' :module: target.cython',
|
||||
'',
|
||||
' Docstring.',
|
||||
'',
|
||||
'',
|
||||
'.. py:function:: foo(*args, **kwargs)',
|
||||
' :module: target.cython',
|
||||
'',
|
||||
' Docstring.',
|
||||
'',
|
||||
]
|
||||
|
@ -318,13 +318,13 @@ def test_epub_anchor_id(app):
|
||||
app.build()
|
||||
|
||||
html = (app.outdir / 'index.xhtml').read_text()
|
||||
assert ('<p id="std-setting-staticfiles-finders">'
|
||||
assert ('<p id="std-setting-staticfiles_finders">'
|
||||
'<span id="std-setting-STATICFILES_FINDERS"></span>'
|
||||
'blah blah blah</p>' in html)
|
||||
assert ('<span id="std-setting-staticfiles-section"></span>'
|
||||
assert ('<span id="std-setting-staticfiles_section"></span>'
|
||||
'<span id="std-setting-STATICFILES_SECTION"></span>'
|
||||
'<h1>blah blah blah</h1>' in html)
|
||||
assert 'see <a class="reference internal" href="#std-setting-staticfiles-finders">' in html
|
||||
assert 'see <a class="reference internal" href="#std-setting-staticfiles_finders">' in html
|
||||
|
||||
|
||||
@pytest.mark.sphinx('epub', testroot='html_assets')
|
||||
|
@ -175,9 +175,9 @@ def test_html4_output(app, status, warning):
|
||||
r'-| |-'),
|
||||
],
|
||||
'autodoc.html': [
|
||||
(".//dl[@class='py class']/dt[@id='autodoc-target-class']", ''),
|
||||
(".//dl[@class='py function']/dt[@id='autodoc-target-function']/em/span", r'\*\*'),
|
||||
(".//dl[@class='py function']/dt[@id='autodoc-target-function']/em/span", r'kwds'),
|
||||
(".//dl[@class='py class']/dt[@id='autodoc_target.class']", ''),
|
||||
(".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span", r'\*\*'),
|
||||
(".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span", r'kwds'),
|
||||
(".//dd/p", r'Return spam\.'),
|
||||
],
|
||||
'extapi.html': [
|
||||
@ -222,7 +222,7 @@ def test_html4_output(app, status, warning):
|
||||
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
|
||||
(".//a[@href='#with']"
|
||||
"[@class='reference internal']/code/span[@class='pre']", '^with$'),
|
||||
(".//a[@href='#grammar-token-try-stmt']"
|
||||
(".//a[@href='#grammar-token-try_stmt']"
|
||||
"[@class='reference internal']/code/span", '^statement$'),
|
||||
(".//a[@href='#some-label'][@class='reference internal']/span", '^here$'),
|
||||
(".//a[@href='#some-label'][@class='reference internal']/span", '^there$'),
|
||||
@ -254,7 +254,7 @@ def test_html4_output(app, status, warning):
|
||||
(".//dl/dt[@id='term-boson']", 'boson'),
|
||||
# a production list
|
||||
(".//pre/strong", 'try_stmt'),
|
||||
(".//pre/a[@href='#grammar-token-try1-stmt']/code/span", 'try1_stmt'),
|
||||
(".//pre/a[@href='#grammar-token-try1_stmt']/code/span", 'try1_stmt'),
|
||||
# tests for ``only`` directive
|
||||
(".//p", 'A global substitution.'),
|
||||
(".//p", 'In HTML.'),
|
||||
@ -262,7 +262,7 @@ def test_html4_output(app, status, warning):
|
||||
(".//p", 'Always present'),
|
||||
# tests for ``any`` role
|
||||
(".//a[@href='#with']/span", 'headings'),
|
||||
(".//a[@href='objects.html#func-without-body']/code/span", 'objects'),
|
||||
(".//a[@href='objects.html#func_without_body']/code/span", 'objects'),
|
||||
# tests for numeric labels
|
||||
(".//a[@href='#id1'][@class='reference internal']/span", 'Testing various markup'),
|
||||
# tests for smartypants
|
||||
@ -274,18 +274,18 @@ def test_html4_output(app, status, warning):
|
||||
(".//p", 'Il dit : « C’est “super” ! »'),
|
||||
],
|
||||
'objects.html': [
|
||||
(".//dt[@id='mod-cls-meth1']", ''),
|
||||
(".//dt[@id='errmod-error']", ''),
|
||||
(".//dt[@id='mod.cls.meth1']", ''),
|
||||
(".//dt[@id='errmod.error']", ''),
|
||||
(".//dt/code", r'long\(parameter,\s* list\)'),
|
||||
(".//dt/code", 'another one'),
|
||||
(".//a[@href='#mod-cls'][@class='reference internal']", ''),
|
||||
(".//a[@href='#mod.cls'][@class='reference internal']", ''),
|
||||
(".//dl[@class='std userdesc']", ''),
|
||||
(".//dt[@id='userdesc-myobj']", ''),
|
||||
(".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
|
||||
# docfields
|
||||
(".//a[@class='reference internal'][@href='#timeint']/em", 'TimeInt'),
|
||||
(".//a[@class='reference internal'][@href='#time']", 'Time'),
|
||||
(".//a[@class='reference internal'][@href='#errmod-error']/strong", 'Error'),
|
||||
(".//a[@class='reference internal'][@href='#errmod.error']/strong", 'Error'),
|
||||
# C references
|
||||
(".//span[@class='pre']", 'CFunction()'),
|
||||
(".//a[@href='#c.Sphinx_DoSomething']", ''),
|
||||
@ -324,7 +324,7 @@ def test_html4_output(app, status, warning):
|
||||
'\\+p'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-objc']/code/span",
|
||||
'--ObjC\\+\\+'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-plugin-option']/code/span",
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-plugin.option']/code/span",
|
||||
'--plugin.option'),
|
||||
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-create-auth-token']"
|
||||
"/code/span",
|
||||
|
@ -123,25 +123,25 @@ def test_domain_js_find_obj(app, status, warning):
|
||||
('NestedParentA', ('roles', 'nestedparenta', 'class')))
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
|
||||
('NestedParentA.NestedChildA',
|
||||
('roles', 'nestedparenta-nestedchilda', 'class')))
|
||||
('roles', 'nestedparenta.nestedchilda', 'class')))
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
|
||||
('NestedParentA.NestedChildA',
|
||||
('roles', 'nestedparenta-nestedchilda', 'class')))
|
||||
('roles', 'nestedparenta.nestedchilda', 'class')))
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'func') ==
|
||||
('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'function')))
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'func') ==
|
||||
('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'function')))
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
|
||||
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'func') ==
|
||||
('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'function')))
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
|
||||
assert (find_obj('module_a.submodule', 'ModTopLevel', 'mod_child_2', 'meth') ==
|
||||
('module_a.submodule.ModTopLevel.mod_child_2',
|
||||
('module', 'module-a-submodule-modtoplevel-mod-child-2', 'method')))
|
||||
('module', 'module_a.submodule.modtoplevel.mod_child_2', 'method')))
|
||||
assert (find_obj('module_b.submodule', 'ModTopLevel', 'module_a.submodule', 'mod') ==
|
||||
('module_a.submodule',
|
||||
('module', 'module-module-a-submodule', 'module')))
|
||||
('module', 'module-module_a.submodule', 'module')))
|
||||
|
||||
|
||||
def test_get_full_qualified_name():
|
||||
|
@ -18,11 +18,11 @@ from sphinx import addnodes
|
||||
from sphinx.addnodes import (
|
||||
desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional,
|
||||
desc_parameter, desc_parameterlist, desc_returns, desc_signature,
|
||||
desc_sig_name, desc_sig_operator, desc_sig_punctuation,
|
||||
desc_sig_name, desc_sig_operator, desc_sig_punctuation, pending_xref,
|
||||
)
|
||||
from sphinx.domains import IndexEntry
|
||||
from sphinx.domains.python import (
|
||||
py_sig_re, _pseudo_parse_arglist, PythonDomain, PythonModuleIndex
|
||||
py_sig_re, _parse_annotation, _pseudo_parse_arglist, PythonDomain, PythonModuleIndex
|
||||
)
|
||||
from sphinx.testing import restructuredtext
|
||||
from sphinx.testing.util import assert_node
|
||||
@ -78,7 +78,7 @@ def test_domain_py_xrefs(app, status, warning):
|
||||
assert_node(node, **attributes)
|
||||
|
||||
doctree = app.env.get_doctree('roles')
|
||||
refnodes = list(doctree.traverse(addnodes.pending_xref))
|
||||
refnodes = list(doctree.traverse(pending_xref))
|
||||
assert_refnode(refnodes[0], None, None, 'TopLevel', 'class')
|
||||
assert_refnode(refnodes[1], None, None, 'top_level', 'meth')
|
||||
assert_refnode(refnodes[2], None, 'NestedParentA', 'child_1', 'meth')
|
||||
@ -96,7 +96,7 @@ def test_domain_py_xrefs(app, status, warning):
|
||||
assert len(refnodes) == 13
|
||||
|
||||
doctree = app.env.get_doctree('module')
|
||||
refnodes = list(doctree.traverse(addnodes.pending_xref))
|
||||
refnodes = list(doctree.traverse(pending_xref))
|
||||
assert_refnode(refnodes[0], 'module_a.submodule', None,
|
||||
'ModTopLevel', 'class')
|
||||
assert_refnode(refnodes[1], 'module_a.submodule', 'ModTopLevel',
|
||||
@ -125,7 +125,7 @@ def test_domain_py_xrefs(app, status, warning):
|
||||
assert len(refnodes) == 16
|
||||
|
||||
doctree = app.env.get_doctree('module_option')
|
||||
refnodes = list(doctree.traverse(addnodes.pending_xref))
|
||||
refnodes = list(doctree.traverse(pending_xref))
|
||||
print(refnodes)
|
||||
print(refnodes[0])
|
||||
print(refnodes[1])
|
||||
@ -171,11 +171,11 @@ def test_resolve_xref_for_properties(app, status, warning):
|
||||
app.builder.build_all()
|
||||
|
||||
content = (app.outdir / 'module.html').read_text()
|
||||
assert ('Link to <a class="reference internal" href="#module-a-submodule-modtoplevel-prop"'
|
||||
assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
|
||||
' title="module_a.submodule.ModTopLevel.prop">'
|
||||
'<code class="xref py py-attr docutils literal notranslate"><span class="pre">'
|
||||
'prop</span> <span class="pre">attribute</span></code></a>' in content)
|
||||
assert ('Link to <a class="reference internal" href="#module-a-submodule-modtoplevel-prop"'
|
||||
assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
|
||||
' title="module_a.submodule.ModTopLevel.prop">'
|
||||
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
|
||||
'prop</span> <span class="pre">method</span></code></a>' in content)
|
||||
@ -194,18 +194,18 @@ def test_domain_py_find_obj(app, status, warning):
|
||||
assert (find_obj(None, None, 'NestedParentA', 'class') ==
|
||||
[('NestedParentA', ('roles', 'nestedparenta', 'class'))])
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
|
||||
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta-nestedchilda', 'class'))])
|
||||
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
|
||||
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta-nestedchilda', 'class'))])
|
||||
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
|
||||
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'method'))])
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
|
||||
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'method'))])
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
|
||||
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'meth') ==
|
||||
[('NestedParentA.NestedChildA.subchild_1',
|
||||
('roles', 'nestedparenta-nestedchilda-subchild-1', 'method'))])
|
||||
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
|
||||
|
||||
|
||||
def test_get_full_qualified_name():
|
||||
@ -236,13 +236,44 @@ def test_get_full_qualified_name():
|
||||
assert domain.get_full_qualified_name(node) == 'module1.Class.func'
|
||||
|
||||
|
||||
def test_parse_annotation():
|
||||
doctree = _parse_annotation("int")
|
||||
assert_node(doctree, ([pending_xref, "int"],))
|
||||
|
||||
doctree = _parse_annotation("List[int]")
|
||||
assert_node(doctree, ([pending_xref, "List"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Tuple[int, int]")
|
||||
assert_node(doctree, ([pending_xref, "Tuple"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
doctree = _parse_annotation("Callable[[int, int], int]")
|
||||
assert_node(doctree, ([pending_xref, "Callable"],
|
||||
[desc_sig_punctuation, "["],
|
||||
[desc_sig_punctuation, "["],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"],
|
||||
[desc_sig_punctuation, ", "],
|
||||
[pending_xref, "int"],
|
||||
[desc_sig_punctuation, "]"]))
|
||||
|
||||
|
||||
def test_pyfunction_signature(app):
|
||||
text = ".. py:function:: hello(name: str) -> str"
|
||||
doctree = restructuredtext.parse(app, text)
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "hello"],
|
||||
desc_parameterlist,
|
||||
[desc_returns, "str"])],
|
||||
[desc_returns, pending_xref, "str"])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="function",
|
||||
domain="py", objtype="function", noindex=False)
|
||||
@ -250,7 +281,7 @@ def test_pyfunction_signature(app):
|
||||
[desc_parameterlist, desc_parameter, ([desc_sig_name, "name"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
[nodes.inline, "str"])])
|
||||
[nodes.inline, pending_xref, "str"])])
|
||||
|
||||
|
||||
def test_pyfunction_signature_full(app):
|
||||
@ -260,7 +291,7 @@ def test_pyfunction_signature_full(app):
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "hello"],
|
||||
desc_parameterlist,
|
||||
[desc_returns, "str"])],
|
||||
[desc_returns, pending_xref, "str"])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="function",
|
||||
domain="py", objtype="function", noindex=False)
|
||||
@ -268,7 +299,7 @@ def test_pyfunction_signature_full(app):
|
||||
[desc_parameterlist, ([desc_parameter, ([desc_sig_name, "a"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
[desc_sig_name, "str"])],
|
||||
[desc_sig_name, pending_xref, "str"])],
|
||||
[desc_parameter, ([desc_sig_name, "b"],
|
||||
[desc_sig_operator, "="],
|
||||
[nodes.inline, "1"])],
|
||||
@ -276,11 +307,11 @@ def test_pyfunction_signature_full(app):
|
||||
[desc_sig_name, "args"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
[desc_sig_name, "str"])],
|
||||
[desc_sig_name, pending_xref, "str"])],
|
||||
[desc_parameter, ([desc_sig_name, "c"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
[desc_sig_name, "bool"],
|
||||
[desc_sig_name, pending_xref, "bool"],
|
||||
" ",
|
||||
[desc_sig_operator, "="],
|
||||
" ",
|
||||
@ -289,7 +320,7 @@ def test_pyfunction_signature_full(app):
|
||||
[desc_sig_name, "kwargs"],
|
||||
[desc_sig_punctuation, ":"],
|
||||
" ",
|
||||
[desc_sig_name, "str"])])])
|
||||
[desc_sig_name, pending_xref, "str"])])])
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
|
||||
@ -340,7 +371,7 @@ def test_optional_pyfunction_signature(app):
|
||||
assert_node(doctree, (addnodes.index,
|
||||
[desc, ([desc_signature, ([desc_name, "compile"],
|
||||
desc_parameterlist,
|
||||
[desc_returns, "ast object"])],
|
||||
[desc_returns, pending_xref, "ast object"])],
|
||||
desc_content)]))
|
||||
assert_node(doctree[1], addnodes.desc, desctype="function",
|
||||
domain="py", objtype="function", noindex=False)
|
||||
@ -483,61 +514,61 @@ def test_pymethod_options(app):
|
||||
|
||||
# method
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'meth1() (Class method)', 'class-meth1', '', None)])
|
||||
entries=[('single', 'meth1() (Class method)', 'class.meth1', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth1' in domain.objects
|
||||
assert domain.objects['Class.meth1'] == ('index', 'class-meth1', 'method')
|
||||
assert domain.objects['Class.meth1'] == ('index', 'class.meth1', 'method')
|
||||
|
||||
# :classmethod:
|
||||
assert_node(doctree[1][1][2], addnodes.index,
|
||||
entries=[('single', 'meth2() (Class class method)', 'class-meth2', '', None)])
|
||||
entries=[('single', 'meth2() (Class class method)', 'class.meth2', '', None)])
|
||||
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "],
|
||||
[desc_name, "meth2"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth2' in domain.objects
|
||||
assert domain.objects['Class.meth2'] == ('index', 'class-meth2', 'method')
|
||||
assert domain.objects['Class.meth2'] == ('index', 'class.meth2', 'method')
|
||||
|
||||
# :staticmethod:
|
||||
assert_node(doctree[1][1][4], addnodes.index,
|
||||
entries=[('single', 'meth3() (Class static method)', 'class-meth3', '', None)])
|
||||
entries=[('single', 'meth3() (Class static method)', 'class.meth3', '', None)])
|
||||
assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "],
|
||||
[desc_name, "meth3"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth3' in domain.objects
|
||||
assert domain.objects['Class.meth3'] == ('index', 'class-meth3', 'method')
|
||||
assert domain.objects['Class.meth3'] == ('index', 'class.meth3', 'method')
|
||||
|
||||
# :async:
|
||||
assert_node(doctree[1][1][6], addnodes.index,
|
||||
entries=[('single', 'meth4() (Class method)', 'class-meth4', '', None)])
|
||||
entries=[('single', 'meth4() (Class method)', 'class.meth4', '', None)])
|
||||
assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, "async "],
|
||||
[desc_name, "meth4"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth4' in domain.objects
|
||||
assert domain.objects['Class.meth4'] == ('index', 'class-meth4', 'method')
|
||||
assert domain.objects['Class.meth4'] == ('index', 'class.meth4', 'method')
|
||||
|
||||
# :property:
|
||||
assert_node(doctree[1][1][8], addnodes.index,
|
||||
entries=[('single', 'meth5() (Class property)', 'class-meth5', '', None)])
|
||||
entries=[('single', 'meth5() (Class property)', 'class.meth5', '', None)])
|
||||
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
|
||||
[desc_name, "meth5"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth5' in domain.objects
|
||||
assert domain.objects['Class.meth5'] == ('index', 'class-meth5', 'method')
|
||||
assert domain.objects['Class.meth5'] == ('index', 'class.meth5', 'method')
|
||||
|
||||
# :abstractmethod:
|
||||
assert_node(doctree[1][1][10], addnodes.index,
|
||||
entries=[('single', 'meth6() (Class method)', 'class-meth6', '', None)])
|
||||
entries=[('single', 'meth6() (Class method)', 'class.meth6', '', None)])
|
||||
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, "abstract "],
|
||||
[desc_name, "meth6"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth6' in domain.objects
|
||||
assert domain.objects['Class.meth6'] == ('index', 'class-meth6', 'method')
|
||||
assert domain.objects['Class.meth6'] == ('index', 'class.meth6', 'method')
|
||||
|
||||
|
||||
def test_pyclassmethod(app):
|
||||
@ -552,13 +583,13 @@ def test_pyclassmethod(app):
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'meth() (Class class method)', 'class-meth', '', None)])
|
||||
entries=[('single', 'meth() (Class class method)', 'class.meth', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "],
|
||||
[desc_name, "meth"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth' in domain.objects
|
||||
assert domain.objects['Class.meth'] == ('index', 'class-meth', 'method')
|
||||
assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
|
||||
|
||||
|
||||
def test_pystaticmethod(app):
|
||||
@ -573,13 +604,13 @@ def test_pystaticmethod(app):
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'meth() (Class static method)', 'class-meth', '', None)])
|
||||
entries=[('single', 'meth() (Class static method)', 'class.meth', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "],
|
||||
[desc_name, "meth"],
|
||||
[desc_parameterlist, ()])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.meth' in domain.objects
|
||||
assert domain.objects['Class.meth'] == ('index', 'class-meth', 'method')
|
||||
assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
|
||||
|
||||
|
||||
def test_pyattribute(app):
|
||||
@ -596,13 +627,13 @@ def test_pyattribute(app):
|
||||
[desc_content, (addnodes.index,
|
||||
desc)])]))
|
||||
assert_node(doctree[1][1][0], addnodes.index,
|
||||
entries=[('single', 'attr (Class attribute)', 'class-attr', '', None)])
|
||||
entries=[('single', 'attr (Class attribute)', 'class.attr', '', None)])
|
||||
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
|
||||
[desc_annotation, ": str"],
|
||||
[desc_annotation, " = ''"])],
|
||||
[desc_content, ()]))
|
||||
assert 'Class.attr' in domain.objects
|
||||
assert domain.objects['Class.attr'] == ('index', 'class-attr', 'attribute')
|
||||
assert domain.objects['Class.attr'] == ('index', 'class.attr', 'attribute')
|
||||
|
||||
|
||||
def test_pydecorator_signature(app):
|
||||
@ -648,10 +679,10 @@ def test_module_index(app):
|
||||
assert index.generate() == (
|
||||
[('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
|
||||
('s', [IndexEntry('sphinx', 1, 'index', 'module-sphinx', '', '', ''),
|
||||
IndexEntry('sphinx.builders', 2, 'index', 'module-sphinx-builders', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx-builders-html', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx-config', '', '', ''),
|
||||
IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx-intl', '', '', '')])],
|
||||
IndexEntry('sphinx.builders', 2, 'index', 'module-sphinx.builders', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx.builders.html', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx.config', '', '', ''),
|
||||
IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '')])],
|
||||
False
|
||||
)
|
||||
|
||||
@ -663,7 +694,7 @@ def test_module_index_submodule(app):
|
||||
index = PythonModuleIndex(app.env.get_domain('py'))
|
||||
assert index.generate() == (
|
||||
[('s', [IndexEntry('sphinx', 1, '', '', '', '', ''),
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx-config', '', '', '')])],
|
||||
IndexEntry('sphinx.config', 2, 'index', 'module-sphinx.config', '', '', '')])],
|
||||
False
|
||||
)
|
||||
|
||||
@ -692,12 +723,12 @@ def test_modindex_common_prefix(app):
|
||||
restructuredtext.parse(app, text)
|
||||
index = PythonModuleIndex(app.env.get_domain('py'))
|
||||
assert index.generate() == (
|
||||
[('b', [IndexEntry('sphinx.builders', 1, 'index', 'module-sphinx-builders', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx-builders-html', '', '', '')]), # NOQA
|
||||
('c', [IndexEntry('sphinx.config', 0, 'index', 'module-sphinx-config', '', '', '')]),
|
||||
[('b', [IndexEntry('sphinx.builders', 1, 'index', 'module-sphinx.builders', '', '', ''), # NOQA
|
||||
IndexEntry('sphinx.builders.html', 2, 'index', 'module-sphinx.builders.html', '', '', '')]), # NOQA
|
||||
('c', [IndexEntry('sphinx.config', 0, 'index', 'module-sphinx.config', '', '', '')]),
|
||||
('d', [IndexEntry('docutils', 0, 'index', 'module-docutils', '', '', '')]),
|
||||
('s', [IndexEntry('sphinx', 0, 'index', 'module-sphinx', '', '', ''),
|
||||
IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx-intl', '', '', '')])],
|
||||
IndexEntry('sphinx_intl', 0, 'index', 'module-sphinx_intl', '', '', '')])],
|
||||
True
|
||||
)
|
||||
|
||||
|
@ -84,7 +84,7 @@ def test_object_inventory(app):
|
||||
refs = app.env.domaindata['py']['objects']
|
||||
|
||||
assert 'func_without_module' in refs
|
||||
assert refs['func_without_module'] == ('objects', 'func-without-module', 'function')
|
||||
assert refs['func_without_module'] == ('objects', 'func_without_module', 'function')
|
||||
assert 'func_without_module2' in refs
|
||||
assert 'mod.func_in_module' in refs
|
||||
assert 'mod.Cls' in refs
|
||||
|
@ -73,7 +73,7 @@ def test_autoclass_content_class(app):
|
||||
' :module: target.autoclass_content',
|
||||
'',
|
||||
' A class inherits __new__ without docstring.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -133,7 +133,7 @@ def test_autoclass_content_init(app):
|
||||
' :module: target.autoclass_content',
|
||||
'',
|
||||
' __new__ docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -203,7 +203,7 @@ def test_autoclass_content_both(app):
|
||||
' A class inherits __new__ without docstring.',
|
||||
'',
|
||||
' __new__ docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -217,7 +217,7 @@ def test_autodoc_inherit_docstrings(app):
|
||||
' :module: target.inheritance',
|
||||
'',
|
||||
' Inherited function.',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
# disable autodoc_inherit_docstrings
|
||||
@ -271,7 +271,7 @@ def test_autodoc_docstring_signature(app):
|
||||
'',
|
||||
' First line of docstring',
|
||||
' Second line of docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
# disable autodoc_docstring_signature
|
||||
@ -316,7 +316,7 @@ def test_autodoc_docstring_signature(app):
|
||||
'',
|
||||
' First line of docstring',
|
||||
' Second line of docstring',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -457,7 +457,7 @@ def test_mocked_module_imports(app, warning):
|
||||
' :module: target.need_mocks',
|
||||
'',
|
||||
' a function takes mocked object as an argument',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
assert warning.getvalue() == ''
|
||||
|
||||
|
@ -44,7 +44,7 @@ def test_cut_lines(app):
|
||||
' :module: target.process_docstring',
|
||||
'',
|
||||
' second line',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ def test_between(app):
|
||||
' :module: target.process_docstring',
|
||||
'',
|
||||
' second line',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
@ -77,5 +77,5 @@ def test_between_exclude(app):
|
||||
'',
|
||||
' first line',
|
||||
' third line',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
@ -42,5 +42,5 @@ def test_private_field_and_private_members(app):
|
||||
' private_function is a docstring().',
|
||||
'',
|
||||
' :meta private:',
|
||||
' '
|
||||
'',
|
||||
]
|
||||
|
@ -1020,6 +1020,34 @@ Sooper Warning:
|
||||
actual = str(GoogleDocstring(docstring, testConfig))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_noindex(self):
|
||||
docstring = """
|
||||
Attributes:
|
||||
arg
|
||||
description
|
||||
|
||||
Methods:
|
||||
func(i, j)
|
||||
description
|
||||
"""
|
||||
|
||||
expected = """
|
||||
.. attribute:: arg
|
||||
:noindex:
|
||||
|
||||
description
|
||||
|
||||
.. method:: func(i, j)
|
||||
:noindex:
|
||||
|
||||
|
||||
description
|
||||
"""
|
||||
config = Config()
|
||||
actual = str(GoogleDocstring(docstring, config=config, app=None, what='module',
|
||||
options={'noindex': True}))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class NumpyDocstringTest(BaseDocstringTest):
|
||||
docstrings = [(
|
||||
|
@ -870,7 +870,7 @@ def test_xml_refs_in_python_domain(app):
|
||||
assert_elem(
|
||||
para0[0],
|
||||
['SEE THIS DECORATOR:', 'sensitive_variables()', '.'],
|
||||
['sensitive-sensitive-variables'])
|
||||
['sensitive.sensitive_variables'])
|
||||
|
||||
|
||||
@sphinx_intl
|
||||
|
@ -103,6 +103,17 @@ def test_nonl_info_log(app, status, warning):
|
||||
assert 'message1message2\nmessage3' in status.getvalue()
|
||||
|
||||
|
||||
def test_once_warning_log(app, status, warning):
|
||||
logging.setup(app, status, warning)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.warning('message: %d', 1, once=True)
|
||||
logger.warning('message: %d', 1, once=True)
|
||||
logger.warning('message: %d', 2, once=True)
|
||||
|
||||
assert 'WARNING: message: 1\nWARNING: message: 2\n' in strip_escseq(warning.getvalue())
|
||||
|
||||
|
||||
def test_is_suppressed_warning():
|
||||
suppress_warnings = ["ref", "files.*", "rest.duplicated_labels"]
|
||||
|
||||
|
@ -189,9 +189,9 @@ def test_clean_astext():
|
||||
('', '', 'id0'),
|
||||
('term', '', 'term-0'),
|
||||
('term', 'Sphinx', 'term-sphinx'),
|
||||
('', 'io.StringIO', 'io-stringio'), # contains a dot
|
||||
('', 'sphinx.setup_command', 'sphinx-setup-command'), # contains a dot
|
||||
('', '_io.StringIO', 'io-stringio'), # starts with underscore
|
||||
('', 'io.StringIO', 'io.stringio'), # contains a dot
|
||||
('', 'sphinx.setup_command', 'sphinx.setup_command'), # contains a dot & underscore
|
||||
('', '_io.StringIO', 'io.stringio'), # starts with underscore
|
||||
('', 'sphinx', 'sphinx'), # alphabets in unicode fullwidth characters
|
||||
('', '悠好', 'id0'), # multibytes text (in Chinese)
|
||||
('', 'Hello=悠好=こんにちは', 'hello'), # alphabets and multibytes text
|
||||
|
Loading…
Reference in New Issue
Block a user