Merge branch '3.x'

This commit is contained in:
Takeshi KOMIYA 2020-03-10 01:15:52 +09:00
commit e116613a3a
25 changed files with 1356 additions and 999 deletions

View File

@ -1,8 +1,6 @@
comment: false
coverage:
status:
project:
default:
enabled: no
patch:
default:
enabled: no

View File

@ -1,6 +1,6 @@
language: python
sudo: false
os: linux
dist: xenial
language: python
cache: pip
env:
@ -9,7 +9,7 @@ env:
- SKIP_LATEX_BUILD=1
- IS_PYTHON=true
matrix:
jobs:
include:
- python: '3.6'
env:
@ -20,7 +20,9 @@ matrix:
- python: '3.8'
env:
- TOXENV=du16
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
# Disable codecov temporarily
# refs: https://github.com/sphinx-doc/sphinx/pull/7286#issuecomment-596617853
# - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- python: 'nightly'
env:
- TOXENV=py39
@ -34,8 +36,7 @@ matrix:
env: TOXENV=flake8
- language: node_js
node_js:
- 10.7
node_js: '10.7'
env: IS_PYTHON=false
services: xvfb

18
CHANGES
View File

@ -52,18 +52,17 @@ Incompatible changes
* Due to the scoping changes for :rst:dir:`productionlist` some uses of
:rst:role:`token` must be modified to include the scope which was previously
ignored.
* #6903: js domain: Internal data structure has changed. Both objects and
modules have node_id for cross reference
* #6903: Internal data structure of Python, reST and standard domains have
changed. The node_id is added to the index of objects and modules. Now they
contains a pair of docname and node_id for cross reference.
* #7210: js domain: Non intended behavior is removed such as ``parseInt_`` links
to ``.. js:function:: parseInt``
* #6903: rst domain: Internal data structure has changed. Now objects have
node_id for cross reference
* #7229: rst domain: Non intended behavior is removed such as ``numref_`` links
to ``.. rst:role:: numref``
* #6903: py domain: Internal data structure has changed. Both objects and
modules have node_id for cross reference
* #6903: py domain: Non intended behavior is removed such as ``say_hello_``
links to ``.. py:function:: say_hello()``
* #7246: py domain: Drop special cross reference helper for exceptions,
functions and methods
Deprecated
----------
@ -71,6 +70,7 @@ Deprecated
* ``desc_signature['first']``
* ``sphinx.directives.DescDirective``
* ``sphinx.domains.std.StandardDomain.add_object()``
* ``sphinx.domains.python.PyDecoratorMixin``
* ``sphinx.parsers.Parser.app``
* ``sphinx.testing.path.Path.text()``
* ``sphinx.testing.path.Path.bytes()``
@ -87,6 +87,9 @@ Features added
* #6830: autodoc: consider a member private if docstring contains
``:meta private:`` in info-field-list
* #7165: autodoc: Support Annotated type (PEP-593)
* #2815: autodoc: Support singledispatch functions and methods
* #7079: autodoc: :confval:`autodoc_typehints` accepts ``"description"``
configuration. It shows typehints as object description
* #6558: glossary: emit a warning for duplicated glossary entry
* #3106: domain: Register hyperlink target for index page automatically
* #6558: std domain: emit a warning for duplicated generic objects
@ -120,8 +123,11 @@ Bugs fixed
* C++, suppress warnings for directly dependent typenames in cross references
generated automatically in signatures.
* #5637: autodoc: Incorrect handling of nested class names on show-inheritance
* #7267: autodoc: error message for invalid directive options has wrong location
* #5637: inheritance_diagram: Incorrect handling of nested class names
* #7139: ``code-block:: guess`` does not work
* #7278: html search: Fix use of ``html_file_suffix`` instead of
``html_link_suffix`` in search results
Testing
--------

View File

@ -5,7 +5,6 @@ PYTHON ?= python3
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = $(PYTHON) ../sphinx/cmd/build.py
SPHINXPROJ = sphinx
SOURCEDIR = .
BUILDDIR = _build

View File

@ -7,6 +7,7 @@ import sphinx
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo',
'sphinx.ext.autosummary', 'sphinx.ext.extlinks',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram']
master_doc = 'contents'

View File

