mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 7701_anonymous_indirect_target
This commit is contained in:
commit
28fe0d6399
21
CHANGES
21
CHANGES
@ -45,6 +45,8 @@ Features added
|
|||||||
* #7487: autodoc: Allow to generate docs for singledispatch functions by
|
* #7487: autodoc: Allow to generate docs for singledispatch functions by
|
||||||
py:autofunction
|
py:autofunction
|
||||||
* #7143: autodoc: Support final classes and methods
|
* #7143: autodoc: Support final classes and methods
|
||||||
|
* #7384: autodoc: Support signatures defined by ``__new__()``, metaclasses and
|
||||||
|
builtin base classes
|
||||||
* #7466: autosummary: headings in generated documents are not translated
|
* #7466: autosummary: headings in generated documents are not translated
|
||||||
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
* #7490: autosummary: Add ``:caption:`` option to autosummary directive to set a
|
||||||
caption to the toctree
|
caption to the toctree
|
||||||
@ -75,6 +77,7 @@ Features added
|
|||||||
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
:rst:dir:`py:exception:` and :rst:dir:`py:method:` directives
|
||||||
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
* #7596: py domain: Change a type annotation for variables to a hyperlink
|
||||||
* #7582: napoleon: a type for attribute are represented like type annotation
|
* #7582: napoleon: a type for attribute are represented like type annotation
|
||||||
|
* #7734: napoleon: overescaped trailing underscore on attribute
|
||||||
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
* #7683: Add ``allowed_exceptions`` parameter to ``Sphinx.emit()`` to allow
|
||||||
handlers to raise specified exceptions
|
handlers to raise specified exceptions
|
||||||
|
|
||||||
@ -100,8 +103,11 @@ Bugs fixed
|
|||||||
* #7676: autodoc: typo in the default value of autodoc_member_order
|
* #7676: autodoc: typo in the default value of autodoc_member_order
|
||||||
* #7676: autodoc: wrong value for :member-order: option is ignored silently
|
* #7676: autodoc: wrong value for :member-order: option is ignored silently
|
||||||
* #7676: autodoc: member-order="bysource" does not work for C module
|
* #7676: autodoc: member-order="bysource" does not work for C module
|
||||||
|
* #3673: autodoc: member-order="bysource" does not work for a module having
|
||||||
|
__all__
|
||||||
* #7668: autodoc: wrong retann value is passed to a handler of
|
* #7668: autodoc: wrong retann value is passed to a handler of
|
||||||
autodoc-proccess-signature
|
autodoc-proccess-signature
|
||||||
|
* #7711: autodoc: fails with ValueError when processing numpy objects
|
||||||
* #7551: autosummary: a nested class is indexed as non-nested class
|
* #7551: autosummary: a nested class is indexed as non-nested class
|
||||||
* #7661: autosummary: autosummary directive emits warnings twices if failed to
|
* #7661: autosummary: autosummary directive emits warnings twices if failed to
|
||||||
import the target module
|
import the target module
|
||||||
@ -117,6 +123,7 @@ Bugs fixed
|
|||||||
supporting images
|
supporting images
|
||||||
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
* #7610: incorrectly renders consecutive backslashes for docutils-0.16
|
||||||
* #7646: handle errors on event handlers
|
* #7646: handle errors on event handlers
|
||||||
|
* #4187: LaTeX: EN DASH disappears from PDF bookmarks in Japanese documents
|
||||||
* #7701: LaTeX: Anonymous indirect hyperlink target causes duplicated labels
|
* #7701: LaTeX: Anonymous indirect hyperlink target causes duplicated labels
|
||||||
* C++, fix rendering and xrefs in nested names explicitly starting
|
* C++, fix rendering and xrefs in nested names explicitly starting
|
||||||
in global scope, e.g., ``::A::B``.
|
in global scope, e.g., ``::A::B``.
|
||||||
@ -126,7 +133,7 @@ Bugs fixed
|
|||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Release 3.0.4 (in development)
|
Release 3.0.5 (in development)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@ -144,15 +151,21 @@ Features added
|
|||||||
Bugs fixed
|
Bugs fixed
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
Testing
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 3.0.4 (released May 27, 2020)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
* #7567: autodoc: parametrized types are shown twice for generic types
|
* #7567: autodoc: parametrized types are shown twice for generic types
|
||||||
* #7637: autodoc: system defined TypeVars are shown in Python 3.9
|
* #7637: autodoc: system defined TypeVars are shown in Python 3.9
|
||||||
* #7696: html: Updated jQuery version from 3.4.1 to 3.5.1 for security reasons
|
* #7696: html: Updated jQuery version from 3.4.1 to 3.5.1 for security reasons
|
||||||
* #7611: md5 fails when OpenSSL FIPS is enabled
|
* #7611: md5 fails when OpenSSL FIPS is enabled
|
||||||
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
* #7626: release package does not contain ``CODE_OF_CONDUCT``
|
||||||
|
|
||||||
Testing
|
|
||||||
--------
|
|
||||||
|
|
||||||
Release 3.0.3 (released Apr 26, 2020)
|
Release 3.0.3 (released Apr 26, 2020)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@ General configuration
|
|||||||
A dictionary of options that modify how the lexer specified by
|
A dictionary of options that modify how the lexer specified by
|
||||||
:confval:`highlight_language` generates highlighted source code. These are
|
:confval:`highlight_language` generates highlighted source code. These are
|
||||||
lexer-specific; for the options understood by each, see the
|
lexer-specific; for the options understood by each, see the
|
||||||
`Pygments documentation <https://pygments.org/docs/lexers.html>`_.
|
`Pygments documentation <https://pygments.org/docs/lexers>`_.
|
||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
|
|
||||||
|
1
setup.py
1
setup.py
@ -202,6 +202,7 @@ setup(
|
|||||||
'Programming Language :: Python :: 3.5',
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7',
|
||||||
|
'Programming Language :: Python :: 3.8',
|
||||||
'Programming Language :: Python :: Implementation :: CPython',
|
'Programming Language :: Python :: Implementation :: CPython',
|
||||||
'Programming Language :: Python :: Implementation :: PyPy',
|
'Programming Language :: Python :: Implementation :: PyPy',
|
||||||
'Framework :: Setuptools Plugin',
|
'Framework :: Setuptools Plugin',
|
||||||
|
@ -968,12 +968,14 @@ class Sphinx:
|
|||||||
|
|
||||||
self.add_css_file(filename, **attributes)
|
self.add_css_file(filename, **attributes)
|
||||||
|
|
||||||
def add_latex_package(self, packagename: str, options: str = None) -> None:
|
def add_latex_package(self, packagename: str, options: str = None,
|
||||||
|
after_hyperref: bool = False) -> None:
|
||||||
r"""Register a package to include in the LaTeX source code.
|
r"""Register a package to include in the LaTeX source code.
|
||||||
|
|
||||||
Add *packagename* to the list of packages that LaTeX source code will
|
Add *packagename* to the list of packages that LaTeX source code will
|
||||||
include. If you provide *options*, it will be taken to `\usepackage`
|
include. If you provide *options*, it will be taken to `\usepackage`
|
||||||
declaration.
|
declaration. If you set *after_hyperref* truthy, the package will be
|
||||||
|
loaded after ``hyperref`` package.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -983,8 +985,11 @@ class Sphinx:
|
|||||||
# => \usepackage[foo,bar]{mypackage}
|
# => \usepackage[foo,bar]{mypackage}
|
||||||
|
|
||||||
.. versionadded:: 1.3
|
.. versionadded:: 1.3
|
||||||
|
.. versionadded:: 3.1
|
||||||
|
|
||||||
|
*after_hyperref* option.
|
||||||
"""
|
"""
|
||||||
self.registry.add_latex_package(packagename, options)
|
self.registry.add_latex_package(packagename, options, after_hyperref)
|
||||||
|
|
||||||
def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None:
|
def add_lexer(self, alias: str, lexer: Union[Lexer, "Type[Lexer]"]) -> None:
|
||||||
"""Register a new lexer for source code.
|
"""Register a new lexer for source code.
|
||||||
|
@ -129,6 +129,7 @@ class LaTeXBuilder(Builder):
|
|||||||
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
|
self.document_data = [] # type: List[Tuple[str, str, str, str, str, bool]]
|
||||||
self.themes = ThemeFactory(self.app)
|
self.themes = ThemeFactory(self.app)
|
||||||
self.usepackages = self.app.registry.latex_packages
|
self.usepackages = self.app.registry.latex_packages
|
||||||
|
self.usepackages_after_hyperref = self.app.registry.latex_packages_after_hyperref
|
||||||
texescape.init()
|
texescape.init()
|
||||||
|
|
||||||
self.init_context()
|
self.init_context()
|
||||||
@ -180,6 +181,7 @@ class LaTeXBuilder(Builder):
|
|||||||
|
|
||||||
# Apply extension settings to context
|
# Apply extension settings to context
|
||||||
self.context['packages'] = self.usepackages
|
self.context['packages'] = self.usepackages
|
||||||
|
self.context['packages_after_hyperref'] = self.usepackages_after_hyperref
|
||||||
|
|
||||||
# Apply user settings to context
|
# Apply user settings to context
|
||||||
self.context.update(self.config.latex_elements)
|
self.context.update(self.config.latex_elements)
|
||||||
@ -501,6 +503,12 @@ def validate_latex_theme_options(app: Sphinx, config: Config) -> None:
|
|||||||
config.latex_theme_options.pop(key)
|
config.latex_theme_options.pop(key)
|
||||||
|
|
||||||
|
|
||||||
|
def install_pakcages_for_ja(app: Sphinx) -> None:
|
||||||
|
"""Install packages for Japanese."""
|
||||||
|
if app.config.language == 'ja':
|
||||||
|
app.add_latex_package('pxjahyper', after_hyperref=True)
|
||||||
|
|
||||||
|
|
||||||
def default_latex_engine(config: Config) -> str:
|
def default_latex_engine(config: Config) -> str:
|
||||||
""" Better default latex_engine settings for specific languages. """
|
""" Better default latex_engine settings for specific languages. """
|
||||||
if config.language == 'ja':
|
if config.language == 'ja':
|
||||||
@ -548,6 +556,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_builder(LaTeXBuilder)
|
app.add_builder(LaTeXBuilder)
|
||||||
app.connect('config-inited', validate_config_values, priority=800)
|
app.connect('config-inited', validate_config_values, priority=800)
|
||||||
app.connect('config-inited', validate_latex_theme_options, priority=800)
|
app.connect('config-inited', validate_latex_theme_options, priority=800)
|
||||||
|
app.connect('builder-inited', install_pakcages_for_ja)
|
||||||
|
|
||||||
app.add_config_value('latex_engine', default_latex_engine, None,
|
app.add_config_value('latex_engine', default_latex_engine, None,
|
||||||
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
|
ENUM('pdflatex', 'xelatex', 'lualatex', 'platex', 'uplatex'))
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from inspect import Parameter
|
from inspect import Parameter, Signature
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
Any, Callable, Dict, Iterator, List, Optional, Sequence, Set, Tuple, Type, Union
|
||||||
@ -392,6 +392,17 @@ class Documenter:
|
|||||||
# directives of course)
|
# directives of course)
|
||||||
return '.'.join(self.objpath) or self.modname
|
return '.'.join(self.objpath) or self.modname
|
||||||
|
|
||||||
|
def _call_format_args(self, **kwargs: Any) -> str:
|
||||||
|
if kwargs:
|
||||||
|
try:
|
||||||
|
return self.format_args(**kwargs)
|
||||||
|
except TypeError:
|
||||||
|
# avoid chaining exceptions, by putting nothing here
|
||||||
|
pass
|
||||||
|
|
||||||
|
# retry without arguments for old documenters
|
||||||
|
return self.format_args()
|
||||||
|
|
||||||
def format_signature(self, **kwargs: Any) -> str:
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
"""Format the signature (arguments and return annotation) of the object.
|
"""Format the signature (arguments and return annotation) of the object.
|
||||||
|
|
||||||
@ -405,12 +416,7 @@ class Documenter:
|
|||||||
# try to introspect the signature
|
# try to introspect the signature
|
||||||
try:
|
try:
|
||||||
retann = None
|
retann = None
|
||||||
try:
|
args = self._call_format_args(**kwargs)
|
||||||
args = self.format_args(**kwargs)
|
|
||||||
except TypeError:
|
|
||||||
# retry without arguments for old documenters
|
|
||||||
args = self.format_args()
|
|
||||||
|
|
||||||
if args:
|
if args:
|
||||||
matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args)
|
matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args)
|
||||||
if matched:
|
if matched:
|
||||||
@ -714,29 +720,9 @@ class Documenter:
|
|||||||
'.'.join(self.objpath + [mname])
|
'.'.join(self.objpath + [mname])
|
||||||
documenter = classes[-1](self.directive, full_mname, self.indent)
|
documenter = classes[-1](self.directive, full_mname, self.indent)
|
||||||
memberdocumenters.append((documenter, isattr))
|
memberdocumenters.append((documenter, isattr))
|
||||||
member_order = self.options.member_order or \
|
|
||||||
self.env.config.autodoc_member_order
|
|
||||||
if member_order == 'groupwise':
|
|
||||||
# sort by group; alphabetically within groups
|
|
||||||
memberdocumenters.sort(key=lambda e: (e[0].member_order, e[0].name))
|
|
||||||
elif member_order == 'bysource':
|
|
||||||
if self.analyzer:
|
|
||||||
# sort by source order, by virtue of the module analyzer
|
|
||||||
tagorder = self.analyzer.tagorder
|
|
||||||
|
|
||||||
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
|
member_order = self.options.member_order or self.env.config.autodoc_member_order
|
||||||
fullname = entry[0].name.split('::')[1]
|
memberdocumenters = self.sort_members(memberdocumenters, member_order)
|
||||||
return tagorder.get(fullname, len(tagorder))
|
|
||||||
memberdocumenters.sort(key=keyfunc)
|
|
||||||
else:
|
|
||||||
# Assume that member discovery order matches source order.
|
|
||||||
# This is a reasonable assumption in Python 3.6 and up, where
|
|
||||||
# module.__dict__ is insertion-ordered.
|
|
||||||
pass
|
|
||||||
elif member_order == 'alphabetical':
|
|
||||||
memberdocumenters.sort(key=lambda e: e[0].name)
|
|
||||||
else:
|
|
||||||
raise ValueError("Illegal member order {}".format(member_order))
|
|
||||||
|
|
||||||
for documenter, isattr in memberdocumenters:
|
for documenter, isattr in memberdocumenters:
|
||||||
documenter.generate(
|
documenter.generate(
|
||||||
@ -747,6 +733,31 @@ class Documenter:
|
|||||||
self.env.temp_data['autodoc:module'] = None
|
self.env.temp_data['autodoc:module'] = None
|
||||||
self.env.temp_data['autodoc:class'] = None
|
self.env.temp_data['autodoc:class'] = None
|
||||||
|
|
||||||
|
def sort_members(self, documenters: List[Tuple["Documenter", bool]],
|
||||||
|
order: str) -> List[Tuple["Documenter", bool]]:
|
||||||
|
"""Sort the given member list."""
|
||||||
|
if order == 'groupwise':
|
||||||
|
# sort by group; alphabetically within groups
|
||||||
|
documenters.sort(key=lambda e: (e[0].member_order, e[0].name))
|
||||||
|
elif order == 'bysource':
|
||||||
|
if self.analyzer:
|
||||||
|
# sort by source order, by virtue of the module analyzer
|
||||||
|
tagorder = self.analyzer.tagorder
|
||||||
|
|
||||||
|
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
|
||||||
|
fullname = entry[0].name.split('::')[1]
|
||||||
|
return tagorder.get(fullname, len(tagorder))
|
||||||
|
documenters.sort(key=keyfunc)
|
||||||
|
else:
|
||||||
|
# Assume that member discovery order matches source order.
|
||||||
|
# This is a reasonable assumption in Python 3.6 and up, where
|
||||||
|
# module.__dict__ is insertion-ordered.
|
||||||
|
pass
|
||||||
|
else: # alphabetical
|
||||||
|
documenters.sort(key=lambda e: e[0].name)
|
||||||
|
|
||||||
|
return documenters
|
||||||
|
|
||||||
def generate(self, more_content: Any = None, real_modname: str = None,
|
def generate(self, more_content: Any = None, real_modname: str = None,
|
||||||
check_module: bool = False, all_members: bool = False) -> None:
|
check_module: bool = False, all_members: bool = False) -> None:
|
||||||
"""Generate reST for the object given by *self.name*, and possibly for
|
"""Generate reST for the object given by *self.name*, and possibly for
|
||||||
@ -852,6 +863,7 @@ class ModuleDocumenter(Documenter):
|
|||||||
def __init__(self, *args: Any) -> None:
|
def __init__(self, *args: Any) -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
merge_special_members_option(self.options)
|
merge_special_members_option(self.options)
|
||||||
|
self.__all__ = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
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
|
||||||
@ -874,6 +886,30 @@ class ModuleDocumenter(Documenter):
|
|||||||
type='autodoc')
|
type='autodoc')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def import_object(self) -> Any:
|
||||||
|
def is_valid_module_all(__all__: Any) -> bool:
|
||||||
|
"""Check the given *__all__* is valid for a module."""
|
||||||
|
if (isinstance(__all__, (list, tuple)) and
|
||||||
|
all(isinstance(e, str) for e in __all__)):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
ret = super().import_object()
|
||||||
|
|
||||||
|
if not self.options.ignore_module_all:
|
||||||
|
__all__ = getattr(self.object, '__all__', None)
|
||||||
|
if is_valid_module_all(__all__):
|
||||||
|
# valid __all__ found. copy it to self.__all__
|
||||||
|
self.__all__ = __all__
|
||||||
|
elif __all__:
|
||||||
|
# invalid __all__ found.
|
||||||
|
logger.warning(__('__all__ should be a list of strings, not %r '
|
||||||
|
'(in module %s) -- ignoring __all__') %
|
||||||
|
(__all__, self.fullname), type='autodoc')
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
Documenter.add_directive_header(self, sig)
|
Documenter.add_directive_header(self, sig)
|
||||||
|
|
||||||
@ -889,24 +925,12 @@ class ModuleDocumenter(Documenter):
|
|||||||
|
|
||||||
def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]:
|
def get_object_members(self, want_all: bool) -> Tuple[bool, List[Tuple[str, Any]]]:
|
||||||
if want_all:
|
if want_all:
|
||||||
if (self.options.ignore_module_all or not
|
if self.__all__:
|
||||||
hasattr(self.object, '__all__')):
|
memberlist = self.__all__
|
||||||
|
else:
|
||||||
# for implicit module members, check __module__ to avoid
|
# for implicit module members, check __module__ to avoid
|
||||||
# documenting imported objects
|
# documenting imported objects
|
||||||
return True, get_module_members(self.object)
|
return True, get_module_members(self.object)
|
||||||
else:
|
|
||||||
memberlist = self.object.__all__
|
|
||||||
# Sometimes __all__ is broken...
|
|
||||||
if not isinstance(memberlist, (list, tuple)) or not \
|
|
||||||
all(isinstance(entry, str) for entry in memberlist):
|
|
||||||
logger.warning(
|
|
||||||
__('__all__ should be a list of strings, not %r '
|
|
||||||
'(in module %s) -- ignoring __all__') %
|
|
||||||
(memberlist, self.fullname),
|
|
||||||
type='autodoc'
|
|
||||||
)
|
|
||||||
# fall back to all members
|
|
||||||
return True, get_module_members(self.object)
|
|
||||||
else:
|
else:
|
||||||
memberlist = self.options.members or []
|
memberlist = self.options.members or []
|
||||||
ret = []
|
ret = []
|
||||||
@ -922,6 +946,25 @@ class ModuleDocumenter(Documenter):
|
|||||||
)
|
)
|
||||||
return False, ret
|
return False, ret
|
||||||
|
|
||||||
|
def sort_members(self, documenters: List[Tuple["Documenter", bool]],
|
||||||
|
order: str) -> List[Tuple["Documenter", bool]]:
|
||||||
|
if order == 'bysource' and self.__all__:
|
||||||
|
# Sort alphabetically first (for members not listed on the __all__)
|
||||||
|
documenters.sort(key=lambda e: e[0].name)
|
||||||
|
|
||||||
|
# Sort by __all__
|
||||||
|
def keyfunc(entry: Tuple[Documenter, bool]) -> int:
|
||||||
|
name = entry[0].name.split('::')[1]
|
||||||
|
if name in self.__all__:
|
||||||
|
return self.__all__.index(name)
|
||||||
|
else:
|
||||||
|
return len(self.__all__)
|
||||||
|
documenters.sort(key=keyfunc)
|
||||||
|
|
||||||
|
return documenters
|
||||||
|
else:
|
||||||
|
return super().sort_members(documenters, order)
|
||||||
|
|
||||||
|
|
||||||
class ModuleLevelDocumenter(Documenter):
|
class ModuleLevelDocumenter(Documenter):
|
||||||
"""
|
"""
|
||||||
@ -1168,6 +1211,14 @@ class DecoratorDocumenter(FunctionDocumenter):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# Types which have confusing metaclass signatures it would be best not to show.
|
||||||
|
# These are listed by name, rather than storing the objects themselves, to avoid
|
||||||
|
# needing to import the modules.
|
||||||
|
_METACLASS_CALL_BLACKLIST = [
|
||||||
|
'enum.EnumMeta.__call__',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore
|
class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for classes.
|
Specialized Documenter subclass for classes.
|
||||||
@ -1202,27 +1253,83 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
self.doc_as_attr = True
|
self.doc_as_attr = True
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def _get_signature(self) -> Optional[Signature]:
|
||||||
|
def get_user_defined_function_or_method(obj: Any, attr: str) -> Any:
|
||||||
|
""" Get the `attr` function or method from `obj`, if it is user-defined. """
|
||||||
|
if inspect.is_builtin_class_method(obj, attr):
|
||||||
|
return None
|
||||||
|
attr = self.get_attr(obj, attr, None)
|
||||||
|
if not (inspect.ismethod(attr) or inspect.isfunction(attr)):
|
||||||
|
return None
|
||||||
|
return attr
|
||||||
|
|
||||||
|
# This sequence is copied from inspect._signature_from_callable.
|
||||||
|
# ValueError means that no signature could be found, so we keep going.
|
||||||
|
|
||||||
|
# First, let's see if it has an overloaded __call__ defined
|
||||||
|
# in its metaclass
|
||||||
|
call = get_user_defined_function_or_method(type(self.object), '__call__')
|
||||||
|
|
||||||
|
if call is not None:
|
||||||
|
if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST:
|
||||||
|
call = None
|
||||||
|
|
||||||
|
if call is not None:
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', call, True)
|
||||||
|
try:
|
||||||
|
return inspect.signature(call, bound_method=True)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Now we check if the 'obj' class has a '__new__' method
|
||||||
|
new = get_user_defined_function_or_method(self.object, '__new__')
|
||||||
|
if new is not None:
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', new, True)
|
||||||
|
try:
|
||||||
|
return inspect.signature(new, bound_method=True)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Finally, we should have at least __init__ implemented
|
||||||
|
init = get_user_defined_function_or_method(self.object, '__init__')
|
||||||
|
if init is not None:
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', init, True)
|
||||||
|
try:
|
||||||
|
return inspect.signature(init, bound_method=True)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# None of the attributes are user-defined, so fall back to let inspect
|
||||||
|
# handle it.
|
||||||
|
# We don't know the exact method that inspect.signature will read
|
||||||
|
# the signature from, so just pass the object itself to our hook.
|
||||||
|
self.env.app.emit('autodoc-before-process-signature', self.object, False)
|
||||||
|
try:
|
||||||
|
return inspect.signature(self.object, bound_method=False)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Still no signature: happens e.g. for old-style classes
|
||||||
|
# with __init__ in C and no `__text_signature__`.
|
||||||
|
return None
|
||||||
|
|
||||||
def format_args(self, **kwargs: Any) -> str:
|
def format_args(self, **kwargs: Any) -> str:
|
||||||
if self.env.config.autodoc_typehints in ('none', 'description'):
|
if self.env.config.autodoc_typehints in ('none', 'description'):
|
||||||
kwargs.setdefault('show_annotation', False)
|
kwargs.setdefault('show_annotation', False)
|
||||||
|
|
||||||
# for classes, the relevant signature is the __init__ method's
|
|
||||||
initmeth = self.get_attr(self.object, '__init__', None)
|
|
||||||
# classes without __init__ method, default __init__ or
|
|
||||||
# __init__ written in C?
|
|
||||||
if initmeth is None or \
|
|
||||||
inspect.is_builtin_class_method(self.object, '__init__') or \
|
|
||||||
not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)):
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
self.env.app.emit('autodoc-before-process-signature', initmeth, True)
|
sig = self._get_signature()
|
||||||
sig = inspect.signature(initmeth, bound_method=True)
|
except TypeError as exc:
|
||||||
return stringify_signature(sig, show_return_annotation=False, **kwargs)
|
# __signature__ attribute contained junk
|
||||||
except TypeError:
|
logger.warning(__("Failed to get a constructor signature for %s: %s"),
|
||||||
# still not possible: happens e.g. for old-style classes
|
self.fullname, exc)
|
||||||
# with __init__ in C
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
if sig is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return stringify_signature(sig, show_return_annotation=False, **kwargs)
|
||||||
|
|
||||||
def format_signature(self, **kwargs: Any) -> str:
|
def format_signature(self, **kwargs: Any) -> str:
|
||||||
if self.doc_as_attr:
|
if self.doc_as_attr:
|
||||||
return ''
|
return ''
|
||||||
|
@ -318,7 +318,7 @@ class GoogleDocstring:
|
|||||||
return [line[min_indent:] for line in lines]
|
return [line[min_indent:] for line in lines]
|
||||||
|
|
||||||
def _escape_args_and_kwargs(self, name: str) -> str:
|
def _escape_args_and_kwargs(self, name: str) -> str:
|
||||||
if name.endswith('_'):
|
if name.endswith('_') and getattr(self._config, 'strip_signature_backslash', False):
|
||||||
name = name[:-1] + r'\_'
|
name = name[:-1] + r'\_'
|
||||||
|
|
||||||
if name[:2] == '**':
|
if name[:2] == '**':
|
||||||
|
@ -98,6 +98,8 @@ class SphinxComponentRegistry:
|
|||||||
#: LaTeX packages; list of package names and its options
|
#: LaTeX packages; list of package names and its options
|
||||||
self.latex_packages = [] # type: List[Tuple[str, str]]
|
self.latex_packages = [] # type: List[Tuple[str, str]]
|
||||||
|
|
||||||
|
self.latex_packages_after_hyperref = [] # type: List[Tuple[str, str]]
|
||||||
|
|
||||||
#: post transforms; list of transforms
|
#: post transforms; list of transforms
|
||||||
self.post_transforms = [] # type: List[Type[Transform]]
|
self.post_transforms = [] # type: List[Type[Transform]]
|
||||||
|
|
||||||
@ -363,8 +365,11 @@ class SphinxComponentRegistry:
|
|||||||
logger.debug('[app] adding js_file: %r, %r', filename, attributes)
|
logger.debug('[app] adding js_file: %r, %r', filename, attributes)
|
||||||
self.js_files.append((filename, attributes))
|
self.js_files.append((filename, attributes))
|
||||||
|
|
||||||
def add_latex_package(self, name: str, options: str) -> None:
|
def add_latex_package(self, name: str, options: str, after_hyperref: bool = False) -> None:
|
||||||
logger.debug('[app] adding latex package: %r', name)
|
logger.debug('[app] adding latex package: %r', name)
|
||||||
|
if after_hyperref:
|
||||||
|
self.latex_packages_after_hyperref.append((name, options))
|
||||||
|
else:
|
||||||
self.latex_packages.append((name, options))
|
self.latex_packages.append((name, options))
|
||||||
|
|
||||||
def add_enumerable_node(self, node: "Type[Node]", figtype: str,
|
def add_enumerable_node(self, node: "Type[Node]", figtype: str,
|
||||||
|
@ -46,6 +46,14 @@
|
|||||||
<%- endfor %>
|
<%- endfor %>
|
||||||
|
|
||||||
<%= hyperref %>
|
<%= hyperref %>
|
||||||
|
<%- for name, option in packages_after_hyperref %>
|
||||||
|
<%- if option %>
|
||||||
|
\usepackage[<%= option %>]{<%= name %>}
|
||||||
|
<%- else %>
|
||||||
|
\usepackage{<%= name %>}
|
||||||
|
<%- endif %>
|
||||||
|
<%- endfor %>
|
||||||
|
|
||||||
<%= contentsname %>
|
<%= contentsname %>
|
||||||
\usepackage{sphinxmessages}
|
\usepackage{sphinxmessages}
|
||||||
<%= tocdepth %>
|
<%= tocdepth %>
|
||||||
|
@ -726,7 +726,7 @@ def getdoc(obj: Any, attrgetter: Callable = safe_getattr,
|
|||||||
# This tries to obtain the docstring from super classes.
|
# This tries to obtain the docstring from super classes.
|
||||||
for basecls in getattr(cls, '__mro__', []):
|
for basecls in getattr(cls, '__mro__', []):
|
||||||
meth = safe_getattr(basecls, name, None)
|
meth = safe_getattr(basecls, name, None)
|
||||||
if meth:
|
if meth is not None:
|
||||||
doc = inspect.getdoc(meth)
|
doc = inspect.getdoc(meth)
|
||||||
if doc:
|
if doc:
|
||||||
break
|
break
|
||||||
|
25
tests/roots/test-ext-autodoc/target/sort_by_all.py
Normal file
25
tests/roots/test-ext-autodoc/target/sort_by_all.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
__all__ = ['baz', 'foo', 'Bar']
|
||||||
|
|
||||||
|
|
||||||
|
def foo():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Bar:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def baz():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def qux():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Quux:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def foobar():
|
||||||
|
pass
|
@ -37,6 +37,26 @@ def tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewAnnotation:
|
||||||
|
def __new__(cls, i: int) -> 'NewAnnotation':
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NewComment:
|
||||||
|
def __new__(cls, i):
|
||||||
|
# type: (int) -> NewComment
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _MetaclassWithCall(type):
|
||||||
|
def __call__(cls, a: int):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SignatureFromMetaclass(metaclass=_MetaclassWithCall):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def complex_func(arg1, arg2, arg3=None, *args, **kwargs):
|
def complex_func(arg1, arg2, arg3=None, *args, **kwargs):
|
||||||
# type: (str, List[int], Tuple[int, Union[str, Unknown]], *str, **str) -> None
|
# type: (str, List[int], Tuple[int, Union[str, Unknown]], *str, **str) -> None
|
||||||
pass
|
pass
|
||||||
@ -48,4 +68,3 @@ def missing_attr(c,
|
|||||||
):
|
):
|
||||||
# type: (...) -> str
|
# type: (...) -> str
|
||||||
return a + (b or "")
|
return a + (b or "")
|
||||||
|
|
||||||
|
@ -169,21 +169,64 @@ def test_format_signature(app):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class E:
|
class E:
|
||||||
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
# no signature for classes without __init__
|
|
||||||
for C in (D, E):
|
|
||||||
assert formatsig('class', 'D', C, None, None) == ''
|
|
||||||
|
|
||||||
|
# an empty init and no init are the same
|
||||||
|
for C in (D, E):
|
||||||
|
assert formatsig('class', 'D', C, None, None) == '()'
|
||||||
|
|
||||||
|
|
||||||
|
class SomeMeta(type):
|
||||||
|
def __call__(cls, a, b=None):
|
||||||
|
return type.__call__(cls, a, b)
|
||||||
|
|
||||||
|
# these three are all equivalent
|
||||||
class F:
|
class F:
|
||||||
def __init__(self, a, b=None):
|
def __init__(self, a, b=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class FNew:
|
||||||
|
def __new__(cls, a, b=None):
|
||||||
|
return super().__new__(cls)
|
||||||
|
|
||||||
|
class FMeta(metaclass=SomeMeta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# and subclasses should always inherit
|
||||||
class G(F):
|
class G(F):
|
||||||
pass
|
pass
|
||||||
for C in (F, G):
|
|
||||||
|
class GNew(FNew):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class GMeta(FMeta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# subclasses inherit
|
||||||
|
for C in (F, FNew, FMeta, G, GNew, GMeta):
|
||||||
assert formatsig('class', 'C', C, None, None) == '(a, b=None)'
|
assert formatsig('class', 'C', C, None, None) == '(a, b=None)'
|
||||||
assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
|
assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
|
||||||
|
|
||||||
|
|
||||||
|
class ListSubclass(list):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# only supported if the python implementation decides to document it
|
||||||
|
if getattr(list, '__text_signature__', None) is not None:
|
||||||
|
assert formatsig('class', 'C', ListSubclass, None, None) == '(iterable=(), /)'
|
||||||
|
else:
|
||||||
|
assert formatsig('class', 'C', ListSubclass, None, None) == ''
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionSubclass(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Exception has no __text_signature__ at least in Python 3.8
|
||||||
|
if getattr(Exception, '__text_signature__', None) is None:
|
||||||
|
assert formatsig('class', 'C', ExceptionSubclass, None, None) == ''
|
||||||
|
|
||||||
|
|
||||||
# __init__ have signature at first line of docstring
|
# __init__ have signature at first line of docstring
|
||||||
directive.env.config.autoclass_content = 'both'
|
directive.env.config.autoclass_content = 'both'
|
||||||
|
|
||||||
@ -497,14 +540,14 @@ def test_autodoc_members(app):
|
|||||||
# default (no-members)
|
# default (no-members)
|
||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base')
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base')
|
||||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
]
|
]
|
||||||
|
|
||||||
# default ALL-members
|
# default ALL-members
|
||||||
options = {"members": None}
|
options = {"members": None}
|
||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
||||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
' .. py:method:: Base.inheritedclassmeth()',
|
' .. py:method:: Base.inheritedclassmeth()',
|
||||||
' .. py:method:: Base.inheritedmeth()',
|
' .. py:method:: Base.inheritedmeth()',
|
||||||
' .. py:method:: Base.inheritedstaticmeth(cls)'
|
' .. py:method:: Base.inheritedstaticmeth(cls)'
|
||||||
@ -514,7 +557,7 @@ def test_autodoc_members(app):
|
|||||||
options = {"members": "inheritedmeth,inheritedstaticmeth"}
|
options = {"members": "inheritedmeth,inheritedstaticmeth"}
|
||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
||||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
' .. py:method:: Base.inheritedmeth()',
|
' .. py:method:: Base.inheritedmeth()',
|
||||||
' .. py:method:: Base.inheritedstaticmeth(cls)'
|
' .. py:method:: Base.inheritedstaticmeth(cls)'
|
||||||
]
|
]
|
||||||
@ -526,7 +569,7 @@ def test_autodoc_exclude_members(app):
|
|||||||
"exclude-members": "inheritedmeth,inheritedstaticmeth"}
|
"exclude-members": "inheritedmeth,inheritedstaticmeth"}
|
||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
||||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
' .. py:method:: Base.inheritedclassmeth()'
|
' .. py:method:: Base.inheritedclassmeth()'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -535,7 +578,7 @@ def test_autodoc_exclude_members(app):
|
|||||||
"exclude-members": "inheritedmeth"}
|
"exclude-members": "inheritedmeth"}
|
||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
||||||
assert list(filter(lambda l: '::' in l, actual)) == [
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -679,10 +722,10 @@ def test_autodoc_ignore_module_all(app):
|
|||||||
assert list(filter(lambda l: 'class::' in l, actual)) == [
|
assert list(filter(lambda l: 'class::' in l, actual)) == [
|
||||||
'.. py:class:: Class(arg)',
|
'.. py:class:: Class(arg)',
|
||||||
'.. py:class:: CustomDict',
|
'.. py:class:: CustomDict',
|
||||||
'.. py:class:: InnerChild',
|
'.. py:class:: InnerChild()',
|
||||||
'.. py:class:: InstAttCls()',
|
'.. py:class:: InstAttCls()',
|
||||||
'.. py:class:: Outer',
|
'.. py:class:: Outer()',
|
||||||
' .. py:class:: Outer.Inner',
|
' .. py:class:: Outer.Inner()',
|
||||||
'.. py:class:: StrRepr'
|
'.. py:class:: StrRepr'
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -703,7 +746,7 @@ def test_autodoc_noindex(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
actual = do_autodoc(app, 'class', 'target.inheritance.Base', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
' :noindex:',
|
' :noindex:',
|
||||||
' :module: target.inheritance',
|
' :module: target.inheritance',
|
||||||
''
|
''
|
||||||
@ -730,13 +773,13 @@ def test_autodoc_inner_class(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.Outer', options)
|
actual = do_autodoc(app, 'class', 'target.Outer', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Outer',
|
'.. py:class:: Outer()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
' Foo',
|
' Foo',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:class:: Outer.Inner',
|
' .. py:class:: Outer.Inner()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
' Foo',
|
' Foo',
|
||||||
@ -757,7 +800,7 @@ def test_autodoc_inner_class(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
|
actual = do_autodoc(app, 'class', 'target.Outer.Inner', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Outer.Inner',
|
'.. py:class:: Outer.Inner()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
' Foo',
|
' Foo',
|
||||||
@ -774,7 +817,7 @@ def test_autodoc_inner_class(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.InnerChild', options)
|
actual = do_autodoc(app, 'class', 'target.InnerChild', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: InnerChild',
|
'.. py:class:: InnerChild()',
|
||||||
' :module: target', '',
|
' :module: target', '',
|
||||||
' Bases: :class:`target.Outer.Inner`',
|
' Bases: :class:`target.Outer.Inner`',
|
||||||
'',
|
'',
|
||||||
@ -818,7 +861,7 @@ def test_autodoc_descriptor(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.descriptor.Class', options)
|
actual = do_autodoc(app, 'class', 'target.descriptor.Class', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Class',
|
'.. py:class:: Class()',
|
||||||
' :module: target.descriptor',
|
' :module: target.descriptor',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -914,6 +957,40 @@ def test_autodoc_member_order(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autodoc_module_member_order(app):
|
||||||
|
# case member-order='bysource'
|
||||||
|
options = {"members": 'foo, Bar, baz, qux, Quux, foobar',
|
||||||
|
'member-order': 'bysource',
|
||||||
|
"undoc-members": True}
|
||||||
|
actual = do_autodoc(app, 'module', 'target.sort_by_all', options)
|
||||||
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
|
'.. py:module:: target.sort_by_all',
|
||||||
|
'.. py:function:: baz()',
|
||||||
|
'.. py:function:: foo()',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
'.. py:class:: Quux()',
|
||||||
|
'.. py:function:: foobar()',
|
||||||
|
'.. py:function:: qux()',
|
||||||
|
]
|
||||||
|
|
||||||
|
# case member-order='bysource' and ignore-module-all
|
||||||
|
options = {"members": 'foo, Bar, baz, qux, Quux, foobar',
|
||||||
|
'member-order': 'bysource',
|
||||||
|
"undoc-members": True,
|
||||||
|
"ignore-module-all": True}
|
||||||
|
actual = do_autodoc(app, 'module', 'target.sort_by_all', options)
|
||||||
|
assert list(filter(lambda l: '::' in l, actual)) == [
|
||||||
|
'.. py:module:: target.sort_by_all',
|
||||||
|
'.. py:function:: foo()',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
'.. py:function:: baz()',
|
||||||
|
'.. py:function:: qux()',
|
||||||
|
'.. py:class:: Quux()',
|
||||||
|
'.. py:function:: foobar()',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_autodoc_module_scope(app):
|
def test_autodoc_module_scope(app):
|
||||||
app.env.temp_data['autodoc:module'] = 'target'
|
app.env.temp_data['autodoc:module'] = 'target'
|
||||||
@ -952,7 +1029,7 @@ def test_class_attributes(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.AttCls', options)
|
actual = do_autodoc(app, 'class', 'target.AttCls', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: AttCls',
|
'.. py:class:: AttCls()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -1072,7 +1149,7 @@ def test_slots(app):
|
|||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Foo',
|
'.. py:class:: Foo()',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -1088,7 +1165,7 @@ def test_enum_class(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.enum.EnumCls', options)
|
actual = do_autodoc(app, 'class', 'target.enum.EnumCls', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: EnumCls',
|
'.. py:class:: EnumCls(value)',
|
||||||
' :module: target.enum',
|
' :module: target.enum',
|
||||||
'',
|
'',
|
||||||
' this is enum class',
|
' this is enum class',
|
||||||
@ -1205,7 +1282,7 @@ def test_abstractmethods(app):
|
|||||||
'.. py:module:: target.abstractmethods',
|
'.. py:module:: target.abstractmethods',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Base',
|
'.. py:class:: Base()',
|
||||||
' :module: target.abstractmethods',
|
' :module: target.abstractmethods',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -1322,7 +1399,7 @@ def test_coroutine(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
|
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: AsyncClass',
|
'.. py:class:: AsyncClass()',
|
||||||
' :module: target.coroutine',
|
' :module: target.coroutine',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -1364,7 +1441,7 @@ def test_coroutine(app):
|
|||||||
def test_partialmethod(app):
|
def test_partialmethod(app):
|
||||||
expected = [
|
expected = [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Cell',
|
'.. py:class:: Cell()',
|
||||||
' :module: target.partialmethod',
|
' :module: target.partialmethod',
|
||||||
'',
|
'',
|
||||||
' An example for partialmethod.',
|
' An example for partialmethod.',
|
||||||
@ -1394,7 +1471,7 @@ def test_partialmethod(app):
|
|||||||
def test_partialmethod_undoc_members(app):
|
def test_partialmethod_undoc_members(app):
|
||||||
expected = [
|
expected = [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Cell',
|
'.. py:class:: Cell()',
|
||||||
' :module: target.partialmethod',
|
' :module: target.partialmethod',
|
||||||
'',
|
'',
|
||||||
' An example for partialmethod.',
|
' An example for partialmethod.',
|
||||||
@ -1581,7 +1658,7 @@ def test_singledispatchmethod(app):
|
|||||||
'.. py:module:: target.singledispatchmethod',
|
'.. py:module:: target.singledispatchmethod',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Foo',
|
'.. py:class:: Foo()',
|
||||||
' :module: target.singledispatchmethod',
|
' :module: target.singledispatchmethod',
|
||||||
'',
|
'',
|
||||||
' docstring',
|
' docstring',
|
||||||
@ -1626,7 +1703,7 @@ def test_cython(app):
|
|||||||
'.. py:module:: target.cython',
|
'.. py:module:: target.cython',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Class',
|
'.. py:class:: Class()',
|
||||||
' :module: target.cython',
|
' :module: target.cython',
|
||||||
'',
|
'',
|
||||||
' Docstring.',
|
' Docstring.',
|
||||||
@ -1657,7 +1734,7 @@ def test_final(app):
|
|||||||
'.. py:module:: target.final',
|
'.. py:module:: target.final',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: Class',
|
'.. py:class:: Class()',
|
||||||
' :module: target.final',
|
' :module: target.final',
|
||||||
' :final:',
|
' :final:',
|
||||||
'',
|
'',
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import platform
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ def test_autoclass_content_class(app):
|
|||||||
'.. py:module:: target.autoclass_content',
|
'.. py:module:: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: A',
|
'.. py:class:: A()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, no __new__',
|
' A class having no __init__, no __new__',
|
||||||
@ -45,13 +46,13 @@ def test_autoclass_content_class(app):
|
|||||||
' A class having __init__, no __new__',
|
' A class having __init__, no __new__',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: D',
|
'.. py:class:: D()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, __new__(no docstring)',
|
' A class having no __init__, __new__(no docstring)',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: E',
|
'.. py:class:: E()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, __new__',
|
' A class having no __init__, __new__',
|
||||||
@ -87,7 +88,7 @@ def test_autoclass_content_init(app):
|
|||||||
'.. py:module:: target.autoclass_content',
|
'.. py:module:: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: A',
|
'.. py:class:: A()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, no __new__',
|
' A class having no __init__, no __new__',
|
||||||
@ -105,13 +106,13 @@ def test_autoclass_content_init(app):
|
|||||||
' __init__ docstring',
|
' __init__ docstring',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: D',
|
'.. py:class:: D()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, __new__(no docstring)',
|
' A class having no __init__, __new__(no docstring)',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: E',
|
'.. py:class:: E()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' __new__ docstring',
|
' __new__ docstring',
|
||||||
@ -147,7 +148,7 @@ def test_autoclass_content_both(app):
|
|||||||
'.. py:module:: target.autoclass_content',
|
'.. py:module:: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: A',
|
'.. py:class:: A()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, no __new__',
|
' A class having no __init__, no __new__',
|
||||||
@ -167,13 +168,13 @@ def test_autoclass_content_both(app):
|
|||||||
' __init__ docstring',
|
' __init__ docstring',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: D',
|
'.. py:class:: D()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, __new__(no docstring)',
|
' A class having no __init__, __new__(no docstring)',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: E',
|
'.. py:class:: E()',
|
||||||
' :module: target.autoclass_content',
|
' :module: target.autoclass_content',
|
||||||
'',
|
'',
|
||||||
' A class having no __init__, __new__',
|
' A class having no __init__, __new__',
|
||||||
@ -237,7 +238,7 @@ def test_autodoc_docstring_signature(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: DocstringSig',
|
'.. py:class:: DocstringSig()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -279,7 +280,7 @@ def test_autodoc_docstring_signature(app):
|
|||||||
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
actual = do_autodoc(app, 'class', 'target.DocstringSig', options)
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
'',
|
'',
|
||||||
'.. py:class:: DocstringSig',
|
'.. py:class:: DocstringSig()',
|
||||||
' :module: target',
|
' :module: target',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
@ -435,7 +436,7 @@ def test_mocked_module_imports(app, warning):
|
|||||||
'.. py:module:: target.need_mocks',
|
'.. py:module:: target.need_mocks',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
'.. py:class:: TestAutodoc',
|
'.. py:class:: TestAutodoc()',
|
||||||
' :module: target.need_mocks',
|
' :module: target.need_mocks',
|
||||||
'',
|
'',
|
||||||
' TestAutodoc docstring.',
|
' TestAutodoc docstring.',
|
||||||
@ -493,6 +494,18 @@ def test_autodoc_typehints_signature(app):
|
|||||||
' :module: target.typehints',
|
' :module: target.typehints',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
'.. py:class:: NewAnnotation(i: int)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:class:: NewComment(i: int)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:class:: SignatureFromMetaclass(a: int)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
'.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, '
|
'.. py:function:: complex_func(arg1: str, arg2: List[int], arg3: Tuple[int, '
|
||||||
'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None',
|
'Union[str, Unknown]] = None, *args: str, **kwargs: str) -> None',
|
||||||
' :module: target.typehints',
|
' :module: target.typehints',
|
||||||
@ -547,6 +560,18 @@ def test_autodoc_typehints_none(app):
|
|||||||
' :module: target.typehints',
|
' :module: target.typehints',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
'.. py:class:: NewAnnotation(i)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:class:: NewComment(i)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:class:: SignatureFromMetaclass(a)',
|
||||||
|
' :module: target.typehints',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
'.. py:function:: complex_func(arg1, arg2, arg3=None, *args, **kwargs)',
|
'.. py:function:: complex_func(arg1, arg2, arg3=None, *args, **kwargs)',
|
||||||
' :module: target.typehints',
|
' :module: target.typehints',
|
||||||
'',
|
'',
|
||||||
|
@ -292,7 +292,7 @@ def test_autosummary_generate(app, status, warning):
|
|||||||
assert len(doctree[3][0][0][2]) == 5
|
assert len(doctree[3][0][0][2]) == 5
|
||||||
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
assert doctree[3][0][0][2][0].astext() == 'autosummary_dummy_module\n\n'
|
||||||
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
assert doctree[3][0][0][2][1].astext() == 'autosummary_dummy_module.Foo()\n\n'
|
||||||
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.Foo.Bar\n\n'
|
assert doctree[3][0][0][2][2].astext() == 'autosummary_dummy_module.Foo.Bar()\n\n'
|
||||||
assert doctree[3][0][0][2][3].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
assert doctree[3][0][0][2][3].astext() == 'autosummary_dummy_module.bar(x[, y])\n\n'
|
||||||
assert doctree[3][0][0][2][4].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
|
assert doctree[3][0][0][2][4].astext() == 'autosummary_dummy_module.qux\n\na module-level attribute'
|
||||||
|
|
||||||
|
@ -1394,6 +1394,26 @@ Summary
|
|||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
arg_ : type
|
||||||
|
some description
|
||||||
|
"""
|
||||||
|
|
||||||
|
expected = """
|
||||||
|
:ivar arg_: some description
|
||||||
|
:vartype arg_: type
|
||||||
|
"""
|
||||||
|
|
||||||
|
config = Config(napoleon_use_ivar=True)
|
||||||
|
app = mock.Mock()
|
||||||
|
actual = str(NumpyDocstring(docstring, config, app, "class"))
|
||||||
|
|
||||||
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_underscore_in_attribute_strip_signature_backslash(self):
|
||||||
|
docstring = """
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
|
||||||
arg_ : type
|
arg_ : type
|
||||||
some description
|
some description
|
||||||
"""
|
"""
|
||||||
@ -1404,6 +1424,7 @@ arg_ : type
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
config = Config(napoleon_use_ivar=True)
|
config = Config(napoleon_use_ivar=True)
|
||||||
|
config.strip_signature_backslash = True
|
||||||
app = mock.Mock()
|
app = mock.Mock()
|
||||||
actual = str(NumpyDocstring(docstring, config, app, "class"))
|
actual = str(NumpyDocstring(docstring, config, app, "class"))
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@ def test_project_path2doc(app):
|
|||||||
assert project.path2doc('index.foo') is None # unknown extension
|
assert project.path2doc('index.foo') is None # unknown extension
|
||||||
assert project.path2doc('index.foo.rst') == 'index.foo'
|
assert project.path2doc('index.foo.rst') == 'index.foo'
|
||||||
assert project.path2doc('index') is None
|
assert project.path2doc('index') is None
|
||||||
assert project.path2doc('/path/to/index.rst') == PathComparer('/path/to/index')
|
assert project.path2doc('path/to/index.rst') == 'path/to/index'
|
||||||
assert project.path2doc(app.srcdir / '/to/index.rst') == PathComparer('/to/index')
|
assert project.path2doc(app.srcdir / 'to/index.rst') == 'to/index'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx(srcdir='project_doc2path', testroot='basic')
|
@pytest.mark.sphinx(srcdir='project_doc2path', testroot='basic')
|
||||||
|
@ -29,12 +29,14 @@ def test_signature():
|
|||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
inspect.signature('')
|
inspect.signature('')
|
||||||
|
|
||||||
# builitin classes
|
# builtins are supported on a case-by-case basis, depending on whether
|
||||||
|
# they define __text_signature__
|
||||||
|
if getattr(list, '__text_signature__', None):
|
||||||
|
sig = inspect.stringify_signature(inspect.signature(list))
|
||||||
|
assert sig == '(iterable=(), /)'
|
||||||
|
else:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
inspect.signature(int)
|
inspect.signature(list)
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
|
||||||
inspect.signature(str)
|
|
||||||
|
|
||||||
# normal function
|
# normal function
|
||||||
def func(a, b, c=1, d=2, *e, **f):
|
def func(a, b, c=1, d=2, *e, **f):
|
||||||
|
Loading…
Reference in New Issue
Block a user