mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '2.0'
This commit is contained in:
commit
e7a0af807a
@ -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
10
CHANGES
@ -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
|
||||||
--------
|
--------
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
144
sphinx/ext/autodoc/typehints.py
Normal file
144
sphinx/ext/autodoc/typehints.py
Normal 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)
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 -%}
|
||||||
|
|
||||||
|
@ -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',
|
||||||
|
@ -7,3 +7,5 @@
|
|||||||
|
|
||||||
.. automodule:: autodoc_dummy_bar
|
.. automodule:: autodoc_dummy_bar
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
.. autofunction:: target.typehints.incr
|
||||||
|
1
tests/roots/test-ext-autodoc/target/imported_members.py
Normal file
1
tests/roots/test-ext-autodoc/target/imported_members.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
from .partialfunction import func2, func3
|
13
tests/roots/test-ext-autodoc/target/typed_vars.py
Normal file
13
tests/roots/test-ext-autodoc/target/typed_vars.py
Normal 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
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
7
tox.ini
7
tox.ini
@ -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 =
|
||||||
|
Loading…
Reference in New Issue
Block a user