mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x'
This commit is contained in:
82
CHANGES
82
CHANGES
@@ -54,7 +54,7 @@ Bugs fixed
|
|||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Release 3.4.0 (in development)
|
Release 3.5.0 (in development)
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
Dependencies
|
Dependencies
|
||||||
@@ -63,6 +63,45 @@ Dependencies
|
|||||||
Incompatible changes
|
Incompatible changes
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
Deprecated
|
||||||
|
----------
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
Testing
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 3.4.1 (in development)
|
||||||
|
==============================
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
Incompatible changes
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Deprecated
|
||||||
|
----------
|
||||||
|
|
||||||
|
Features added
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Bugs fixed
|
||||||
|
----------
|
||||||
|
|
||||||
|
Testing
|
||||||
|
--------
|
||||||
|
|
||||||
|
Release 3.4.0 (released Dec 20, 2020)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
Incompatible changes
|
||||||
|
--------------------
|
||||||
|
|
||||||
* #8105: autodoc: the signature of class constructor will be shown for decorated
|
* #8105: autodoc: the signature of class constructor will be shown for decorated
|
||||||
classes, not a signature of decorator
|
classes, not a signature of decorator
|
||||||
|
|
||||||
@@ -79,7 +118,9 @@ Deprecated
|
|||||||
* ``sphinx.ext.autodoc.SlotsAttributeDocumenter``
|
* ``sphinx.ext.autodoc.SlotsAttributeDocumenter``
|
||||||
* ``sphinx.ext.autodoc.TypeVarDocumenter``
|
* ``sphinx.ext.autodoc.TypeVarDocumenter``
|
||||||
* ``sphinx.ext.autodoc.importer._getannotations()``
|
* ``sphinx.ext.autodoc.importer._getannotations()``
|
||||||
|
* ``sphinx.ext.autodoc.importer._getmro()``
|
||||||
* ``sphinx.pycode.ModuleAnalyzer.parse()``
|
* ``sphinx.pycode.ModuleAnalyzer.parse()``
|
||||||
|
* ``sphinx.util.osutil.movefile()``
|
||||||
* ``sphinx.util.requests.is_ssl_error()``
|
* ``sphinx.util.requests.is_ssl_error()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
@@ -98,6 +139,7 @@ Features added
|
|||||||
* #8460: autodoc: Support custom types defined by typing.NewType
|
* #8460: autodoc: Support custom types defined by typing.NewType
|
||||||
* #8285: napoleon: Add :confval:`napoleon_attr_annotations` to merge type hints
|
* #8285: napoleon: Add :confval:`napoleon_attr_annotations` to merge type hints
|
||||||
on source code automatically if any type is specified in docstring
|
on source code automatically if any type is specified in docstring
|
||||||
|
* #8236: napoleon: Support numpydoc's "Receives" section
|
||||||
* #6914: Add a new event :event:`warn-missing-reference` to custom warning
|
* #6914: Add a new event :event:`warn-missing-reference` to custom warning
|
||||||
messages when failed to resolve a cross-reference
|
messages when failed to resolve a cross-reference
|
||||||
* #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference
|
* #6914: Emit a detailed warning when failed to resolve a ``:ref:`` reference
|
||||||
@@ -121,49 +163,37 @@ Bugs fixed
|
|||||||
attributes
|
attributes
|
||||||
* #8503: autodoc: autoattribute could not create document for a GenericAlias as
|
* #8503: autodoc: autoattribute could not create document for a GenericAlias as
|
||||||
class attributes correctly
|
class attributes correctly
|
||||||
|
* #8534: autodoc: autoattribute could not create document for a commented
|
||||||
|
attribute in alias class
|
||||||
* #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is
|
* #8452: autodoc: autodoc_type_aliases doesn't work when autodoc_typehints is
|
||||||
set to "description"
|
set to "description"
|
||||||
|
* #8541: autodoc: autodoc_type_aliases doesn't work for the type annotation to
|
||||||
|
instance attributes
|
||||||
* #8460: autodoc: autodata and autoattribute directives do not display type
|
* #8460: autodoc: autodata and autoattribute directives do not display type
|
||||||
information of TypeVars
|
information of TypeVars
|
||||||
* #8493: autodoc: references to builtins not working in class aliases
|
* #8493: autodoc: references to builtins not working in class aliases
|
||||||
* #8522: autodoc: ``__bool__`` method could be called
|
* #8522: autodoc: ``__bool__`` method could be called
|
||||||
|
* #8067: autodoc: A typehint for the instance variable having type_comment on
|
||||||
|
super class is not displayed
|
||||||
|
* #8545: autodoc: a __slots__ attribute is not documented even having docstring
|
||||||
|
* #741: autodoc: inherited-members doesn't work for instance attributes on super
|
||||||
|
class
|
||||||
* #8477: autosummary: non utf-8 reST files are generated when template contains
|
* #8477: autosummary: non utf-8 reST files are generated when template contains
|
||||||
multibyte characters
|
multibyte characters
|
||||||
* #8501: autosummary: summary extraction splits text after "el at." unexpectedly
|
* #8501: autosummary: summary extraction splits text after "el at." unexpectedly
|
||||||
|
* #8524: html: Wrong url_root has been generated on a document named "index"
|
||||||
* #8419: html search: Do not load ``language_data.js`` in non-search pages
|
* #8419: html search: Do not load ``language_data.js`` in non-search pages
|
||||||
|
* #8549: i18n: ``-D gettext_compact=0`` is no longer working
|
||||||
* #8454: graphviz: The layout option for graph and digraph directives don't work
|
* #8454: graphviz: The layout option for graph and digraph directives don't work
|
||||||
* #8131: linkcheck: Use GET when HEAD requests cause Too Many Redirects, to
|
* #8131: linkcheck: Use GET when HEAD requests cause Too Many Redirects, to
|
||||||
accommodate infinite redirect loops on HEAD
|
accommodate infinite redirect loops on HEAD
|
||||||
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
|
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
|
||||||
|
* #8365: py domain: ``:type:`` and ``:rtype:`` gives false ambiguous class
|
||||||
|
lookup warnings
|
||||||
* #8352: std domain: Failed to parse an option that starts with bracket
|
* #8352: std domain: Failed to parse an option that starts with bracket
|
||||||
* #8519: LaTeX: Prevent page brake in the middle of a seealso
|
* #8519: LaTeX: Prevent page brake in the middle of a seealso
|
||||||
|
|
||||||
Testing
|
|
||||||
--------
|
|
||||||
|
|
||||||
Release 3.3.2 (in development)
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Dependencies
|
|
||||||
------------
|
|
||||||
|
|
||||||
Incompatible changes
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Deprecated
|
|
||||||
----------
|
|
||||||
|
|
||||||
Features added
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Bugs fixed
|
|
||||||
----------
|
|
||||||
|
|
||||||
* #8520: C, fix copying of AliasNode.
|
* #8520: C, fix copying of AliasNode.
|
||||||
|
|
||||||
Testing
|
|
||||||
--------
|
|
||||||
|
|
||||||
Release 3.3.1 (released Nov 12, 2020)
|
Release 3.3.1 (released Nov 12, 2020)
|
||||||
=====================================
|
=====================================
|
||||||
|
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ You can also browse it from this repository from
|
|||||||
``doc/internals/contributing.rst``
|
``doc/internals/contributing.rst``
|
||||||
|
|
||||||
Sphinx uses GitHub to host source code, track patches and bugs, and more.
|
Sphinx uses GitHub to host source code, track patches and bugs, and more.
|
||||||
Please make an effort to provide as much possible when filing bugs.
|
Please make an effort to provide as much detail as possible when filing
|
||||||
|
bugs.
|
||||||
|
|||||||
@@ -102,11 +102,21 @@ The following is a list of deprecated interfaces.
|
|||||||
- 4.0
|
- 4.0
|
||||||
- ``sphinx.util.inspect.getannotations()``
|
- ``sphinx.util.inspect.getannotations()``
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autodoc.importer._getmro()``
|
||||||
|
- 3.4
|
||||||
|
- 4.0
|
||||||
|
- ``sphinx.util.inspect.getmro()``
|
||||||
|
|
||||||
* - ``sphinx.pycode.ModuleAnalyzer.parse()``
|
* - ``sphinx.pycode.ModuleAnalyzer.parse()``
|
||||||
- 3.4
|
- 3.4
|
||||||
- 5.0
|
- 5.0
|
||||||
- ``sphinx.pycode.ModuleAnalyzer.analyze()``
|
- ``sphinx.pycode.ModuleAnalyzer.analyze()``
|
||||||
|
|
||||||
|
* - ``sphinx.util.osutil.movefile()``
|
||||||
|
- 3.4
|
||||||
|
- 5.0
|
||||||
|
- ``os.replace()``
|
||||||
|
|
||||||
* - ``sphinx.util.requests.is_ssl_error()``
|
* - ``sphinx.util.requests.is_ssl_error()``
|
||||||
- 3.4
|
- 3.4
|
||||||
- 5.0
|
- 5.0
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ class MessageCatalogBuilder(I18nBuilder):
|
|||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.add_builder(MessageCatalogBuilder)
|
app.add_builder(MessageCatalogBuilder)
|
||||||
|
|
||||||
app.add_config_value('gettext_compact', True, 'gettext', Any)
|
app.add_config_value('gettext_compact', True, 'gettext', {bool, str})
|
||||||
app.add_config_value('gettext_location', True, 'gettext')
|
app.add_config_value('gettext_location', True, 'gettext')
|
||||||
app.add_config_value('gettext_uuid', False, 'gettext')
|
app.add_config_value('gettext_uuid', False, 'gettext')
|
||||||
app.add_config_value('gettext_auto_build', True, 'env')
|
app.add_config_value('gettext_auto_build', True, 'env')
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import html
|
import html
|
||||||
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@@ -43,7 +44,7 @@ from sphinx.util.fileutil import copy_asset
|
|||||||
from sphinx.util.i18n import format_date
|
from sphinx.util.i18n import format_date
|
||||||
from sphinx.util.inventory import InventoryFile
|
from sphinx.util.inventory import InventoryFile
|
||||||
from sphinx.util.matching import DOTFILES, Matcher, patmatch
|
from sphinx.util.matching import DOTFILES, Matcher, patmatch
|
||||||
from sphinx.util.osutil import copyfile, ensuredir, movefile, os_path, relative_uri
|
from sphinx.util.osutil import copyfile, ensuredir, os_path, relative_uri
|
||||||
from sphinx.util.tags import Tags
|
from sphinx.util.tags import Tags
|
||||||
from sphinx.writers.html import HTMLTranslator, HTMLWriter
|
from sphinx.writers.html import HTMLTranslator, HTMLWriter
|
||||||
|
|
||||||
@@ -1064,7 +1065,7 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
else:
|
else:
|
||||||
with open(searchindexfn + '.tmp', 'wb') as fb:
|
with open(searchindexfn + '.tmp', 'wb') as fb:
|
||||||
self.indexer.dump(fb, self.indexer_format)
|
self.indexer.dump(fb, self.indexer_format)
|
||||||
movefile(searchindexfn + '.tmp', searchindexfn)
|
os.replace(searchindexfn + '.tmp', searchindexfn)
|
||||||
|
|
||||||
|
|
||||||
def convert_html_css_files(app: Sphinx, config: Config) -> None:
|
def convert_html_css_files(app: Sphinx, config: Config) -> None:
|
||||||
|
|||||||
@@ -173,6 +173,14 @@ class Config:
|
|||||||
defvalue = self.values[name][0]
|
defvalue = self.values[name][0]
|
||||||
if self.values[name][2] == Any:
|
if self.values[name][2] == Any:
|
||||||
return value
|
return value
|
||||||
|
elif self.values[name][2] == {bool, str}:
|
||||||
|
if value == '0':
|
||||||
|
# given falsy string from command line option
|
||||||
|
return False
|
||||||
|
elif value == '1':
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return value
|
||||||
elif type(defvalue) is bool or self.values[name][2] == [bool]:
|
elif type(defvalue) is bool or self.values[name][2] == [bool]:
|
||||||
if value == '0':
|
if value == '0':
|
||||||
# given falsy string from command line option
|
# given falsy string from command line option
|
||||||
|
|||||||
@@ -271,6 +271,8 @@ class PyXrefMixin:
|
|||||||
result = super().make_xref(rolename, domain, target, # type: ignore
|
result = super().make_xref(rolename, domain, target, # type: ignore
|
||||||
innernode, contnode, env)
|
innernode, contnode, env)
|
||||||
result['refspecific'] = True
|
result['refspecific'] = True
|
||||||
|
result['py:module'] = env.ref_context.get('py:module')
|
||||||
|
result['py:class'] = env.ref_context.get('py:class')
|
||||||
if target.startswith(('.', '~')):
|
if target.startswith(('.', '~')):
|
||||||
prefix, result['reftarget'] = target[0], target[1:]
|
prefix, result['reftarget'] = target[0], target[1:]
|
||||||
if prefix == '.':
|
if prefix == '.':
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ from sphinx.application import Sphinx
|
|||||||
from sphinx.config import ENUM, Config
|
from sphinx.config import ENUM, Config
|
||||||
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
|
from sphinx.deprecation import RemovedInSphinx50Warning, RemovedInSphinx60Warning
|
||||||
from sphinx.environment import BuildEnvironment
|
from sphinx.environment import BuildEnvironment
|
||||||
from sphinx.ext.autodoc.importer import get_module_members, get_object_members, import_object
|
from sphinx.ext.autodoc.importer import (get_class_members, get_module_members,
|
||||||
|
get_object_members, import_object)
|
||||||
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
|
||||||
@@ -270,9 +271,11 @@ class ObjectMember(tuple):
|
|||||||
def __new__(cls, name: str, obj: Any, **kwargs: Any) -> Any:
|
def __new__(cls, name: str, obj: Any, **kwargs: Any) -> Any:
|
||||||
return super().__new__(cls, (name, obj)) # type: ignore
|
return super().__new__(cls, (name, obj)) # type: ignore
|
||||||
|
|
||||||
def __init__(self, name: str, obj: Any, skipped: bool = False) -> None:
|
def __init__(self, name: str, obj: Any, docstring: Optional[str] = None,
|
||||||
|
skipped: bool = False) -> None:
|
||||||
self.__name__ = name
|
self.__name__ = name
|
||||||
self.object = obj
|
self.object = obj
|
||||||
|
self.docstring = docstring
|
||||||
self.skipped = skipped
|
self.skipped = skipped
|
||||||
|
|
||||||
|
|
||||||
@@ -700,6 +703,11 @@ class Documenter:
|
|||||||
cls_doc = self.get_attr(cls, '__doc__', None)
|
cls_doc = self.get_attr(cls, '__doc__', None)
|
||||||
if cls_doc == doc:
|
if cls_doc == doc:
|
||||||
doc = None
|
doc = None
|
||||||
|
|
||||||
|
if isinstance(obj, ObjectMember) and obj.docstring:
|
||||||
|
# hack for ClassDocumenter to inject docstring via ObjectMember
|
||||||
|
doc = obj.docstring
|
||||||
|
|
||||||
has_doc = bool(doc)
|
has_doc = bool(doc)
|
||||||
|
|
||||||
metadata = extract_metadata(doc)
|
metadata = extract_metadata(doc)
|
||||||
@@ -1559,7 +1567,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
|
self.add_line(' ' + _('Bases: %s') % ', '.join(bases), sourcename)
|
||||||
|
|
||||||
def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]:
|
def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]:
|
||||||
members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer)
|
members = get_class_members(self.object, self.objpath, self.get_attr)
|
||||||
if not want_all:
|
if not want_all:
|
||||||
if not self.options.members:
|
if not self.options.members:
|
||||||
return False, [] # type: ignore
|
return False, [] # type: ignore
|
||||||
@@ -1567,16 +1575,18 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
|
|||||||
selected = []
|
selected = []
|
||||||
for name in self.options.members: # type: str
|
for name in self.options.members: # type: str
|
||||||
if name in members:
|
if name in members:
|
||||||
selected.append((name, members[name].value))
|
selected.append(ObjectMember(name, members[name].value,
|
||||||
|
docstring=members[name].docstring))
|
||||||
else:
|
else:
|
||||||
logger.warning(__('missing attribute %s in object %s') %
|
logger.warning(__('missing attribute %s in object %s') %
|
||||||
(name, self.fullname), type='autodoc')
|
(name, self.fullname), type='autodoc')
|
||||||
return False, selected
|
return False, selected
|
||||||
elif self.options.inherited_members:
|
elif self.options.inherited_members:
|
||||||
return False, [(m.name, m.value) for m in members.values()]
|
return False, [ObjectMember(m.name, m.value, docstring=m.docstring)
|
||||||
|
for m in members.values()]
|
||||||
else:
|
else:
|
||||||
return False, [(m.name, m.value) for m in members.values()
|
return False, [ObjectMember(m.name, m.value, docstring=m.docstring)
|
||||||
if m.directly_defined]
|
for m in members.values() if m.class_ == self.object]
|
||||||
|
|
||||||
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
||||||
if self.doc_as_attr:
|
if self.doc_as_attr:
|
||||||
@@ -1822,6 +1832,26 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
return isinstance(parent, ModuleDocumenter) and isattr
|
return isinstance(parent, ModuleDocumenter) and isattr
|
||||||
|
|
||||||
|
def update_annotations(self, parent: Any) -> None:
|
||||||
|
"""Update __annotations__ to support type_comment and so on."""
|
||||||
|
try:
|
||||||
|
annotations = inspect.getannotations(parent)
|
||||||
|
|
||||||
|
analyzer = ModuleAnalyzer.for_module(self.modname)
|
||||||
|
analyzer.analyze()
|
||||||
|
for (classname, attrname), annotation in analyzer.annotations.items():
|
||||||
|
if classname == '' and attrname not in annotations:
|
||||||
|
annotations[attrname] = annotation # type: ignore
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
|
ret = super().import_object(raiseerror)
|
||||||
|
if self.parent:
|
||||||
|
self.update_annotations(self.parent)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
def add_directive_header(self, sig: str) -> None:
|
def add_directive_header(self, sig: str) -> None:
|
||||||
super().add_directive_header(sig)
|
super().add_directive_header(sig)
|
||||||
sourcename = self.get_sourcename()
|
sourcename = self.get_sourcename()
|
||||||
@@ -1836,11 +1866,6 @@ class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin,
|
|||||||
if self.objpath[-1] in annotations:
|
if self.objpath[-1] in annotations:
|
||||||
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
||||||
self.add_line(' :type: ' + objrepr, sourcename)
|
self.add_line(' :type: ' + objrepr, sourcename)
|
||||||
else:
|
|
||||||
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
|
||||||
if self.analyzer and key in self.analyzer.annotations:
|
|
||||||
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
|
||||||
sourcename)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.options.no_value or self.should_suppress_value_header():
|
if self.options.no_value or self.should_suppress_value_header():
|
||||||
@@ -2033,6 +2058,28 @@ class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class NonDataDescriptorMixin(DataDocumenterMixinBase):
|
||||||
|
"""
|
||||||
|
Mixin for AttributeDocumenter to provide the feature for supporting non
|
||||||
|
data-descriptors.
|
||||||
|
|
||||||
|
.. note:: This mix-in must be inherited after other mix-ins. Otherwise, docstring
|
||||||
|
and :value: header will be suppressed unexpectedly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def should_suppress_value_header(self) -> bool:
|
||||||
|
return (inspect.isattributedescriptor(self.object) or
|
||||||
|
super().should_suppress_directive_header())
|
||||||
|
|
||||||
|
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
||||||
|
if not inspect.isattributedescriptor(self.object):
|
||||||
|
# the docstring of non datadescriptor is very probably the wrong thing
|
||||||
|
# to display
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
return super().get_doc(ignore) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class SlotsMixin(DataDocumenterMixinBase):
|
class SlotsMixin(DataDocumenterMixinBase):
|
||||||
"""
|
"""
|
||||||
Mixin for AttributeDocumenter to provide the feature for supporting __slots__.
|
Mixin for AttributeDocumenter to provide the feature for supporting __slots__.
|
||||||
@@ -2080,8 +2127,96 @@ class SlotsMixin(DataDocumenterMixinBase):
|
|||||||
return super().get_doc(ignore) # type: ignore
|
return super().get_doc(ignore) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase):
|
||||||
|
"""
|
||||||
|
Mixin for AttributeDocumenter to provide the feature for supporting uninitialized
|
||||||
|
instance attributes (that are defined in __init__() methods with doc-comments).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
self.attr = None #: This is a target of this mix-in.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get_attribute_comment(self, parent: Any) -> Optional[List[str]]:
|
||||||
|
try:
|
||||||
|
for cls in inspect.getmro(parent):
|
||||||
|
try:
|
||||||
|
module = safe_getattr(cls, '__module__')
|
||||||
|
qualname = safe_getattr(cls, '__qualname__')
|
||||||
|
|
||||||
|
analyzer = ModuleAnalyzer.for_module(module)
|
||||||
|
analyzer.analyze()
|
||||||
|
if qualname and self.objpath:
|
||||||
|
key = (qualname, self.objpath[-1])
|
||||||
|
if key in analyzer.attr_docs:
|
||||||
|
return list(analyzer.attr_docs[key])
|
||||||
|
except (AttributeError, PycodeError):
|
||||||
|
pass
|
||||||
|
except (AttributeError, PycodeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def is_uninitialized_instance_attribute(self, parent: Any) -> bool:
|
||||||
|
"""Check the subject is an attribute defined in __init__()."""
|
||||||
|
# An instance variable defined in __init__().
|
||||||
|
if self.get_attribute_comment(parent):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
|
"""Check the exisitence of uninitizlied instance attribute when failed to import
|
||||||
|
the attribute.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return super().import_object(raiseerror=True) # type: ignore
|
||||||
|
except ImportError as exc:
|
||||||
|
try:
|
||||||
|
ret = import_object(self.modname, self.objpath[:-1], 'class',
|
||||||
|
attrgetter=self.get_attr, # type: ignore
|
||||||
|
warningiserror=self.config.autodoc_warningiserror)
|
||||||
|
parent = ret[3]
|
||||||
|
if self.is_uninitialized_instance_attribute(parent):
|
||||||
|
self.object = UNINITIALIZED_ATTR
|
||||||
|
self.parent = parent
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if raiseerror:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
logger.warning(exc.args[0], type='autodoc', subtype='import_object')
|
||||||
|
self.env.note_reread()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def should_suppress_value_header(self) -> bool:
|
||||||
|
return (self.object is UNINITIALIZED_ATTR or
|
||||||
|
super().should_suppress_value_header())
|
||||||
|
|
||||||
|
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
||||||
|
if self.object is UNINITIALIZED_ATTR:
|
||||||
|
comment = self.get_attribute_comment(self.parent)
|
||||||
|
if comment:
|
||||||
|
return [comment]
|
||||||
|
|
||||||
|
return super().get_doc(ignore) # type: ignore
|
||||||
|
|
||||||
|
def add_content(self, more_content: Optional[StringList], no_docstring: bool = False
|
||||||
|
) -> None:
|
||||||
|
if self.object is UNINITIALIZED_ATTR:
|
||||||
|
self.analyzer = None
|
||||||
|
|
||||||
|
super().add_content(more_content, no_docstring=no_docstring) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore
|
class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore
|
||||||
TypeVarMixin, DocstringStripSignatureMixin, ClassLevelDocumenter):
|
TypeVarMixin, UninitializedInstanceAttributeMixin,
|
||||||
|
NonDataDescriptorMixin, DocstringStripSignatureMixin,
|
||||||
|
ClassLevelDocumenter):
|
||||||
"""
|
"""
|
||||||
Specialized Documenter subclass for attributes.
|
Specialized Documenter subclass for attributes.
|
||||||
"""
|
"""
|
||||||
@@ -2131,35 +2266,36 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# An instance variable defined inside __init__().
|
|
||||||
try:
|
|
||||||
analyzer = ModuleAnalyzer.for_module(self.modname)
|
|
||||||
attr_docs = analyzer.find_attr_docs()
|
|
||||||
if self.objpath:
|
|
||||||
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
|
||||||
if key in attr_docs:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
except PycodeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def update_annotations(self, parent: Any) -> None:
|
||||||
|
"""Update __annotations__ to support type_comment and so on."""
|
||||||
|
try:
|
||||||
|
annotations = inspect.getannotations(parent)
|
||||||
|
|
||||||
|
for cls in inspect.getmro(parent):
|
||||||
|
try:
|
||||||
|
module = safe_getattr(cls, '__module__')
|
||||||
|
qualname = safe_getattr(cls, '__qualname__')
|
||||||
|
|
||||||
|
analyzer = ModuleAnalyzer.for_module(module)
|
||||||
|
analyzer.analyze()
|
||||||
|
for (classname, attrname), annotation in analyzer.annotations.items():
|
||||||
|
if classname == qualname and attrname not in annotations:
|
||||||
|
annotations[attrname] = annotation # type: ignore
|
||||||
|
except (AttributeError, PycodeError):
|
||||||
|
pass
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
try:
|
try:
|
||||||
ret = super().import_object(raiseerror=True)
|
ret = super().import_object(raiseerror=True)
|
||||||
if inspect.isenumattribute(self.object):
|
if inspect.isenumattribute(self.object):
|
||||||
self.object = self.object.value
|
self.object = self.object.value
|
||||||
if inspect.isattributedescriptor(self.object):
|
|
||||||
self._datadescriptor = True
|
|
||||||
else:
|
|
||||||
# if it's not a data descriptor
|
|
||||||
self._datadescriptor = False
|
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
if self.isinstanceattribute():
|
if self.isinstanceattribute():
|
||||||
self.object = INSTANCEATTR
|
self.object = INSTANCEATTR
|
||||||
self._datadescriptor = False
|
|
||||||
ret = True
|
ret = True
|
||||||
elif raiseerror:
|
elif raiseerror:
|
||||||
raise
|
raise
|
||||||
@@ -2168,6 +2304,9 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
|
|||||||
self.env.note_reread()
|
self.env.note_reread()
|
||||||
ret = False
|
ret = False
|
||||||
|
|
||||||
|
if self.parent:
|
||||||
|
self.update_annotations(self.parent)
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def get_real_modname(self) -> str:
|
def get_real_modname(self) -> str:
|
||||||
@@ -2187,27 +2326,19 @@ class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type:
|
|||||||
if self.objpath[-1] in annotations:
|
if self.objpath[-1] in annotations:
|
||||||
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
|
||||||
self.add_line(' :type: ' + objrepr, sourcename)
|
self.add_line(' :type: ' + objrepr, sourcename)
|
||||||
else:
|
|
||||||
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
|
|
||||||
if self.analyzer and key in self.analyzer.annotations:
|
|
||||||
self.add_line(' :type: ' + self.analyzer.annotations[key],
|
|
||||||
sourcename)
|
|
||||||
|
|
||||||
# data descriptors do not have useful values
|
try:
|
||||||
if not self._datadescriptor:
|
if (self.object is INSTANCEATTR or self.options.no_value or
|
||||||
try:
|
self.should_suppress_value_header()):
|
||||||
if self.object is INSTANCEATTR or self.options.no_value:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
objrepr = object_description(self.object)
|
|
||||||
self.add_line(' :value: ' + objrepr, sourcename)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
pass
|
||||||
|
else:
|
||||||
|
objrepr = object_description(self.object)
|
||||||
|
self.add_line(' :value: ' + objrepr, sourcename)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
def get_doc(self, ignore: int = None) -> List[List[str]]:
|
||||||
if not self._datadescriptor:
|
if self.object is INSTANCEATTR:
|
||||||
# if it's not a data descriptor, its docstring is very probably the
|
|
||||||
# wrong thing to display
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ import traceback
|
|||||||
import warnings
|
import warnings
|
||||||
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple
|
from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple
|
||||||
|
|
||||||
from sphinx.pycode import ModuleAnalyzer
|
from sphinx.pycode import ModuleAnalyzer, PycodeError
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
from sphinx.util.inspect import getannotations, getslots, isclass, isenumclass, safe_getattr
|
from sphinx.util.inspect import (getannotations, getmro, getslots, isclass, isenumclass,
|
||||||
|
safe_getattr)
|
||||||
|
|
||||||
if False:
|
if False:
|
||||||
# For type annotation
|
# For type annotation
|
||||||
@@ -165,12 +166,9 @@ class Attribute(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
def _getmro(obj: Any) -> Tuple["Type", ...]:
|
def _getmro(obj: Any) -> Tuple["Type", ...]:
|
||||||
"""Get __mro__ from given *obj* safely."""
|
warnings.warn('sphinx.ext.autodoc.importer._getmro() is deprecated.',
|
||||||
__mro__ = safe_getattr(obj, '__mro__', None)
|
RemovedInSphinx40Warning)
|
||||||
if isinstance(__mro__, tuple):
|
return getmro(obj)
|
||||||
return __mro__
|
|
||||||
else:
|
|
||||||
return tuple()
|
|
||||||
|
|
||||||
|
|
||||||
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
||||||
@@ -218,7 +216,7 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# annotation only member (ex. attr: int)
|
# annotation only member (ex. attr: int)
|
||||||
for i, cls in enumerate(_getmro(subject)):
|
for i, cls in enumerate(getmro(subject)):
|
||||||
try:
|
try:
|
||||||
for name in getannotations(cls):
|
for name in getannotations(cls):
|
||||||
name = unmangle(cls, name)
|
name = unmangle(cls, name)
|
||||||
@@ -235,3 +233,88 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
|
|||||||
members[name] = Attribute(name, True, INSTANCEATTR)
|
members[name] = Attribute(name, True, INSTANCEATTR)
|
||||||
|
|
||||||
return members
|
return members
|
||||||
|
|
||||||
|
|
||||||
|
class ClassAttribute:
|
||||||
|
"""The attribute of the class."""
|
||||||
|
|
||||||
|
def __init__(self, cls: Any, name: str, value: Any, docstring: Optional[str] = None):
|
||||||
|
self.class_ = cls
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
self.docstring = docstring
|
||||||
|
|
||||||
|
|
||||||
|
def get_class_members(subject: Any, objpath: List[str], attrgetter: Callable
|
||||||
|
) -> Dict[str, ClassAttribute]:
|
||||||
|
"""Get members and attributes of target class."""
|
||||||
|
from sphinx.ext.autodoc import INSTANCEATTR
|
||||||
|
|
||||||
|
# the members directly defined in the class
|
||||||
|
obj_dict = attrgetter(subject, '__dict__', {})
|
||||||
|
|
||||||
|
members = {} # type: Dict[str, ClassAttribute]
|
||||||
|
|
||||||
|
# enum members
|
||||||
|
if isenumclass(subject):
|
||||||
|
for name, value in subject.__members__.items():
|
||||||
|
if name not in members:
|
||||||
|
members[name] = ClassAttribute(subject, name, value)
|
||||||
|
|
||||||
|
superclass = subject.__mro__[1]
|
||||||
|
for name in obj_dict:
|
||||||
|
if name not in superclass.__dict__:
|
||||||
|
value = safe_getattr(subject, name)
|
||||||
|
members[name] = ClassAttribute(subject, name, value)
|
||||||
|
|
||||||
|
# members in __slots__
|
||||||
|
try:
|
||||||
|
__slots__ = getslots(subject)
|
||||||
|
if __slots__:
|
||||||
|
from sphinx.ext.autodoc import SLOTSATTR
|
||||||
|
|
||||||
|
for name, docstring in __slots__.items():
|
||||||
|
members[name] = ClassAttribute(subject, name, SLOTSATTR, docstring)
|
||||||
|
except (AttributeError, TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# other members
|
||||||
|
for name in dir(subject):
|
||||||
|
try:
|
||||||
|
value = attrgetter(subject, name)
|
||||||
|
unmangled = unmangle(subject, name)
|
||||||
|
if unmangled and unmangled not in members:
|
||||||
|
if name in obj_dict:
|
||||||
|
members[unmangled] = ClassAttribute(subject, unmangled, value)
|
||||||
|
else:
|
||||||
|
members[unmangled] = ClassAttribute(None, unmangled, value)
|
||||||
|
except AttributeError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
for cls in getmro(subject):
|
||||||
|
# annotation only member (ex. attr: int)
|
||||||
|
try:
|
||||||
|
for name in getannotations(cls):
|
||||||
|
name = unmangle(cls, name)
|
||||||
|
if name and name not in members:
|
||||||
|
members[name] = ClassAttribute(cls, name, INSTANCEATTR)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# append instance attributes (cf. self.attr1) if analyzer knows
|
||||||
|
try:
|
||||||
|
modname = safe_getattr(cls, '__module__')
|
||||||
|
qualname = safe_getattr(cls, '__qualname__')
|
||||||
|
analyzer = ModuleAnalyzer.for_module(modname)
|
||||||
|
analyzer.analyze()
|
||||||
|
for (ns, name), docstring in analyzer.attr_docs.items():
|
||||||
|
if ns == qualname and name not in members:
|
||||||
|
members[name] = ClassAttribute(cls, name, INSTANCEATTR,
|
||||||
|
'\n'.join(docstring))
|
||||||
|
except (AttributeError, PycodeError):
|
||||||
|
pass
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return members
|
||||||
|
|||||||
@@ -174,6 +174,8 @@ class GoogleDocstring:
|
|||||||
'notes': self._parse_notes_section,
|
'notes': self._parse_notes_section,
|
||||||
'other parameters': self._parse_other_parameters_section,
|
'other parameters': self._parse_other_parameters_section,
|
||||||
'parameters': self._parse_parameters_section,
|
'parameters': self._parse_parameters_section,
|
||||||
|
'receive': self._parse_receives_section,
|
||||||
|
'receives': self._parse_receives_section,
|
||||||
'return': self._parse_returns_section,
|
'return': self._parse_returns_section,
|
||||||
'returns': self._parse_returns_section,
|
'returns': self._parse_returns_section,
|
||||||
'raise': self._parse_raises_section,
|
'raise': self._parse_raises_section,
|
||||||
@@ -709,6 +711,15 @@ class GoogleDocstring:
|
|||||||
lines.append('')
|
lines.append('')
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
def _parse_receives_section(self, section: str) -> List[str]:
|
||||||
|
if self._config.napoleon_use_param:
|
||||||
|
# Allow to declare multiple parameters at once (ex: x, y: int)
|
||||||
|
fields = self._consume_fields(multiple=True)
|
||||||
|
return self._format_docutils_params(fields)
|
||||||
|
else:
|
||||||
|
fields = self._consume_fields()
|
||||||
|
return self._format_fields(_('Receives'), fields)
|
||||||
|
|
||||||
def _parse_references_section(self, section: str) -> List[str]:
|
def _parse_references_section(self, section: str) -> List[str]:
|
||||||
use_admonition = self._config.napoleon_use_admonition_for_references
|
use_admonition = self._config.napoleon_use_admonition_for_references
|
||||||
return self._parse_generic_section(_('References'), use_admonition)
|
return self._parse_generic_section(_('References'), use_admonition)
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
|
{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and
|
||||||
(sidebars != []) %}
|
(sidebars != []) %}
|
||||||
{%- set url_root = pathto('', 1) %}
|
{%- set url_root = pathto('', 1) %}
|
||||||
{# XXX necessary? #}
|
{# URL root should never be #, then all links are fragments #}
|
||||||
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
|
||||||
{%- if not embedded and docstitle %}
|
{%- if not embedded and docstitle %}
|
||||||
{%- set titlesuffix = " — "|safe + docstitle|e %}
|
{%- set titlesuffix = " — "|safe + docstitle|e %}
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
{%- macro script() %}
|
{%- macro script() %}
|
||||||
<script id="documentation_options" data-url_root="{{ pathto('', 1) }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
|
<script id="documentation_options" data-url_root="{{ url_root }}" src="{{ pathto('_static/documentation_options.js', 1) }}"></script>
|
||||||
{%- for js in script_files %}
|
{%- for js in script_files %}
|
||||||
{{ js_tag(js) }}
|
{{ js_tag(js) }}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from sphinx.locale import __
|
|||||||
from sphinx.transforms import SphinxTransform
|
from sphinx.transforms import SphinxTransform
|
||||||
from sphinx.util import epoch_to_rfc1123, logging, requests, rfc1123_to_epoch, sha1
|
from sphinx.util import epoch_to_rfc1123, logging, requests, rfc1123_to_epoch, sha1
|
||||||
from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_uri
|
from sphinx.util.images import get_image_extension, guess_mimetype, parse_data_uri
|
||||||
from sphinx.util.osutil import ensuredir, movefile
|
from sphinx.util.osutil import ensuredir
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ class ImageDownloader(BaseImageConverter):
|
|||||||
# append a suffix if URI does not contain suffix
|
# append a suffix if URI does not contain suffix
|
||||||
ext = get_image_extension(mimetype)
|
ext = get_image_extension(mimetype)
|
||||||
newpath = os.path.join(self.imagedir, dirname, basename + ext)
|
newpath = os.path.join(self.imagedir, dirname, basename + ext)
|
||||||
movefile(path, newpath)
|
os.replace(path, newpath)
|
||||||
self.app.env.original_image_uri.pop(path)
|
self.app.env.original_image_uri.pop(path)
|
||||||
self.app.env.original_image_uri[newpath] = node['uri']
|
self.app.env.original_image_uri[newpath] = node['uri']
|
||||||
path = newpath
|
path = newpath
|
||||||
|
|||||||
@@ -271,6 +271,7 @@ class DocFieldTransformer:
|
|||||||
self.directive.domain,
|
self.directive.domain,
|
||||||
target,
|
target,
|
||||||
contnode=content[0],
|
contnode=content[0],
|
||||||
|
env=self.directive.state.document.settings.env
|
||||||
)
|
)
|
||||||
if _is_single_paragraph(field_body):
|
if _is_single_paragraph(field_body):
|
||||||
paragraph = cast(nodes.paragraph, field_body[0])
|
paragraph = cast(nodes.paragraph, field_body[0])
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import warnings
|
|||||||
from functools import partial, partialmethod
|
from functools import partial, partialmethod
|
||||||
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
|
from inspect import Parameter, isclass, ismethod, ismethoddescriptor, ismodule # NOQA
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, cast
|
from typing import Any, Callable, Dict, Mapping, Optional, Sequence, Tuple, cast
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||||
from sphinx.pycode.ast import ast # for py36-37
|
from sphinx.pycode.ast import ast # for py36-37
|
||||||
@@ -36,6 +36,10 @@ else:
|
|||||||
MethodDescriptorType = type(str.join)
|
MethodDescriptorType = type(str.join)
|
||||||
WrapperDescriptorType = type(dict.__dict__['fromkeys'])
|
WrapperDescriptorType = type(dict.__dict__['fromkeys'])
|
||||||
|
|
||||||
|
if False:
|
||||||
|
# For type annotation
|
||||||
|
from typing import Type # NOQA
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
memory_address_re = re.compile(r' at 0x[0-9a-f]{8,16}(?=>)', re.IGNORECASE)
|
||||||
@@ -166,6 +170,18 @@ def getannotations(obj: Any) -> Mapping[str, Any]:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def getmro(obj: Any) -> Tuple["Type", ...]:
|
||||||
|
"""Get __mro__ from given *obj* safely.
|
||||||
|
|
||||||
|
Raises AttributeError if given *obj* raises an error on accessing __mro__.
|
||||||
|
"""
|
||||||
|
__mro__ = safe_getattr(obj, '__mro__', None)
|
||||||
|
if isinstance(__mro__, tuple):
|
||||||
|
return __mro__
|
||||||
|
else:
|
||||||
|
return tuple()
|
||||||
|
|
||||||
|
|
||||||
def getslots(obj: Any) -> Optional[Dict]:
|
def getslots(obj: Any) -> Optional[Dict]:
|
||||||
"""Get __slots__ attribute of the class as dict.
|
"""Get __slots__ attribute of the class as dict.
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ from io import StringIO
|
|||||||
from os import path
|
from os import path
|
||||||
from typing import Any, Generator, Iterator, List, Optional, Type
|
from typing import Any, Generator, Iterator, List, Optional, Type
|
||||||
|
|
||||||
|
from sphinx.deprecation import RemovedInSphinx50Warning
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# for ALT Linux (#6712)
|
# for ALT Linux (#6712)
|
||||||
from sphinx.testing.path import path as Path
|
from sphinx.testing.path import path as Path
|
||||||
@@ -83,6 +85,9 @@ def mtimes_of_files(dirnames: List[str], suffix: str) -> Iterator[float]:
|
|||||||
|
|
||||||
def movefile(source: str, dest: str) -> None:
|
def movefile(source: str, dest: str) -> None:
|
||||||
"""Move a file, removing the destination if it exists."""
|
"""Move a file, removing the destination if it exists."""
|
||||||
|
warnings.warn('sphinx.util.osutil.movefile() is deprecated for removal. '
|
||||||
|
'Please use os.replace() instead.',
|
||||||
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
if os.path.exists(dest):
|
if os.path.exists(dest):
|
||||||
try:
|
try:
|
||||||
os.unlink(dest)
|
os.unlink(dest)
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ myint = int
|
|||||||
#: docstring
|
#: docstring
|
||||||
variable: myint
|
variable: myint
|
||||||
|
|
||||||
|
#: docstring
|
||||||
|
variable2 = None # type: myint
|
||||||
|
|
||||||
|
|
||||||
def sum(x: myint, y: myint) -> myint:
|
def sum(x: myint, y: myint) -> myint:
|
||||||
"""docstring"""
|
"""docstring"""
|
||||||
@@ -32,4 +35,7 @@ class Foo:
|
|||||||
"""docstring"""
|
"""docstring"""
|
||||||
|
|
||||||
#: docstring
|
#: docstring
|
||||||
attr: myint
|
attr1: myint
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.attr2: myint = None #: docstring
|
||||||
|
|||||||
10
tests/roots/test-ext-autodoc/target/instance_variable.py
Normal file
10
tests/roots/test-ext-autodoc/target/instance_variable.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
class Foo:
|
||||||
|
def __init__(self):
|
||||||
|
self.attr1 = None #: docstring foo
|
||||||
|
self.attr2 = None #: docstring foo
|
||||||
|
|
||||||
|
|
||||||
|
class Bar(Foo):
|
||||||
|
def __init__(self):
|
||||||
|
self.attr2 = None #: docstring bar
|
||||||
|
self.attr3 = None #: docstring bar
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
class Foo:
|
class Foo:
|
||||||
|
"""docstring"""
|
||||||
|
|
||||||
__slots__ = ['attr']
|
__slots__ = ['attr']
|
||||||
|
|
||||||
|
|
||||||
class Bar:
|
class Bar:
|
||||||
|
"""docstring"""
|
||||||
|
|
||||||
__slots__ = {'attr1': 'docstring of attr1',
|
__slots__ = {'attr1': 'docstring of attr1',
|
||||||
'attr2': 'docstring of attr2',
|
'attr2': 'docstring of attr2',
|
||||||
'attr3': None}
|
'attr3': None}
|
||||||
@@ -12,4 +16,6 @@ class Bar:
|
|||||||
|
|
||||||
|
|
||||||
class Baz:
|
class Baz:
|
||||||
|
"""docstring"""
|
||||||
|
|
||||||
__slots__ = 'attr'
|
__slots__ = 'attr'
|
||||||
|
|||||||
@@ -29,3 +29,6 @@ class Class:
|
|||||||
|
|
||||||
class Derived(Class):
|
class Derived(Class):
|
||||||
attr7: int
|
attr7: int
|
||||||
|
|
||||||
|
|
||||||
|
Alias = Derived
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
|
|||||||
if not srcdir.exists():
|
if not srcdir.exists():
|
||||||
(rootdir / 'test-root').copytree(srcdir)
|
(rootdir / 'test-root').copytree(srcdir)
|
||||||
except UnicodeEncodeError:
|
except UnicodeEncodeError:
|
||||||
|
# Now Python 3.7+ follows PEP-540 and uses utf-8 encoding for filesystem by default.
|
||||||
|
# So this error handling will be no longer used (after dropping python 3.6 support).
|
||||||
srcdir = basedir / 'all'
|
srcdir = basedir / 'all'
|
||||||
|
if not srcdir.exists():
|
||||||
|
(rootdir / 'test-root').copytree(srcdir)
|
||||||
else:
|
else:
|
||||||
# add a doc with a non-ASCII file name to the source dir
|
# add a doc with a non-ASCII file name to the source dir
|
||||||
(srcdir / (test_name + '.txt')).write_text(dedent("""
|
(srcdir / (test_name + '.txt')).write_text(dedent("""
|
||||||
|
|||||||
@@ -791,6 +791,53 @@ def test_canonical(app):
|
|||||||
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True)
|
assert domain.objects['_io.StringIO'] == ('index', 'io.StringIO', 'class', True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_info_field_list(app):
|
||||||
|
text = (".. py:module:: example\n"
|
||||||
|
".. py:class:: Class\n"
|
||||||
|
"\n"
|
||||||
|
" :param str name: blah blah\n"
|
||||||
|
" :param age: blah blah\n"
|
||||||
|
" :type age: int\n")
|
||||||
|
doctree = restructuredtext.parse(app, text)
|
||||||
|
print(doctree)
|
||||||
|
|
||||||
|
assert_node(doctree, (nodes.target,
|
||||||
|
addnodes.index,
|
||||||
|
addnodes.index,
|
||||||
|
[desc, ([desc_signature, ([desc_annotation, "class "],
|
||||||
|
[desc_addname, "example."],
|
||||||
|
[desc_name, "Class"])],
|
||||||
|
[desc_content, nodes.field_list, nodes.field])]))
|
||||||
|
assert_node(doctree[3][1][0][0],
|
||||||
|
([nodes.field_name, "Parameters"],
|
||||||
|
[nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph],
|
||||||
|
[nodes.list_item, nodes.paragraph])]))
|
||||||
|
|
||||||
|
# :param str name:
|
||||||
|
assert_node(doctree[3][1][0][0][1][0][0][0],
|
||||||
|
([addnodes.literal_strong, "name"],
|
||||||
|
" (",
|
||||||
|
[pending_xref, addnodes.literal_emphasis, "str"],
|
||||||
|
")",
|
||||||
|
" -- ",
|
||||||
|
"blah blah"))
|
||||||
|
assert_node(doctree[3][1][0][0][1][0][0][0][2], pending_xref,
|
||||||
|
refdomain="py", reftype="class", reftarget="str",
|
||||||
|
**{"py:module": "example", "py:class": "Class"})
|
||||||
|
|
||||||
|
# :param age: + :type age:
|
||||||
|
assert_node(doctree[3][1][0][0][1][0][1][0],
|
||||||
|
([addnodes.literal_strong, "age"],
|
||||||
|
" (",
|
||||||
|
[pending_xref, addnodes.literal_emphasis, "int"],
|
||||||
|
")",
|
||||||
|
" -- ",
|
||||||
|
"blah blah"))
|
||||||
|
assert_node(doctree[3][1][0][0][1][0][1][0][2], pending_xref,
|
||||||
|
refdomain="py", reftype="class", reftarget="int",
|
||||||
|
**{"py:module": "example", "py:class": "Class"})
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx(freshenv=True)
|
@pytest.mark.sphinx(freshenv=True)
|
||||||
def test_module_index(app):
|
def test_module_index(app):
|
||||||
text = (".. py:module:: docutils\n"
|
text = (".. py:module:: docutils\n"
|
||||||
|
|||||||
@@ -1171,6 +1171,8 @@ def test_slots(app):
|
|||||||
'.. py:class:: Bar()',
|
'.. py:class:: Bar()',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
'',
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:attribute:: Bar.attr1',
|
' .. py:attribute:: Bar.attr1',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
@@ -1191,6 +1193,8 @@ def test_slots(app):
|
|||||||
'.. py:class:: Baz()',
|
'.. py:class:: Baz()',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
'',
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:attribute:: Baz.attr',
|
' .. py:attribute:: Baz.attr',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
@@ -1199,6 +1203,8 @@ def test_slots(app):
|
|||||||
'.. py:class:: Foo()',
|
'.. py:class:: Foo()',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
'',
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:attribute:: Foo.attr',
|
' .. py:attribute:: Foo.attr',
|
||||||
' :module: target.slots',
|
' :module: target.slots',
|
||||||
@@ -1559,6 +1565,11 @@ def test_autodoc_typed_instance_variables(app):
|
|||||||
'.. py:module:: target.typed_vars',
|
'.. py:module:: target.typed_vars',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
'.. py:attribute:: Alias',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
'',
|
||||||
|
' alias of :class:`target.typed_vars.Derived`',
|
||||||
|
'',
|
||||||
'.. py:class:: Class()',
|
'.. py:class:: Class()',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
'',
|
'',
|
||||||
@@ -1667,9 +1678,31 @@ def test_autodoc_typed_inherited_instance_variables(app):
|
|||||||
'',
|
'',
|
||||||
' .. py:attribute:: Derived.attr3',
|
' .. py:attribute:: Derived.attr3',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
' :value: 0',
|
' :value: 0',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
|
' .. py:attribute:: Derived.attr4',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' attr4',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Derived.attr5',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' attr5',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Derived.attr6',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' attr6',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
' .. py:attribute:: Derived.attr7',
|
' .. py:attribute:: Derived.attr7',
|
||||||
' :module: target.typed_vars',
|
' :module: target.typed_vars',
|
||||||
' :type: int',
|
' :type: int',
|
||||||
|
|||||||
@@ -57,6 +57,19 @@ def test_autoattribute_typed_variable(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autoattribute_typed_variable_in_alias(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Alias.attr2')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Alias.attr2',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_autoattribute_instance_variable(app):
|
def test_autoattribute_instance_variable(app):
|
||||||
@@ -72,6 +85,21 @@ def test_autoattribute_instance_variable(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autoattribute_instance_variable_in_alias(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Alias.attr4')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Alias.attr4',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' attr4',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_autoattribute_slots_variable_list(app):
|
def test_autoattribute_slots_variable_list(app):
|
||||||
actual = do_autodoc(app, 'attribute', 'target.slots.Foo.attr')
|
actual = do_autodoc(app, 'attribute', 'target.slots.Foo.attr')
|
||||||
|
|||||||
@@ -51,6 +51,61 @@ def test_classes(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_instance_variable(app):
|
||||||
|
options = {'members': True}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr2',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr3',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_inherited_instance_variable(app):
|
||||||
|
options = {'members': True,
|
||||||
|
'inherited-members': True}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.instance_variable.Bar', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr1',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring foo',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr2',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr3',
|
||||||
|
' :module: target.instance_variable',
|
||||||
|
'',
|
||||||
|
' docstring bar',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_decorators(app):
|
def test_decorators(app):
|
||||||
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
actual = do_autodoc(app, 'class', 'target.decorator.Baz')
|
||||||
assert list(actual) == [
|
assert list(actual) == [
|
||||||
@@ -77,6 +132,32 @@ def test_decorators(app):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_slots_attribute(app):
|
||||||
|
options = {"members": None}
|
||||||
|
actual = do_autodoc(app, 'class', 'target.slots.Bar', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:class:: Bar()',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr1',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
' docstring of attr1',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Bar.attr2',
|
||||||
|
' :module: target.slots',
|
||||||
|
'',
|
||||||
|
' docstring of instance attr2',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
|
||||||
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
def test_show_inheritance_for_subclass_of_generic_type(app):
|
def test_show_inheritance_for_subclass_of_generic_type(app):
|
||||||
|
|||||||
@@ -707,7 +707,14 @@ def test_autodoc_type_aliases(app):
|
|||||||
' docstring',
|
' docstring',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:attribute:: Foo.attr',
|
' .. py:attribute:: Foo.attr1',
|
||||||
|
' :module: target.annotations',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Foo.attr2',
|
||||||
' :module: target.annotations',
|
' :module: target.annotations',
|
||||||
' :type: int',
|
' :type: int',
|
||||||
'',
|
'',
|
||||||
@@ -733,6 +740,14 @@ def test_autodoc_type_aliases(app):
|
|||||||
'',
|
'',
|
||||||
' docstring',
|
' docstring',
|
||||||
'',
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:data:: variable2',
|
||||||
|
' :module: target.annotations',
|
||||||
|
' :type: int',
|
||||||
|
' :value: None',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
]
|
]
|
||||||
|
|
||||||
# define aliases
|
# define aliases
|
||||||
@@ -749,7 +764,14 @@ def test_autodoc_type_aliases(app):
|
|||||||
' docstring',
|
' docstring',
|
||||||
'',
|
'',
|
||||||
'',
|
'',
|
||||||
' .. py:attribute:: Foo.attr',
|
' .. py:attribute:: Foo.attr1',
|
||||||
|
' :module: target.annotations',
|
||||||
|
' :type: myint',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
' .. py:attribute:: Foo.attr2',
|
||||||
' :module: target.annotations',
|
' :module: target.annotations',
|
||||||
' :type: myint',
|
' :type: myint',
|
||||||
'',
|
'',
|
||||||
@@ -775,6 +797,14 @@ def test_autodoc_type_aliases(app):
|
|||||||
'',
|
'',
|
||||||
' docstring',
|
' docstring',
|
||||||
'',
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:data:: variable2',
|
||||||
|
' :module: target.annotations',
|
||||||
|
' :type: myint',
|
||||||
|
' :value: None',
|
||||||
|
'',
|
||||||
|
' docstring',
|
||||||
|
'',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -303,6 +303,34 @@ class GoogleDocstringTest(BaseDocstringTest):
|
|||||||
"""
|
"""
|
||||||
Single line summary
|
Single line summary
|
||||||
|
|
||||||
|
Receive:
|
||||||
|
arg1 (list(int)): Description
|
||||||
|
arg2 (list[int]): Description
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
:Receives: * **arg1** (*list(int)*) -- Description
|
||||||
|
* **arg2** (*list[int]*) -- Description
|
||||||
|
"""
|
||||||
|
), (
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
Receives:
|
||||||
|
arg1 (list(int)): Description
|
||||||
|
arg2 (list[int]): Description
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
:Receives: * **arg1** (*list(int)*) -- Description
|
||||||
|
* **arg2** (*list[int]*) -- Description
|
||||||
|
"""
|
||||||
|
), (
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
Yield:
|
Yield:
|
||||||
str:Extended
|
str:Extended
|
||||||
description of yielded value
|
description of yielded value
|
||||||
@@ -1263,6 +1291,48 @@ class NumpyDocstringTest(BaseDocstringTest):
|
|||||||
"""
|
"""
|
||||||
Single line summary
|
Single line summary
|
||||||
|
|
||||||
|
Receive
|
||||||
|
-------
|
||||||
|
arg1:str
|
||||||
|
Extended
|
||||||
|
description of arg1
|
||||||
|
arg2 : int
|
||||||
|
Extended
|
||||||
|
description of arg2
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
:Receives: * **arg1** (:class:`str`) -- Extended
|
||||||
|
description of arg1
|
||||||
|
* **arg2** (:class:`int`) -- Extended
|
||||||
|
description of arg2
|
||||||
|
"""
|
||||||
|
), (
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
Receives
|
||||||
|
--------
|
||||||
|
arg1:str
|
||||||
|
Extended
|
||||||
|
description of arg1
|
||||||
|
arg2 : int
|
||||||
|
Extended
|
||||||
|
description of arg2
|
||||||
|
""",
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
|
:Receives: * **arg1** (:class:`str`) -- Extended
|
||||||
|
description of arg1
|
||||||
|
* **arg2** (:class:`int`) -- Extended
|
||||||
|
description of arg2
|
||||||
|
"""
|
||||||
|
), (
|
||||||
|
"""
|
||||||
|
Single line summary
|
||||||
|
|
||||||
Yield
|
Yield
|
||||||
-----
|
-----
|
||||||
str
|
str
|
||||||
|
|||||||
Reference in New Issue
Block a user