summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2020-01-30 23:29:32 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2020-01-30 23:29:32 +0900
commite7a0af807a58f97be826c19644fac72b50fb6769 (patch)
tree2ae84f05016687286e5849739ab9e035e49c40d9
parenta2415706c849e0405d4fd37fbbb0577056342eea (diff)
parent0f8a868bf738342194d07f2edcc866001590fd63 (diff)
downloadsphinx-git-e7a0af807a58f97be826c19644fac72b50fb6769.tar.gz
Merge branch '2.0'
-rw-r--r--.travis.yml2
-rw-r--r--CHANGES10
-rw-r--r--doc/extdev/appapi.rst2
-rw-r--r--doc/extdev/deprecated.rst5
-rw-r--r--doc/usage/extensions/autodoc.rst21
-rw-r--r--sphinx/builders/gettext.py14
-rw-r--r--sphinx/ext/autodoc/__init__.py58
-rw-r--r--sphinx/ext/autodoc/importer.py33
-rw-r--r--sphinx/ext/autodoc/typehints.py144
-rw-r--r--sphinx/ext/autosummary/generate.py4
-rw-r--r--sphinx/roles.py4
-rw-r--r--sphinx/templates/gettext/message.pot_t2
-rw-r--r--sphinx/util/inspect.py4
-rw-r--r--tests/roots/test-ext-autodoc/index.rst2
-rw-r--r--tests/roots/test-ext-autodoc/target/imported_members.py1
-rw-r--r--tests/roots/test-ext-autodoc/target/typed_vars.py13
-rw-r--r--tests/test_autodoc.py66
-rw-r--r--tests/test_ext_autodoc_configs.py18
-rw-r--r--tox.ini7
19 files changed, 383 insertions, 27 deletions
diff --git a/.travis.yml b/.travis.yml
index 046efc35a..008f4e442 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,7 +24,7 @@ matrix:
env:
- TOXENV=du15
- PYTEST_ADDOPTS="--cov ./ --cov-append --cov-config setup.cfg"
- - python: '3.8'
+ - python: 'nightly'
env:
- TOXENV=du16
- python: '3.6'
diff --git a/CHANGES b/CHANGES
index 5efe16e69..f7c7db5d3 100644
--- a/CHANGES
+++ b/CHANGES
@@ -83,6 +83,7 @@ Deprecated
* ``sphinx.util.detect_encoding()``
* ``sphinx.util.get_module_source()``
* ``sphinx.util.inspect.Signature``
+* ``sphinx.util.inspect.safe_getmembers()``
Features added
--------------
@@ -100,8 +101,14 @@ Features added
* #2755: autodoc: Support type_comment style (ex. ``# type: (str) -> str``)
annotation (python3.8+ or `typed_ast <https://github.com/python/typed_ast>`_
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
visitor/departure method for original node class not found
+* #6418: Add new event: :event:`object-description-transform`
Bugs fixed
----------
@@ -112,7 +119,10 @@ Bugs fixed
* #6559: Wrong node-ids are generated in glossary directive
* #6986: apidoc: misdetects module name for .so file inside module
* #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: partial functions imported from other modules are listed as
+ module members without :impoprted-members: option
Testing
--------
diff --git a/doc/extdev/appapi.rst b/doc/extdev/appapi.rst
index c32eb1427..76ad65a88 100644
--- a/doc/extdev/appapi.rst
+++ b/doc/extdev/appapi.rst
@@ -222,7 +222,7 @@ connect handlers to the events. Example:
*objtype* arguments are strings indicating object description of the object.
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)
diff --git a/doc/extdev/deprecated.rst b/doc/extdev/deprecated.rst
index f6ebc3916..518f5d973 100644
--- a/doc/extdev/deprecated.rst
+++ b/doc/extdev/deprecated.rst
@@ -92,6 +92,11 @@ The following is a list of deprecated interfaces.
- ``sphinx.util.inspect.signature`` and
``sphinx.util.inspect.stringify_signature()``
+ * - ``sphinx.util.inspect.safe_getmembers()``
+ - 2.4
+ - 4.0
+ - ``inspect.getmembers()``
+
* - ``sphinx.builders.gettext.POHEADER``
- 2.3
- 4.0
diff --git a/doc/usage/extensions/autodoc.rst b/doc/usage/extensions/autodoc.rst
index 6ca148706..dbc1abed0 100644
--- a/doc/usage/extensions/autodoc.rst
+++ b/doc/usage/extensions/autodoc.rst
@@ -599,3 +599,24 @@ 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.
diff --git a/sphinx/builders/gettext.py b/sphinx/builders/gettext.py
index 638408503..65f112510 100644
--- a/sphinx/builders/gettext.py
+++ b/sphinx/builders/gettext.py
@@ -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.i18n import CatalogInfo, docname_to_domain
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.template import SphinxRenderer
@@ -108,7 +108,8 @@ class MsgOrigin:
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:
template_path = path.join(package_dir, 'templates', 'gettext')
super().__init__(template_path)
@@ -122,6 +123,13 @@ class GettextRenderer(SphinxRenderer):
self.env.filters['e'] = 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):
"""Dummy tags module for I18nBuilder.
@@ -297,7 +305,7 @@ class MessageCatalogBuilder(I18nBuilder):
ensuredir(path.join(self.outdir, path.dirname(textdomain)))
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')
if should_write(pofn, content):
diff --git a/sphinx/ext/autodoc/__init__.py b/sphinx/ext/autodoc/__init__.py
index 58c9897a9..8333edc47 100644
--- a/sphinx/ext/autodoc/__init__.py
+++ b/sphinx/ext/autodoc/__init__.py
@@ -22,7 +22,7 @@ from sphinx.application import Sphinx
from sphinx.config import ENUM
from sphinx.deprecation import RemovedInSphinx40Warning
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.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError
@@ -30,9 +30,7 @@ from sphinx.util import inspect
from sphinx.util import logging
from sphinx.util import rpartition
from sphinx.util.docstrings import extract_metadata, prepare_docstring
-from sphinx.util.inspect import (
- getdoc, object_description, safe_getattr, safe_getmembers, stringify_signature
-)
+from sphinx.util.inspect import getdoc, object_description, safe_getattr, stringify_signature
if False:
# For type annotation
@@ -350,10 +348,9 @@ class Documenter:
if self.options.imported_members:
return True
- modname = self.get_attr(self.object, '__module__', None)
- if inspect.ispartial(self.object) and modname == '_functools': # for pypy
- return True
- elif modname and modname != self.modname:
+ subject = inspect.unpartial(self.object)
+ modname = self.get_attr(subject, '__module__', None)
+ if modname and modname != self.modname:
return False
return True
@@ -547,7 +544,10 @@ class Documenter:
# process members and determine which to skip
for (membername, member) in members:
# 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)
@@ -824,7 +824,7 @@ class ModuleDocumenter(Documenter):
hasattr(self.object, '__all__')):
# for implicit module members, check __module__ to avoid
# documenting imported objects
- return True, safe_getmembers(self.object)
+ return True, get_module_members(self.object)
else:
memberlist = self.object.__all__
# Sometimes __all__ is broken...
@@ -837,7 +837,7 @@ class ModuleDocumenter(Documenter):
type='autodoc'
)
# fall back to all members
- return True, safe_getmembers(self.object)
+ return True, get_module_members(self.object)
else:
memberlist = self.options.members or []
ret = []
@@ -1282,6 +1282,37 @@ class DataDocumenter(ModuleLevelDocumenter):
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
"""
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
) -> bool:
"""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:
"""Never import anything."""
@@ -1549,6 +1582,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_autodocumenter(ClassDocumenter)
app.add_autodocumenter(ExceptionDocumenter)
app.add_autodocumenter(DataDocumenter)
+ app.add_autodocumenter(DataDeclarationDocumenter)
app.add_autodocumenter(FunctionDocumenter)
app.add_autodocumenter(DecoratorDocumenter)
app.add_autodocumenter(MethodDocumenter)
diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py
index cf0e3ea35..3d4fb1805 100644
--- a/sphinx/ext/autodoc/importer.py
+++ b/sphinx/ext/autodoc/importer.py
@@ -12,7 +12,7 @@ import importlib
import traceback
import warnings
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.util import logging
@@ -101,12 +101,35 @@ def import_object(modname: str, objpath: List[str], objtype: str = '',
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'])
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
analyzer: Any = None) -> Dict[str, Attribute]:
"""Get members and attributes of target object."""
+ from sphinx.ext.autodoc import INSTANCEATTR
+
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
@@ -140,10 +163,14 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
except AttributeError:
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:
# append instance attributes (cf. self.attr1) if analyzer knows
- from sphinx.ext.autodoc import INSTANCEATTR
-
namespace = '.'.join(objpath)
for (ns, name) in analyzer.find_attr_docs():
if namespace == ns and name not in members:
diff --git a/sphinx/ext/autodoc/typehints.py b/sphinx/ext/autodoc/typehints.py
new file mode 100644
index 000000000..3eb845685
--- /dev/null
+++ b/sphinx/ext/autodoc/typehints.py
@@ -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)
diff --git a/sphinx/ext/autosummary/generate.py b/sphinx/ext/autosummary/generate.py
index b42d80544..7c29d41e8 100644
--- a/sphinx/ext/autosummary/generate.py
+++ b/sphinx/ext/autosummary/generate.py
@@ -71,13 +71,13 @@ def setup_documenters(app: Any) -> None:
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
- SlotsAttributeDocumenter,
+ SlotsAttributeDocumenter, DataDeclarationDocumenter,
)
documenters = [
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
- SlotsAttributeDocumenter,
+ SlotsAttributeDocumenter, DataDeclarationDocumenter,
] # type: List[Type[Documenter]]
for documenter in documenters:
app.registry.add_documenter(documenter.objtype, documenter)
diff --git a/sphinx/roles.py b/sphinx/roles.py
index 22f9e6124..a42a5610d 100644
--- a/sphinx/roles.py
+++ b/sphinx/roles.py
@@ -394,8 +394,10 @@ class GUILabel(SphinxRole):
class MenuSelection(GUILabel):
+ BULLET_CHARACTER = '\N{TRIANGULAR BULLET}'
+
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()
diff --git a/sphinx/templates/gettext/message.pot_t b/sphinx/templates/gettext/message.pot_t
index 6bec78729..6138f54c9 100644
--- a/sphinx/templates/gettext/message.pot_t
+++ b/sphinx/templates/gettext/message.pot_t
@@ -18,7 +18,7 @@ msgstr ""
{% for message in messages %}
{% if display_location -%}
{% for source, line in message.locations -%}
-#: {{ source }}:{{ line }}
+#: {{ relpath(source) }}:{{ line }}
{% endfor -%}
{% endif -%}
diff --git a/sphinx/util/inspect.py b/sphinx/util/inspect.py
index 16f153ff8..af8102402 100644
--- a/sphinx/util/inspect.py
+++ b/sphinx/util/inspect.py
@@ -257,6 +257,8 @@ def safe_getattr(obj: Any, name: str, *defargs: Any) -> Any:
def safe_getmembers(object: Any, predicate: Callable[[str], bool] = None,
attr_getter: Callable = safe_getattr) -> List[Tuple[str, Any]]:
"""A version of inspect.getmembers() that uses safe_getattr()."""
+ warnings.warn('safe_getmembers() is deprecated', RemovedInSphinx40Warning)
+
results = [] # type: List[Tuple[str, Any]]
for key in dir(object):
try:
@@ -430,6 +432,8 @@ class Signature:
its return annotation.
"""
+ empty = inspect.Signature.empty
+
def __init__(self, subject: Callable, bound_method: bool = False,
has_retval: bool = True) -> None:
warnings.warn('sphinx.util.inspect.Signature() is deprecated',
diff --git a/tests/roots/test-ext-autodoc/index.rst b/tests/roots/test-ext-autodoc/index.rst
index ce4302204..1a60fc281 100644
--- a/tests/roots/test-ext-autodoc/index.rst
+++ b/tests/roots/test-ext-autodoc/index.rst
@@ -7,3 +7,5 @@
.. automodule:: autodoc_dummy_bar
:members:
+
+.. autofunction:: target.typehints.incr
diff --git a/tests/roots/test-ext-autodoc/target/imported_members.py b/tests/roots/test-ext-autodoc/target/imported_members.py
new file mode 100644
index 000000000..ee6e5b387
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/imported_members.py
@@ -0,0 +1 @@
+from .partialfunction import func2, func3
diff --git a/tests/roots/test-ext-autodoc/target/typed_vars.py b/tests/roots/test-ext-autodoc/target/typed_vars.py
new file mode 100644
index 000000000..4a9a6f7b5
--- /dev/null
+++ b/tests/roots/test-ext-autodoc/target/typed_vars.py
@@ -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
diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py
index 71ddb6624..6fb4db6ad 100644
--- a/tests/test_autodoc.py
+++ b/tests/test_autodoc.py
@@ -1292,6 +1292,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')
def test_bound_method():
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.bound_method', options)
@@ -1401,6 +1412,61 @@ def test_partialmethod_undoc_members(app):
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')
def test_autodoc_for_egged_code(app):
options = {"members": None,
diff --git a/tests/test_ext_autodoc_configs.py b/tests/test_ext_autodoc_configs.py
index 3913bfac4..bb4ad61b8 100644
--- a/tests/test_ext_autodoc_configs.py
+++ b/tests/test_ext_autodoc_configs.py
@@ -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')
def test_autodoc_default_options(app):
# no settings
diff --git a/tox.ini b/tox.ini
index 0ae042b3f..1ac0bff79 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,21 +1,22 @@
[tox]
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]
usedevelop = True
passenv =
https_proxy http_proxy no_proxy PERL PERL5LIB PYTEST_ADDOPTS EPUBCHECK_PATH
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.
deps =
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
du13: docutils==0.13.1
du14: docutils==0.14
du15: docutils==0.15
- du16: docutils==0.16rc1
+ du16: docutils==0.16
extras =
test
setenv =