Merge branch '2.0'

This commit is contained in:
Takeshi KOMIYA 2020-01-30 23:29:32 +09:00
commit e7a0af807a
19 changed files with 383 additions and 27 deletions

View File

@ -24,7 +24,7 @@ matrix:
env: env:
- TOXENV=du15 - TOXENV=du15
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg" - PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- python: '3.8' - python: 'nightly'
env: env:
- TOXENV=du16 - TOXENV=du16
- python: '3.6' - python: '3.6'

10
CHANGES
View File

@ -83,6 +83,7 @@ Deprecated
* ``sphinx.util.detect_encoding()`` * ``sphinx.util.detect_encoding()``
* ``sphinx.util.get_module_source()`` * ``sphinx.util.get_module_source()``
* ``sphinx.util.inspect.Signature`` * ``sphinx.util.inspect.Signature``
* ``sphinx.util.inspect.safe_getmembers()``
Features added Features added
-------------- --------------
@ -100,8 +101,14 @@ Features added
* #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``) * #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``)
annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_ annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_
is required) is required)
* #7051: autodoc: Support instance variables without defaults (PEP-526)
* #6418: autodoc: Add a new extension ``sphinx.ext.autodoc.typehints``. It shows
typehints as object description if ``autodoc_typehints = "description"`` set.
This is an experimental extension and it will be integrated into autodoc core
in Sphinx-3.0
* SphinxTranslator now calls visitor/departure method for super node class if * SphinxTranslator now calls visitor/departure method for super node class if
visitor/departure method for original node class not found visitor/departure method for original node class not found
* #6418: Add new event: :event:`object-description-transform`
Bugs fixed Bugs fixed
---------- ----------
@ -112,7 +119,10 @@ Bugs fixed
* #6559: Wrong node-ids are generated in glossary directive * #6559: Wrong node-ids are generated in glossary directive
* #6986: apidoc: misdetects module name for .so file inside module * #6986: apidoc: misdetects module name for .so file inside module
* #6999: napoleon: fails to parse tilde in :exc: role * #6999: napoleon: fails to parse tilde in :exc: role
* #7019: gettext: Absolute path used in message catalogs
* #7023: autodoc: nested partial functions are not listed * #7023: autodoc: nested partial functions are not listed
* #7023: autodoc: partial functions imported from other modules are listed as
module members without :impoprted-members: option
Testing Testing
-------- --------

View File

@ -222,7 +222,7 @@ connect handlers to the events. Example:
*objtype* arguments are strings indicating object description of the object. *objtype* arguments are strings indicating object description of the object.
And *contentnode* is a content for the object. It can be modified in-place. And *contentnode* is a content for the object. It can be modified in-place.
.. versionadded:: 3.0 .. versionadded:: 2.4
.. event:: doctree-read (app, doctree) .. event:: doctree-read (app, doctree)

View File

@ -92,6 +92,11 @@ The following is a list of deprecated interfaces.
- ``sphinx.util.inspect.signature`` and - ``sphinx.util.inspect.signature`` and
``sphinx.util.inspect.stringify_signature()`` ``sphinx.util.inspect.stringify_signature()``
* - ``sphinx.util.inspect.safe_getmembers()``
- 2.4
- 4.0
- ``inspect.getmembers()``
* - ``sphinx.builders.gettext.POHEADER`` * - ``sphinx.builders.gettext.POHEADER``
- 2.3 - 2.3
- 4.0 - 4.0

View File

@ -599,3 +599,24 @@ member should be included in the documentation by using the following event:
``inherited_members``, ``undoc_members``, ``show_inheritance`` and ``inherited_members``, ``undoc_members``, ``show_inheritance`` and
``noindex`` that are true if the flag option of same name was given to the ``noindex`` that are true if the flag option of same name was given to the
auto directive 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

