mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 7079_autodoc_typehints_description
This commit is contained in:
commit
3f21fd6041
14
CHANGES
14
CHANGES
@ -23,18 +23,17 @@ Incompatible changes
|
|||||||
* Due to the scoping changes for :rst:dir:`productionlist` some uses of
|
* 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
|
:rst:role:`token` must be modified to include the scope which was previously
|
||||||
ignored.
|
ignored.
|
||||||
* #6903: js domain: Internal data structure has changed. Both objects and
|
* #6903: Internal data structure of Python, reST and standard domains have
|
||||||
modules have node_id for cross reference
|
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
|
* #7210: js domain: Non intended behavior is removed such as ``parseInt_`` links
|
||||||
to ``.. js:function:: parseInt``
|
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
|
* #7229: rst domain: Non intended behavior is removed such as ``numref_`` links
|
||||||
to ``.. rst:role:: numref``
|
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_``
|
* #6903: py domain: Non intended behavior is removed such as ``say_hello_``
|
||||||
links to ``.. py:function:: say_hello()``
|
links to ``.. py:function:: say_hello()``
|
||||||
|
* #7246: py domain: Drop special cross reference helper for exceptions,
|
||||||
|
functions and methods
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
@ -42,6 +41,7 @@ Deprecated
|
|||||||
* ``desc_signature['first']``
|
* ``desc_signature['first']``
|
||||||
* ``sphinx.directives.DescDirective``
|
* ``sphinx.directives.DescDirective``
|
||||||
* ``sphinx.domains.std.StandardDomain.add_object()``
|
* ``sphinx.domains.std.StandardDomain.add_object()``
|
||||||
|
* ``sphinx.domains.python.PyDecoratorMixin``
|
||||||
* ``sphinx.parsers.Parser.app``
|
* ``sphinx.parsers.Parser.app``
|
||||||
* ``sphinx.testing.path.Path.text()``
|
* ``sphinx.testing.path.Path.text()``
|
||||||
* ``sphinx.testing.path.Path.bytes()``
|
* ``sphinx.testing.path.Path.bytes()``
|
||||||
@ -58,6 +58,7 @@ Features added
|
|||||||
* #6830: autodoc: consider a member private if docstring contains
|
* #6830: autodoc: consider a member private if docstring contains
|
||||||
``:meta private:`` in info-field-list
|
``:meta private:`` in info-field-list
|
||||||
* #7165: autodoc: Support Annotated type (PEP-593)
|
* #7165: autodoc: Support Annotated type (PEP-593)
|
||||||
|
* #2815: autodoc: Support singledispatch functions and methods
|
||||||
* #7079: autodoc: :confval:`autodoc_typehints` accepts ``"description"``
|
* #7079: autodoc: :confval:`autodoc_typehints` accepts ``"description"``
|
||||||
configuration. It shows typehints as object description
|
configuration. It shows typehints as object description
|
||||||
* #6558: glossary: emit a warning for duplicated glossary entry
|
* #6558: glossary: emit a warning for duplicated glossary entry
|
||||||
@ -93,6 +94,7 @@ Bugs fixed
|
|||||||
* C++, suppress warnings for directly dependent typenames in cross references
|
* C++, suppress warnings for directly dependent typenames in cross references
|
||||||
generated automatically in signatures.
|
generated automatically in signatures.
|
||||||
* #5637: autodoc: Incorrect handling of nested class names on show-inheritance
|
* #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
|
* #5637: inheritance_diagram: Incorrect handling of nested class names
|
||||||
* #7139: ``code-block:: guess`` does not work
|
* #7139: ``code-block:: guess`` does not work
|
||||||
|
|
||||||
|
@ -41,6 +41,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 5.0
|
- 5.0
|
||||||
- ``sphinx.domains.std.StandardDomain.note_object()``
|
- ``sphinx.domains.std.StandardDomain.note_object()``
|
||||||
|
|
||||||
|
* - ``sphinx.domains.python.PyDecoratorMixin``
|
||||||
|
- 3.0
|
||||||
|
- 5.0
|
||||||
|
- N/A
|
||||||
|
|
||||||
* - ``sphinx.parsers.Parser.app``
|
* - ``sphinx.parsers.Parser.app``
|
||||||
- 3.0
|
- 3.0
|
||||||
- 5.0
|
- 5.0
|
||||||
|
@ -25,7 +25,7 @@ from sphinx import addnodes
|
|||||||
from sphinx.addnodes import pending_xref, desc_signature
|
from sphinx.addnodes import pending_xref, desc_signature
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
from sphinx.builders import Builder
|
from sphinx.builders import Builder
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
from sphinx.directives import ObjectDescription
|
from sphinx.directives import ObjectDescription
|
||||||
from sphinx.domains import Domain, ObjType, Index, IndexEntry
|
from sphinx.domains import Domain, ObjType, Index, IndexEntry
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
@ -489,6 +489,23 @@ class PyFunction(PyObject):
|
|||||||
return _('%s() (built-in function)') % name
|
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):
|
class PyVariable(PyObject):
|
||||||
"""Description of a variable."""
|
"""Description of a variable."""
|
||||||
|
|
||||||
@ -700,6 +717,22 @@ class PyStaticMethod(PyMethod):
|
|||||||
return super().run()
|
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):
|
class PyAttribute(PyObject):
|
||||||
"""Description of an attribute."""
|
"""Description of an attribute."""
|
||||||
|
|
||||||
@ -742,6 +775,15 @@ class PyDecoratorMixin:
|
|||||||
Mixin for decorator directives.
|
Mixin for decorator directives.
|
||||||
"""
|
"""
|
||||||
def handle_signature(self, sig: str, signode: desc_signature) -> Tuple[str, str]:
|
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
|
ret = super().handle_signature(sig, signode) # type: ignore
|
||||||
signode.insert(0, addnodes.desc_addname('@', '@'))
|
signode.insert(0, addnodes.desc_addname('@', '@'))
|
||||||
return ret
|
return ret
|
||||||
@ -750,25 +792,6 @@ class PyDecoratorMixin:
|
|||||||
return False
|
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):
|
class PyModule(SphinxDirective):
|
||||||
"""
|
"""
|
||||||
Directive to mark description of a new module.
|
Directive to mark description of a new module.
|
||||||
@ -1110,14 +1133,6 @@ class PythonDomain(Domain):
|
|||||||
elif modname and classname and \
|
elif modname and classname and \
|
||||||
modname + '.' + classname + '.' + name in self.objects:
|
modname + '.' + classname + '.' + name in self.objects:
|
||||||
newname = modname + '.' + classname + '.' + name
|
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:
|
if newname is not None:
|
||||||
matches.append((newname, self.objects[newname]))
|
matches.append((newname, self.objects[newname]))
|
||||||
return matches
|
return matches
|
||||||
|
@ -14,7 +14,8 @@ import importlib
|
|||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Union
|
from typing import Any, Callable, Dict, Iterator, List, Sequence, Set, Tuple, Type, Union
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
from docutils.statemachine import StringList
|
from docutils.statemachine import StringList
|
||||||
|
|
||||||
@ -1056,6 +1057,62 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # typ
|
|||||||
self.add_line(' :async:', sourcename)
|
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):
|
class DecoratorDocumenter(FunctionDocumenter):
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for decorator functions.
|
Specialized Documenter subclass for decorator functions.
|
||||||
@ -1400,6 +1457,66 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
pass
|
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
|
class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for attributes.
|
Specialized Documenter subclass for attributes.
|
||||||
@ -1612,8 +1729,10 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_autodocumenter(DataDocumenter)
|
app.add_autodocumenter(DataDocumenter)
|
||||||
app.add_autodocumenter(DataDeclarationDocumenter)
|
app.add_autodocumenter(DataDeclarationDocumenter)
|
||||||
app.add_autodocumenter(FunctionDocumenter)
|
app.add_autodocumenter(FunctionDocumenter)
|
||||||
|
app.add_autodocumenter(SingledispatchFunctionDocumenter)
|
||||||
app.add_autodocumenter(DecoratorDocumenter)
|
app.add_autodocumenter(DecoratorDocumenter)
|
||||||
app.add_autodocumenter(MethodDocumenter)
|
app.add_autodocumenter(MethodDocumenter)
|
||||||
|
app.add_autodocumenter(SingledispatchMethodDocumenter)
|
||||||
app.add_autodocumenter(AttributeDocumenter)
|
app.add_autodocumenter(AttributeDocumenter)
|
||||||
app.add_autodocumenter(PropertyDocumenter)
|
app.add_autodocumenter(PropertyDocumenter)
|
||||||
app.add_autodocumenter(InstanceAttributeDocumenter)
|
app.add_autodocumenter(InstanceAttributeDocumenter)
|
||||||
|
@ -137,7 +137,7 @@ class AutodocDirective(SphinxDirective):
|
|||||||
except (KeyError, ValueError, TypeError) as exc:
|
except (KeyError, ValueError, TypeError) as exc:
|
||||||
# an option is either unknown or has a wrong type
|
# 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' %
|
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 []
|
return []
|
||||||
|
|
||||||
# generate the output
|
# generate the output
|
||||||
|
@ -72,12 +72,14 @@ def setup_documenters(app: Any) -> None:
|
|||||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||||
SlotsAttributeDocumenter, DataDeclarationDocumenter,
|
SlotsAttributeDocumenter, DataDeclarationDocumenter,
|
||||||
|
SingledispatchFunctionDocumenter,
|
||||||
)
|
)
|
||||||
documenters = [
|
documenters = [
|
||||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||||
SlotsAttributeDocumenter, DataDeclarationDocumenter,
|
SlotsAttributeDocumenter, DataDeclarationDocumenter,
|
||||||
|
SingledispatchFunctionDocumenter,
|
||||||
] # type: List[Type[Documenter]]
|
] # type: List[Type[Documenter]]
|
||||||
for documenter in documenters:
|
for documenter in documenters:
|
||||||
app.registry.add_documenter(documenter.objtype, documenter)
|
app.registry.add_documenter(documenter.objtype, documenter)
|
||||||
|
@ -224,6 +224,26 @@ def isattributedescriptor(obj: Any) -> bool:
|
|||||||
return False
|
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:
|
def isfunction(obj: Any) -> bool:
|
||||||
"""Check if the object is function."""
|
"""Check if the object is function."""
|
||||||
return inspect.isfunction(unwrap(obj))
|
return inspect.isfunction(unwrap(obj))
|
||||||
|
@ -51,3 +51,11 @@ module
|
|||||||
.. py:attribute:: attr2
|
.. py:attribute:: attr2
|
||||||
|
|
||||||
:type: :doc:`index`
|
:type: :doc:`index`
|
||||||
|
|
||||||
|
.. py:module:: exceptions
|
||||||
|
|
||||||
|
.. py:exception:: Exception
|
||||||
|
|
||||||
|
.. py:module:: object
|
||||||
|
|
||||||
|
.. py:function:: sum()
|
||||||
|
19
tests/roots/test-ext-autodoc/target/singledispatch.py
Normal file
19
tests/roots/test-ext-autodoc/target/singledispatch.py
Normal 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
|
20
tests/roots/test-ext-autodoc/target/singledispatchmethod.py
Normal file
20
tests/roots/test-ext-autodoc/target/singledispatchmethod.py
Normal 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
|
@ -1563,3 +1563,49 @@ def test_autodoc_for_egged_code(app):
|
|||||||
' :module: sample',
|
' :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.',
|
||||||
|
' '
|
||||||
|
]
|
||||||
|
@ -585,6 +585,36 @@ def test_pyattribute(app):
|
|||||||
assert domain.objects['Class.attr'] == ('index', 'class-attr', 'attribute')
|
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)
|
@pytest.mark.sphinx(freshenv=True)
|
||||||
def test_module_index(app):
|
def test_module_index(app):
|
||||||
text = (".. py:module:: docutils\n"
|
text = (".. py:module:: docutils\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user