@ -61,6 +61,11 @@ The following is a list of deprecated interfaces.
- 5.0
- ``sphinx.domains.std.StandardDomain.note_object()``
* - ``sphinx.domains.python.PyDecoratorMixin``
- 3.0
- 5.0
- N/A
* - ``sphinx.parsers.Parser.app``
- 3.0
- 5.0

View File

@ -7,7 +7,6 @@ if "%SPHINXBUILD%" == "" (
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=sphinx-doc
if "%1" == "" goto help

View File

@ -466,9 +466,13 @@ There are also config values that you can set:
following values:
* ``'signature'`` -- Show typehints as its signature (default)
* ``'description'`` -- Show typehints as content of function or method
* ``'none'`` -- Do not show typehints
.. versionadded:: 2.1
.. versionadded:: 3.0
New option ``'description'`` is added.
.. confval:: autodoc_warningiserror
@ -596,24 +600,3 @@ member should be included in the documentation by using the following event:
``inherited_members``, ``undoc_members``, ``show_inheritance`` and
``noindex`` that are true if the flag option of same name was given to the
auto directive
Generating documents from type annotations
------------------------------------------
As an experimental feature, autodoc provides ``sphinx.ext.autodoc.typehints`` as
an additional extension. It extends autodoc itself to generate function document
from its type annotations.
To enable the feature, please add ``sphinx.ext.autodoc.typehints`` to list of
extensions and set `'description'` to :confval:`autodoc_typehints`:
.. code-block:: python
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autodoc.typehints']
autodoc_typehints = 'description'
.. versionadded:: 2.4
Added as an experimental feature. This will be integrated into autodoc core
in Sphinx-3.0.

View File

@ -48,7 +48,7 @@ extras_require = {
'docutils-stubs',
],
'test': [
'pytest < 5.3.3',
'pytest',
'pytest-cov',
'html5lib',
'typed_ast', # for py35-37

View File

@ -469,6 +469,7 @@ class StandaloneHTMLBuilder(Builder):
'show_source': self.config.html_show_sourcelink,
'sourcelink_suffix': self.config.html_sourcelink_suffix,
'file_suffix': self.out_suffix,
'link_suffix': self.link_suffix,
'script_files': self.script_files,
'language': self.config.language,
'css_files': self.css_files,

View File

@ -25,7 +25,7 @@ from sphinx import addnodes
from sphinx.addnodes import pending_xref, desc_signature
from sphinx.application import Sphinx
from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType, Index, IndexEntry
from sphinx.environment import BuildEnvironment
@ -439,8 +439,14 @@ class PyModulelevel(PyObject):
"""
def run(self) -> List[Node]:
warnings.warn('PyModulelevel is deprecated.',
RemovedInSphinx40Warning)
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyModulelevel is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx40Warning)
break
else:
warnings.warn('PyModulelevel is deprecated', RemovedInSphinx40Warning)
return super().run()
@ -485,6 +491,23 @@ class PyFunction(PyObject):
return _('%s() (built-in function)') % name
class PyDecoratorFunction(PyFunction):
"""Description of a decorator."""
def run(self) -> List[Node]:
# a decorator function is a function after all
self.name = 'py:function'
return super().run()
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
ret = super().handle_signature(sig, signode)
signode.insert(0, addnodes.desc_addname('@', '@'))
return ret
def needs_arglist(self) -> bool:
return False
class PyVariable(PyObject):
"""Description of a variable."""
@ -542,8 +565,14 @@ class PyClassmember(PyObject):
"""
def run(self) -> List[Node]:
warnings.warn('PyClassmember is deprecated.',
RemovedInSphinx40Warning)
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyClassmember is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx40Warning)
break
else:
warnings.warn('PyClassmember is deprecated', RemovedInSphinx40Warning)
return super().run()
@ -696,6 +725,22 @@ class PyStaticMethod(PyMethod):
return super().run()
class PyDecoratorMethod(PyMethod):
"""Description of a decoratormethod."""
def run(self) -> List[Node]:
self.name = 'py:method'
return super().run()
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
ret = super().handle_signature(sig, signode)
signode.insert(0, addnodes.desc_addname('@', '@'))
return ret
def needs_arglist(self) -> bool:
return False
class PyAttribute(PyObject):
"""Description of an attribute."""
@ -738,6 +783,15 @@ class PyDecoratorMixin:
Mixin for decorator directives.
"""
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
for cls in self.__class__.__mro__:
if cls.__name__ != 'DirectiveAdapter':
warnings.warn('PyDecoratorMixin is deprecated. '
'Please check the implementation of %s' % cls,
RemovedInSphinx50Warning)
break
else:
warnings.warn('PyDecoratorMixin is deprecated', RemovedInSphinx50Warning)
ret = super().handle_signature(sig, signode) # type: ignore
signode.insert(0, addnodes.desc_addname('@', '@'))
return ret
@ -746,25 +800,6 @@ class PyDecoratorMixin:
return False
class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel):
"""
Directive to mark functions meant to be used as decorators.
"""
def run(self) -> List[Node]:
# a decorator function is a function after all
self.name = 'py:function'
return super().run()
class PyDecoratorMethod(PyDecoratorMixin, PyClassmember):
"""
Directive to mark methods meant to be used as decorators.
"""
def run(self) -> List[Node]:
self.name = 'py:method'
return super().run()
class PyModule(SphinxDirective):
"""
Directive to mark description of a new module.
@ -1106,14 +1141,6 @@ class PythonDomain(Domain):
elif modname and classname and \
modname + '.' + classname + '.' + name in self.objects:
newname = modname + '.' + classname + '.' + name
# special case: builtin exceptions have module "exceptions" set
elif type == 'exc' and '.' not in name and \
'exceptions.' + name in self.objects:
newname = 'exceptions.' + name
# special case: object methods
elif type in ('func', 'meth') and '.' not in name and \
'object.' + name in self.objects:
newname = 'object.' + name
if newname is not None:
matches.append((newname, self.objects[newname]))
return matches

View File

@ -16,6 +16,7 @@ import warnings
from types import ModuleType
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
from typing import TYPE_CHECKING
from unittest.mock import patch
from docutils.statemachine import StringList
@ -1002,7 +1003,7 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
(inspect.isroutine(member) and isinstance(parent, ModuleDocumenter)))
def format_args(self, **kwargs: Any) -> str:
if self.env.config.autodoc_typehints == 'none':
if self.env.config.autodoc_typehints in ('none', 'description'):
kwargs.setdefault('show_annotation', False)
if inspect.isbuiltin(self.object) or inspect.ismethoddescriptor(self.object):
@ -1055,6 +1056,62 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
self.add_line(' :async:', sourcename)
class SingledispatchFunctionDocumenter(FunctionDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed functions.
"""
objtype = 'singledispatch_function'
directivetype = 'function'
member_order = 30
# before FunctionDocumenter
priority = FunctionDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
return (super().can_document_member(member, membername, isattr, parent) and
inspect.is_singledispatch_function(member))
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
# 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
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])
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
"""Annotate type hint to the first argument of function if needed."""
sig = inspect.signature(func)
if len(sig.parameters) == 0:
return
name = list(sig.parameters)[0]
if name not in func.__annotations__:
func.__annotations__[name] = typ
class DecoratorDocumenter(FunctionDocumenter):
"""
Specialized Documenter subclass for decorator functions.
@ -1399,6 +1456,66 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
pass
class SingledispatchMethodDocumenter(MethodDocumenter):
"""
Specialized Documenter subclass for singledispatch'ed methods.
"""
objtype = 'singledispatch_method'
directivetype = 'method'
member_order = 50
# before MethodDocumenter
priority = MethodDocumenter.priority + 1
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
if super().can_document_member(member, membername, isattr, parent) and parent.object:
meth = parent.object.__dict__.get(membername)
return inspect.is_singledispatch_method(meth)
else:
return False
def add_directive_header(self, sig: str) -> None:
sourcename = self.get_sourcename()
# 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)
documenter = MethodDocumenter(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])
def annotate_to_first_argument(self, func: Callable, typ: Type) -> None:
"""Annotate type hint to the first argument of function if needed."""
sig = inspect.signature(func, bound_method=True)
if len(sig.parameters) == 0:
return
name = list(sig.parameters)[0]
if name not in func.__annotations__:
func.__annotations__[name] = typ
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
"""
Specialized Documenter subclass for attributes.
@ -1611,8 +1728,10 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(DataDocumenter)
app.add_autodocumenter(DataDeclarationDocumenter)
app.add_autodocumenter(FunctionDocumenter)
app.add_autodocumenter(SingledispatchFunctionDocumenter)
app.add_autodocumenter(DecoratorDocumenter)
app.add_autodocumenter(MethodDocumenter)
app.add_autodocumenter(SingledispatchMethodDocumenter)
app.add_autodocumenter(AttributeDocumenter)
app.add_autodocumenter(PropertyDocumenter)
app.add_autodocumenter(InstanceAttributeDocumenter)
@ -1624,7 +1743,8 @@ def setup(app: Sphinx) -> Dict[str, Any]:
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)
app.add_config_value('autodoc_typehints', "signature", True, ENUM("signature", "none"))
app.add_config_value('autodoc_typehints', "signature", True,
ENUM("signature", "description", "none"))
app.add_config_value('autodoc_warningiserror', True, True)
app.add_config_value('autodoc_inherit_docstrings', True, True)
app.add_event('autodoc-before-process-signature')
@ -1633,5 +1753,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_event('autodoc-skip-member')
app.setup_extension('sphinx.ext.autodoc.type_comment')
app.setup_extension('sphinx.ext.autodoc.typehints')
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}

View File

@ -133,7 +133,7 @@ class AutodocDirective(SphinxDirective):
except (KeyError, ValueError, TypeError) as exc:
# an option is either unknown or has a wrong type
logger.error('An option to %s is either unknown or has an invalid value: %s' %
(self.name, exc), location=(source, lineno))
(self.name, exc), location=(self.env.docname, lineno))
return []
# generate the output

View File

@ -18,21 +18,9 @@ from docutils.nodes import Element
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.config import Config, ENUM
from sphinx.util import inspect, typing
def config_inited(app: Sphinx, config: Config) -> None:
if config.autodoc_typehints == 'description':
# HACK: override this to make autodoc suppressing typehints in signatures
config.autodoc_typehints = 'none' # type: ignore
# preserve user settings
app._autodoc_typehints_description = True # type: ignore
else:
app._autodoc_typehints_description = False # type: ignore
def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
options: Dict, args: str, retann: str) -> None:
"""Record type hints to env object."""
@ -53,7 +41,7 @@ def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
def merge_typehints(app: Sphinx, domain: str, objtype: str, contentnode: Element) -> None:
if domain != 'py':
return
if app._autodoc_typehints_description is False: # type: ignore
if app.config.autodoc_typehints != 'description':
return
signature = cast(addnodes.desc_signature, contentnode.parent[0])
@ -141,10 +129,6 @@ def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> No
def setup(app: Sphinx) -> Dict[str, Any]:
app.setup_extension('sphinx.ext.autodoc')
app.config.values['autodoc_typehints'] = ('signature', True,
ENUM("signature", "description", "none"))
app.connect('config-inited', config_inited)
app.connect('autodoc-process-signature', record_typehints)
app.connect('object-description-transform', merge_typehints)

View File

@ -68,12 +68,14 @@ def setup_documenters(app: Any) -> None:
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, DataDeclarationDocumenter,
SingledispatchFunctionDocumenter,
)
documenters = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, DataDeclarationDocumenter,
SingledispatchFunctionDocumenter,
] # type: List[Type[Documenter]]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ var DOCUMENTATION_OPTIONS = {
COLLAPSE_INDEX: false,
BUILDER: '{{ builder }}',
FILE_SUFFIX: '{{ file_suffix }}',
LINK_SUFFIX: '{{ link_suffix }}',
HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}',
NAVIGATION_WITH_KEYS: {{ 'true' if theme_navigation_with_keys|tobool else 'false'}}

View File

@ -251,6 +251,7 @@ var Search = {
var item = results.pop();
var listItem = $('<li style="display:none"></li>');
var requestUrl = "";
var linkUrl = "";
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
// dirhtml builder
var dirname = item[0] + '/';
@ -260,13 +261,15 @@ var Search = {
dirname = '';
}
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
}
listItem.append($('<a/>').attr('href',
requestUrl +
linkUrl +
highlightstring + item[2]).html(item[1]));
if (item[3]) {
listItem.append($('<span> (' + item[3] + ')</span>'));

View File

@ -224,6 +224,26 @@ def isattributedescriptor(obj: Any) -> bool:
return False
def is_singledispatch_function(obj: Any) -> bool:
"""Check if the object is singledispatch function."""
if (inspect.isfunction(obj) and
hasattr(obj, 'dispatch') and
hasattr(obj, 'register') and
obj.dispatch.__module__ == 'functools'):
return True
else:
return False
def is_singledispatch_method(obj: Any) -> bool:
"""Check if the object is singledispatch method."""
try:
from functools import singledispatchmethod # type: ignore
return isinstance(obj, singledispatchmethod)
except ImportError: # py35-37
return False
def isfunction(obj: Any) -> bool:
"""Check if the object is function."""
return inspect.isfunction(unwrap(obj))

View File

@ -51,3 +51,11 @@ module
.. py:attribute:: attr2
:type: :doc:`index`
.. py:module:: exceptions
.. py:exception:: Exception
.. py:module:: object
.. py:function:: sum()

View File

@ -0,0 +1,19 @@
from functools import singledispatch
@singledispatch
def func(arg, kwarg=None):
"""A function for general use."""
pass
@func.register(int)
def _func_int(arg, kwarg=None):
"""A function for int."""
pass
@func.register(str)
def _func_str(arg, kwarg=None):
"""A function for str."""
pass

View File

@ -0,0 +1,20 @@
from functools import singledispatchmethod
class Foo:
"""docstring"""
@singledispatchmethod
def meth(self, arg, kwarg=None):
"""A method for general use."""
pass
@meth.register(int)
def _meth_int(self, arg, kwarg=None):
"""A method for int."""
pass
@meth.register(str)
def _meth_str(self, arg, kwarg=None):
"""A method for str."""
pass

View File

@ -1563,3 +1563,49 @@ def test_autodoc_for_egged_code(app):
' :module: sample',
''
]
@pytest.mark.usefixtures('setup_test')
def test_singledispatch():
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatch', options)
assert list(actual) == [
'',
'.. py:module:: target.singledispatch',
'',
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',
'',
' A function for general use.',
' '
]
@pytest.mark.skipif(sys.version_info < (3, 8),
reason='singledispatchmethod is available since python3.8')
@pytest.mark.usefixtures('setup_test')
def test_singledispatchmethod():
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.singledispatchmethod', options)
assert list(actual) == [
'',
'.. py:module:: target.singledispatchmethod',
'',
'',
'.. py:class:: Foo',
' :module: target.singledispatchmethod',
'',
' docstring',
' ',
' ',
' .. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',
' ',
' A method for general use.',
' '
]

View File

@ -585,6 +585,36 @@ def test_pyattribute(app):
assert domain.objects['Class.attr'] == ('index', 'class-attr', 'attribute')
def test_pydecorator_signature(app):
text = ".. py:decorator:: deco"
domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_addname, "@"],
[desc_name, "deco"])],
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="py", objtype="function", noindex=False)
assert 'deco' in domain.objects
assert domain.objects['deco'] == ('index', 'deco', 'function')
def test_pydecoratormethod_signature(app):
text = ".. py:decoratormethod:: deco"
domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_addname, "@"],
[desc_name, "deco"])],
desc_content)]))
assert_node(doctree[1], addnodes.desc, desctype="method",
domain="py", objtype="method", noindex=False)
assert 'deco' in domain.objects
assert domain.objects['deco'] == ('index', 'deco', 'method')
@pytest.mark.sphinx(freshenv=True)
def test_module_index(app):
text = (".. py:module:: docutils\n"

View File

@ -462,10 +462,9 @@ def test_mocked_module_imports(app, warning):
assert warning.getvalue() == ''
@pytest.mark.sphinx('html', testroot='ext-autodoc')
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_typehints': "signature"})
def test_autodoc_typehints_signature(app):
app.config.autodoc_typehints = "signature"
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'module', 'target.typehints', options)
@ -513,10 +512,9 @@ def test_autodoc_typehints_signature(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_typehints': "none"})
def test_autodoc_typehints_none(app):
app.config.autodoc_typehints = "none"
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'module', 'target.typehints', options)
@ -564,8 +562,7 @@ def test_autodoc_typehints_none(app):
@pytest.mark.sphinx('text', testroot='ext-autodoc',
confoverrides={'extensions': ['sphinx.ext.autodoc.typehints'],
'autodoc_typehints': 'description'})
confoverrides={'autodoc_typehints': "description"})
def test_autodoc_typehints_description(app):
app.build()
context = (app.outdir / 'index.txt').read_text()