@ -30,7 +30,7 @@ from sphinx.util import split_index_msg, logging, status_iterator
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
from sphinx.util.i18n import CatalogInfo, docname_to_domain from sphinx.util.i18n import CatalogInfo, docname_to_domain
from sphinx.util.nodes import extract_messages, traverse_translatable_index from sphinx.util.nodes import extract_messages, traverse_translatable_index
from sphinx.util.osutil import ensuredir, canon_path from sphinx.util.osutil import ensuredir, canon_path, relpath
from sphinx.util.tags import Tags from sphinx.util.tags import Tags
from sphinx.util.template import SphinxRenderer from sphinx.util.template import SphinxRenderer
@ -108,7 +108,8 @@ class MsgOrigin:
class GettextRenderer(SphinxRenderer): class GettextRenderer(SphinxRenderer):
def __init__(self, template_path: str = None) -> None: def __init__(self, template_path: str = None, outdir: str = None) -> None:
self.outdir = outdir
if template_path is None: if template_path is None:
template_path = path.join(package_dir, 'templates', 'gettext') template_path = path.join(package_dir, 'templates', 'gettext')
super().__init__(template_path) super().__init__(template_path)
@ -122,6 +123,13 @@ class GettextRenderer(SphinxRenderer):
self.env.filters['e'] = escape self.env.filters['e'] = escape
self.env.filters['escape'] = escape self.env.filters['escape'] = escape
def render(self, filename: str, context: Dict) -> str:
def _relpath(s: str) -> str:
return canon_path(relpath(s, self.outdir))
context['relpath'] = _relpath
return super().render(filename, context)
class I18nTags(Tags): class I18nTags(Tags):
"""Dummy tags module for I18nBuilder. """Dummy tags module for I18nBuilder.
@ -297,7 +305,7 @@ class MessageCatalogBuilder(I18nBuilder):
ensuredir(path.join(self.outdir, path.dirname(textdomain))) ensuredir(path.join(self.outdir, path.dirname(textdomain)))
context['messages'] = list(catalog) context['messages'] = list(catalog)
content = GettextRenderer().render('message.pot_t', context) content = GettextRenderer(outdir=self.outdir).render('message.pot_t', context)
pofn = path.join(self.outdir, textdomain + '.pot') pofn = path.join(self.outdir, textdomain + '.pot')
if should_write(pofn, content): if should_write(pofn, content):

View File

@ -22,7 +22,7 @@ from sphinx.application import Sphinx
from sphinx.config import ENUM from sphinx.config import ENUM
from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.deprecation import RemovedInSphinx40Warning
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
from sphinx.ext.autodoc.importer import import_object, get_object_members from sphinx.ext.autodoc.importer import import_object, get_module_members, get_object_members
from sphinx.ext.autodoc.mock import mock from sphinx.ext.autodoc.mock import mock
from sphinx.locale import _, __ from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError from sphinx.pycode import ModuleAnalyzer, PycodeError
@ -30,9 +30,7 @@ from sphinx.util import inspect
from sphinx.util import logging from sphinx.util import logging
from sphinx.util import rpartition from sphinx.util import rpartition
from sphinx.util.docstrings import extract_metadata, prepare_docstring from sphinx.util.docstrings import extract_metadata, prepare_docstring
from sphinx.util.inspect import ( from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
getdoc, object_description, safe_getattr, safe_getmembers, stringify_signature
)
if False: if False:
# For type annotation # For type annotation
@ -350,10 +348,9 @@ class Documenter:
if self.options.imported_members: if self.options.imported_members:
return True return True
modname = self.get_attr(self.object, '__module__', None) subject = inspect.unpartial(self.object)
if inspect.ispartial(self.object) and modname == '_functools': # for pypy modname = self.get_attr(subject, '__module__', None)
return True if modname and modname != self.modname:
elif modname and modname != self.modname:
return False return False
return True return True
@ -547,7 +544,10 @@ class Documenter:
# process members and determine which to skip # process members and determine which to skip
for (membername, member) in members: for (membername, member) in members:
# if isattr is True, the member is documented as an attribute # if isattr is True, the member is documented as an attribute
isattr = False if member is INSTANCEATTR:
isattr = True
else:
isattr = False
doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings) doc = getdoc(member, self.get_attr, self.env.config.autodoc_inherit_docstrings)
@ -824,7 +824,7 @@ class ModuleDocumenter(Documenter):
hasattr(self.object, '__all__')): hasattr(self.object, '__all__')):
# for implicit module members, check __module__ to avoid # for implicit module members, check __module__ to avoid
# documenting imported objects # documenting imported objects
return True, safe_getmembers(self.object) return True, get_module_members(self.object)
else: else:
memberlist = self.object.__all__ memberlist = self.object.__all__
# Sometimes __all__ is broken... # Sometimes __all__ is broken...
@ -837,7 +837,7 @@ class ModuleDocumenter(Documenter):
type='autodoc' type='autodoc'
) )
# fall back to all members # fall back to all members
return True, safe_getmembers(self.object) return True, get_module_members(self.object)
else: else:
memberlist = self.options.members or [] memberlist = self.options.members or []
ret = [] ret = []
@ -1282,6 +1282,37 @@ class DataDocumenter(ModuleLevelDocumenter):
or self.modname or self.modname
class DataDeclarationDocumenter(DataDocumenter):
"""
Specialized Documenter subclass for data that cannot be imported
because they are declared without initial value (refs: PEP-526).
"""
objtype = 'datadecl'
directivetype = 'data'
member_order = 60
# must be higher than AttributeDocumenter
priority = 11
@classmethod
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool:
"""This documents only INSTANCEATTR members."""
return (isinstance(parent, ModuleDocumenter) and
isattr and
member is INSTANCEATTR)
def import_object(self) -> bool:
"""Never import anything."""
# disguise as a data
self.objtype = 'data'
return True
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
"""Never try to get a docstring from the object."""
super().add_content(more_content, no_docstring=True)
class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore
""" """
Specialized Documenter subclass for methods (normal, static and class). Specialized Documenter subclass for methods (normal, static and class).
@ -1469,7 +1500,9 @@ class InstanceAttributeDocumenter(AttributeDocumenter):
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
) -> bool: ) -> bool:
"""This documents only INSTANCEATTR members.""" """This documents only INSTANCEATTR members."""
return isattr and (member is INSTANCEATTR) return (not isinstance(parent, ModuleDocumenter) and
isattr and
member is INSTANCEATTR)
def import_object(self) -> bool: def import_object(self) -> bool:
"""Never import anything.""" """Never import anything."""
@ -1549,6 +1582,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ClassDocumenter)
app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(ExceptionDocumenter)
app.add_autodocumenter(DataDocumenter) app.add_autodocumenter(DataDocumenter)
app.add_autodocumenter(DataDeclarationDocumenter)
app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(FunctionDocumenter)
app.add_autodocumenter(DecoratorDocumenter) app.add_autodocumenter(DecoratorDocumenter)
app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(MethodDocumenter)

View File

@ -12,7 +12,7 @@ import importlib
import traceback import traceback
import warnings import warnings
from collections import namedtuple from collections import namedtuple
from typing import Any, Callable, Dict, List from typing import Any, Callable, Dict, List, Tuple
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.util import logging from sphinx.util import logging
@ -101,12 +101,35 @@ def import_object(modname: str, objpath: List[str], objtype: str = '',
raise ImportError(errmsg) raise ImportError(errmsg)
def get_module_members(module: Any) -> List[Tuple[str, Any]]:
"""Get members of target module."""
from sphinx.ext.autodoc import INSTANCEATTR
members = {} # type: Dict[str, Tuple[str, Any]]
for name in dir(module):
try:
value = safe_getattr(module, name, None)
members[name] = (name, value)
except AttributeError:
continue
# annotation only member (ex. attr: int)
if hasattr(module, '__annotations__'):
for name in module.__annotations__:
if name not in members:
members[name] = (name, INSTANCEATTR)
return sorted(list(members.values()))
Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value']) Attribute = namedtuple('Attribute', ['name', 'directly_defined', 'value'])
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable, def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
analyzer: Any = None) -> Dict[str, Attribute]: analyzer: Any = None) -> Dict[str, Attribute]:
"""Get members and attributes of target object.""" """Get members and attributes of target object."""
from sphinx.ext.autodoc import INSTANCEATTR
# the members directly defined in the class # the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {}) obj_dict = attrgetter(subject, '__dict__', {})
@ -140,10 +163,14 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
except AttributeError: except AttributeError:
continue continue
# annotation only member (ex. attr: int)
if hasattr(subject, '__annotations__'):
for name in subject.__annotations__:
if name not in members:
members[name] = Attribute(name, True, INSTANCEATTR)
if analyzer: if analyzer:
# append instance attributes (cf. self.attr1) if analyzer knows # append instance attributes (cf. self.attr1) if analyzer knows
from sphinx.ext.autodoc import INSTANCEATTR
namespace = '.'.join(objpath) namespace = '.'.join(objpath)
for (ns, name) in analyzer.find_attr_docs(): for (ns, name) in analyzer.find_attr_docs():
if namespace == ns and name not in members: if namespace == ns and name not in members:

View File

@ -0,0 +1,144 @@
"""
sphinx.ext.autodoc.typehints
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Generating content for autodoc using typehints
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from typing import Any, Dict, Iterable
from typing import cast
from docutils import nodes
from docutils.nodes import Element
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.config import ENUM
from sphinx.util import inspect, typing
def config_inited(app, config):
if config.autodoc_typehints == 'description':
# HACK: override this to make autodoc suppressing typehints in signatures
config.autodoc_typehints = 'none'
# preserve user settings
app._autodoc_typehints_description = True
else:
app._autodoc_typehints_description = False
def record_typehints(app: Sphinx, objtype: str, name: str, obj: Any,
options: Dict, args: str, retann: str) -> None:
"""Record type hints to env object."""
try:
if callable(obj):
annotations = app.env.temp_data.setdefault('annotations', {}).setdefault(name, {})
sig = inspect.signature(obj)
for param in sig.parameters.values():
if param.annotation is not param.empty:
annotations[param.name] = typing.stringify(param.annotation)
if sig.return_annotation is not sig.empty:
annotations['return'] = typing.stringify(sig.return_annotation)
except TypeError:
pass
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
return
signature = cast(addnodes.desc_signature, contentnode.parent[0])
fullname = '.'.join([signature['module'], signature['fullname']])
annotations = app.env.temp_data.get('annotations', {})
if annotations.get(fullname, {}):
field_lists = [n for n in contentnode if isinstance(n, nodes.field_list)]
if field_lists == []:
field_list = insert_field_list(contentnode)
field_lists.append(field_list)
for field_list in field_lists:
modify_field_list(field_list, annotations[fullname])
def insert_field_list(node: Element) -> nodes.field_list:
field_list = nodes.field_list()
desc = [n for n in node if isinstance(n, addnodes.desc)]
if desc:
# insert just before sub object descriptions (ex. methods, nested classes, etc.)
index = node.index(desc[0])
node.insert(index - 1, [field_list])
else:
node += field_list
return field_list
def modify_field_list(node: nodes.field_list, annotations: Dict[str, str]) -> None:
arguments = {} # type: Dict[str, Dict[str, bool]]
fields = cast(Iterable[nodes.field], node)
for field in fields:
field_name = field[0].astext()
parts = re.split(' +', field_name)
if parts[0] == 'param':
if len(parts) == 2:
# :param xxx:
arg = arguments.setdefault(parts[1], {})
arg['param'] = True
elif len(parts) > 2:
# :param xxx yyy:
name = ' '.join(parts[2:])
arg = arguments.setdefault(name, {})
arg['param'] = True
arg['type'] = True
elif parts[0] == 'type':
name = ' '.join(parts[1:])
arg = arguments.setdefault(name, {})
arg['type'] = True
elif parts[0] == 'rtype':
arguments['return'] = {'type': True}
for name, annotation in annotations.items():
if name == 'return':
continue
arg = arguments.get(name, {})
field = nodes.field()
if arg.get('param') and arg.get('type'):
# both param and type are already filled manually
continue
elif arg.get('param'):
# only param: fill type field
field += nodes.field_name('', 'type ' + name)
field += nodes.field_body('', nodes.paragraph('', annotation))
elif arg.get('type'):
# only type: It's odd...
field += nodes.field_name('', 'param ' + name)
field += nodes.field_body('', nodes.paragraph('', ''))
else:
# both param and type are not found
field += nodes.field_name('', 'param ' + annotation + ' ' + name)
field += nodes.field_body('', nodes.paragraph('', ''))
node += field
if 'return' in annotations and 'return' not in arguments:
field = nodes.field()
field += nodes.field_name('', 'rtype')
field += nodes.field_body('', nodes.paragraph('', annotation))
node += field
def setup(app):
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

@ -71,13 +71,13 @@ def setup_documenters(app: Any) -> None:
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter, FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, SlotsAttributeDocumenter, DataDeclarationDocumenter,
) )
documenters = [ documenters = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter, ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter, FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter, InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
SlotsAttributeDocumenter, SlotsAttributeDocumenter, DataDeclarationDocumenter,
] # 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)

View File

@ -394,8 +394,10 @@ class GUILabel(SphinxRole):
class MenuSelection(GUILabel): class MenuSelection(GUILabel):
BULLET_CHARACTER = '\N{TRIANGULAR BULLET}'
def run(self) -> Tuple[List[Node], List[system_message]]: def run(self) -> Tuple[List[Node], List[system_message]]:
self.text = self.text.replace('-->', '\N{TRIANGULAR BULLET}') self.text = self.text.replace('-->', self.BULLET_CHARACTER)
return super().run() return super().run()

View File

@ -18,7 +18,7 @@ msgstr ""
{% for message in messages %} {% for message in messages %}
{% if display_location -%} {% if display_location -%}
{% for source, line in message.locations -%} {% for source, line in message.locations -%}
#: {{ source }}:{{ line }} #: {{ relpath(source) }}:{{ line }}
{% endfor -%} {% endfor -%}
{% endif -%} {% endif -%}

View File

@ -257,6 +257,8 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None, def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]: attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
"""A version of inspect.getmembers() that uses safe_getattr().""" """A version of inspect.getmembers() that uses safe_getattr()."""
warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning)
results = [] # type: List[Tuple[str, Any]] results = [] # type: List[Tuple[str, Any]]
for key in dir(object): for key in dir(object):
try: try:
@ -430,6 +432,8 @@ class Signature:
its return annotation. its return annotation.
""" """
empty = inspect.Signature.empty
def __init__(self, subject: Callable, bound_method: bool = False, def __init__(self, subject: Callable, bound_method: bool = False,
has_retval: bool = True) -> None: has_retval: bool = True) -> None:
warnings.warn('sphinx.util.inspect.Signature() is deprecated', warnings.warn('sphinx.util.inspect.Signature() is deprecated',

View File

@ -7,3 +7,5 @@
.. automodule:: autodoc_dummy_bar .. automodule:: autodoc_dummy_bar
:members: :members:
.. autofunction:: target.typehints.incr

View File

@ -0,0 +1 @@
from .partialfunction import func2, func3

View File

@ -0,0 +1,13 @@
#: attr1
attr1: str = ''
#: attr2
attr2: str
class Class:
attr1: int = 0
attr2: int
def __init__(self):
self.attr3: int = 0 #: attr3
self.attr4: int #: attr4

View File

@ -1291,6 +1291,17 @@ def test_partialfunction():
] ]
@pytest.mark.usefixtures('setup_test')
def test_imported_partialfunction_should_not_shown_without_imported_members():
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.imported_members', options)
assert list(actual) == [
'',
'.. py:module:: target.imported_members',
''
]
@pytest.mark.usefixtures('setup_test') @pytest.mark.usefixtures('setup_test')
def test_bound_method(): def test_bound_method():
options = {"members": None} options = {"members": None}
@ -1401,6 +1412,61 @@ def test_partialmethod_undoc_members(app):
assert list(actual) == expected assert list(actual) == expected
@pytest.mark.skipif(sys.version_info < (3, 6), reason='py36+ is available since python3.6.')
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_typed_instance_variables(app):
options = {"members": None,
"undoc-members": True}
actual = do_autodoc(app, 'module', 'target.typed_vars', options)
assert list(actual) == [
'',
'.. py:module:: target.typed_vars',
'',
'',
'.. py:class:: Class()',
' :module: target.typed_vars',
'',
' ',
' .. py:attribute:: Class.attr1',
' :module: target.typed_vars',
' :annotation: = 0',
' ',
' ',
' .. py:attribute:: Class.attr2',
' :module: target.typed_vars',
' :annotation: = None',
' ',
' ',
' .. py:attribute:: Class.attr3',
' :module: target.typed_vars',
' :annotation: = None',
' ',
' attr3',
' ',
' ',
' .. py:attribute:: Class.attr4',
' :module: target.typed_vars',
' :annotation: = None',
' ',
' attr4',
' ',
'',
'.. py:data:: attr1',
' :module: target.typed_vars',
" :annotation: = ''",
'',
' attr1',
' ',
'',
'.. py:data:: attr2',
' :module: target.typed_vars',
" :annotation: = None",
'',
' attr2',
' '
]
@pytest.mark.sphinx('html', testroot='pycode-egg') @pytest.mark.sphinx('html', testroot='pycode-egg')
def test_autodoc_for_egged_code(app): def test_autodoc_for_egged_code(app):
options = {"members": None, options = {"members": None,

View File

@ -539,6 +539,24 @@ def test_autodoc_typehints_none(app):
] ]
@pytest.mark.sphinx('text', testroot='ext-autodoc',
confoverrides={'extensions': ['sphinx.ext.autodoc.typehints'],
'autodoc_typehints': 'description'})
def test_autodoc_typehints_description(app):
app.build()
context = (app.outdir / 'index.txt').text()
assert ('target.typehints.incr(a, b=1)\n'
'\n'
' Parameters:\n'
' * **a** (*int*) --\n'
'\n'
' * **b** (*int*) --\n'
'\n'
' Return type:\n'
' int\n'
in context)
@pytest.mark.sphinx('html', testroot='ext-autodoc') @pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_default_options(app): def test_autodoc_default_options(app):
# no settings # no settings

View File

@ -1,21 +1,22 @@
[tox] [tox]
minversion = 2.4.0 minversion = 2.4.0
envlist = docs,flake8,mypy,coverage,py{35,36,37,38},du{12,13,14,15} envlist = docs,flake8,mypy,coverage,py{35,36,37,38,39},du{12,13,14,15}
[testenv] [testenv]
usedevelop = True usedevelop = True
passenv = passenv =
https_proxy http_proxy no_proxy PERL PERL5LIB PYTEST_ADDOPTS EPUBCHECK_PATH https_proxy http_proxy no_proxy PERL PERL5LIB PYTEST_ADDOPTS EPUBCHECK_PATH
description = description =
py{35,36,37,38}: Run unit tests against {envname}. py{35,36,37,38,39}: Run unit tests against {envname}.
du{12,13,14}: Run unit tests with the given version of docutils. du{12,13,14}: Run unit tests with the given version of docutils.
deps = deps =
coverage < 5.0 # refs: https://github.com/sphinx-doc/sphinx/pull/6924 coverage < 5.0 # refs: https://github.com/sphinx-doc/sphinx/pull/6924
git+https://github.com/html5lib/html5lib-python ; python_version >= "3.9" # refs: https://github.com/html5lib/html5lib-python/issues/419
du12: docutils==0.12 du12: docutils==0.12
du13: docutils==0.13.1 du13: docutils==0.13.1
du14: docutils==0.14 du14: docutils==0.14
du15: docutils==0.15 du15: docutils==0.15
du16: docutils==0.16rc1 du16: docutils==0.16
extras = extras =
test test
setenv = setenv =