mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 8437_safety_make_clean
This commit is contained in:
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -42,12 +42,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
- name: Set up Python ${{ matrix.python }} (deadsnakes)
|
- name: Set up Python ${{ matrix.python }} (deadsnakes)
|
||||||
uses: deadsnakes/action@v1.0.0
|
uses: deadsnakes/action@v2.0.1
|
||||||
if: endsWith(matrix.python, '-dev')
|
if: endsWith(matrix.python, '-dev')
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
env:
|
|
||||||
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
|
|
||||||
- name: Check Python version
|
- name: Check Python version
|
||||||
run: python --version
|
run: python --version
|
||||||
- name: Install graphviz
|
- name: Install graphviz
|
||||||
|
|||||||
7
CHANGES
7
CHANGES
@@ -14,6 +14,7 @@ Deprecated
|
|||||||
----------
|
----------
|
||||||
|
|
||||||
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
|
* The ``follow_wrapped`` argument of ``sphinx.util.inspect.signature()``
|
||||||
|
* ``sphinx.ext.autodoc.DataDeclarationDocumenter``
|
||||||
* ``sphinx.util.requests.is_ssl_error()``
|
* ``sphinx.util.requests.is_ssl_error()``
|
||||||
|
|
||||||
Features added
|
Features added
|
||||||
@@ -42,6 +43,12 @@ Bugs fixed
|
|||||||
is decorated
|
is decorated
|
||||||
* #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables
|
* #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables
|
||||||
and attributes
|
and attributes
|
||||||
|
* #8443: autodoc: autodata directive can't create document for PEP-526 based
|
||||||
|
type annotated variables
|
||||||
|
* #8443: autodoc: autoattribute directive can't create document for PEP-526
|
||||||
|
based uninitalized variables
|
||||||
|
* #8419: html search: Do not load ``language_data.js`` in non-search pages
|
||||||
|
* #8454: graphviz: The layout option for graph and digraph directives don't work
|
||||||
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
|
* #8437: Makefile: ``make clean`` with empty BUILDDIR is dangerous
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ The following is a list of deprecated interfaces.
|
|||||||
- 5.0
|
- 5.0
|
||||||
- N/A
|
- N/A
|
||||||
|
|
||||||
|
* - ``sphinx.ext.autodoc.DataDeclarationDocumenter``
|
||||||
|
- 3.4
|
||||||
|
- 5.0
|
||||||
|
- ``sphinx.ext.autodoc.DataDocumenter``
|
||||||
|
|
||||||
* - ``sphinx.util.requests.is_ssl_error()``
|
* - ``sphinx.util.requests.is_ssl_error()``
|
||||||
- 3.4
|
- 3.4
|
||||||
- 5.0
|
- 5.0
|
||||||
|
|||||||
@@ -301,7 +301,6 @@ class StandaloneHTMLBuilder(Builder):
|
|||||||
self.add_js_file('jquery.js')
|
self.add_js_file('jquery.js')
|
||||||
self.add_js_file('underscore.js')
|
self.add_js_file('underscore.js')
|
||||||
self.add_js_file('doctools.js')
|
self.add_js_file('doctools.js')
|
||||||
self.add_js_file('language_data.js')
|
|
||||||
|
|
||||||
for filename, attrs in self.app.registry.js_files:
|
for filename, attrs in self.app.registry.js_files:
|
||||||
self.add_js_file(filename, **attrs)
|
self.add_js_file(filename, **attrs)
|
||||||
|
|||||||
@@ -961,7 +961,7 @@ class ModuleDocumenter(Documenter):
|
|||||||
def __init__(self, *args: Any) -> None:
|
def __init__(self, *args: Any) -> None:
|
||||||
super().__init__(*args)
|
super().__init__(*args)
|
||||||
merge_members_option(self.options)
|
merge_members_option(self.options)
|
||||||
self.__all__ = None
|
self.__all__ = None # type: Optional[Sequence[str]]
|
||||||
|
|
||||||
@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
|
||||||
@@ -985,26 +985,16 @@ class ModuleDocumenter(Documenter):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
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(raiseerror)
|
ret = super().import_object(raiseerror)
|
||||||
|
|
||||||
if not self.options.ignore_module_all:
|
try:
|
||||||
__all__ = getattr(self.object, '__all__', None)
|
if not self.options.ignore_module_all:
|
||||||
if is_valid_module_all(__all__):
|
self.__all__ = inspect.getall(self.object)
|
||||||
# valid __all__ found. copy it to self.__all__
|
except ValueError as exc:
|
||||||
self.__all__ = __all__
|
# invalid __all__ found.
|
||||||
elif __all__:
|
logger.warning(__('__all__ should be a list of strings, not %r '
|
||||||
# invalid __all__ found.
|
'(in module %s) -- ignoring __all__') %
|
||||||
logger.warning(__('__all__ should be a list of strings, not %r '
|
(exc.args[0], self.fullname), type='autodoc')
|
||||||
'(in module %s) -- ignoring __all__') %
|
|
||||||
(__all__, self.fullname), type='autodoc')
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -1697,6 +1687,28 @@ class DataDocumenter(ModuleLevelDocumenter):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
return isinstance(parent, ModuleDocumenter) and isattr
|
return isinstance(parent, ModuleDocumenter) and isattr
|
||||||
|
|
||||||
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
|
try:
|
||||||
|
return super().import_object(raiseerror=True)
|
||||||
|
except ImportError as exc:
|
||||||
|
# annotation only instance variable (PEP-526)
|
||||||
|
try:
|
||||||
|
self.parent = importlib.import_module(self.modname)
|
||||||
|
annotations = get_type_hints(self.parent, None,
|
||||||
|
self.config.autodoc_type_aliases)
|
||||||
|
if self.objpath[-1] in annotations:
|
||||||
|
self.object = UNINITIALIZED_ATTR
|
||||||
|
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 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()
|
||||||
@@ -1733,6 +1745,13 @@ class DataDocumenter(ModuleLevelDocumenter):
|
|||||||
return self.get_attr(self.parent or self.object, '__module__', None) \
|
return self.get_attr(self.parent or self.object, '__module__', None) \
|
||||||
or self.modname
|
or self.modname
|
||||||
|
|
||||||
|
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
||||||
|
if self.object is UNINITIALIZED_ATTR:
|
||||||
|
# suppress docstring of the value
|
||||||
|
super().add_content(more_content, no_docstring=True)
|
||||||
|
else:
|
||||||
|
super().add_content(more_content, no_docstring=no_docstring)
|
||||||
|
|
||||||
|
|
||||||
class DataDeclarationDocumenter(DataDocumenter):
|
class DataDeclarationDocumenter(DataDocumenter):
|
||||||
"""
|
"""
|
||||||
@@ -1746,30 +1765,10 @@ class DataDeclarationDocumenter(DataDocumenter):
|
|||||||
# must be higher than AttributeDocumenter
|
# must be higher than AttributeDocumenter
|
||||||
priority = 11
|
priority = 11
|
||||||
|
|
||||||
@classmethod
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any
|
warnings.warn("%s is deprecated." % self.__class__.__name__,
|
||||||
) -> bool:
|
RemovedInSphinx50Warning, stacklevel=2)
|
||||||
"""This documents only INSTANCEATTR members."""
|
super().__init__(*args, **kwargs)
|
||||||
return (isinstance(parent, ModuleDocumenter) and
|
|
||||||
isattr and
|
|
||||||
member is INSTANCEATTR)
|
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
|
||||||
"""Never import anything."""
|
|
||||||
# disguise as a data
|
|
||||||
self.objtype = 'data'
|
|
||||||
self.object = UNINITIALIZED_ATTR
|
|
||||||
try:
|
|
||||||
# import module to obtain type annotation
|
|
||||||
self.parent = importlib.import_module(self.modname)
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
|
|
||||||
"""Never try to get a docstring from the object."""
|
|
||||||
super().add_content(more_content, no_docstring=True)
|
|
||||||
|
|
||||||
|
|
||||||
class GenericAliasDocumenter(DataDocumenter):
|
class GenericAliasDocumenter(DataDocumenter):
|
||||||
@@ -2036,6 +2035,22 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
|
|
||||||
def isinstanceattribute(self) -> bool:
|
def isinstanceattribute(self) -> bool:
|
||||||
"""Check the subject is an instance attribute."""
|
"""Check the subject is an instance attribute."""
|
||||||
|
# uninitialized instance variable (PEP-526)
|
||||||
|
with mock(self.config.autodoc_mock_imports):
|
||||||
|
try:
|
||||||
|
ret = import_object(self.modname, self.objpath[:-1], 'class',
|
||||||
|
attrgetter=self.get_attr,
|
||||||
|
warningiserror=self.config.autodoc_warningiserror)
|
||||||
|
self.parent = ret[3]
|
||||||
|
annotations = get_type_hints(self.parent, None,
|
||||||
|
self.config.autodoc_type_aliases)
|
||||||
|
if self.objpath[-1] in annotations:
|
||||||
|
self.object = UNINITIALIZED_ATTR
|
||||||
|
return True
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# An instance variable defined inside __init__().
|
||||||
try:
|
try:
|
||||||
analyzer = ModuleAnalyzer.for_module(self.modname)
|
analyzer = ModuleAnalyzer.for_module(self.modname)
|
||||||
attr_docs = analyzer.find_attr_docs()
|
attr_docs = analyzer.find_attr_docs()
|
||||||
@@ -2046,7 +2061,9 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
except PycodeError:
|
except PycodeError:
|
||||||
return False
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def import_object(self, raiseerror: bool = False) -> bool:
|
def import_object(self, raiseerror: bool = False) -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -2282,7 +2299,6 @@ def setup(app: Sphinx) -> Dict[str, Any]:
|
|||||||
app.add_autodocumenter(ClassDocumenter)
|
app.add_autodocumenter(ClassDocumenter)
|
||||||
app.add_autodocumenter(ExceptionDocumenter)
|
app.add_autodocumenter(ExceptionDocumenter)
|
||||||
app.add_autodocumenter(DataDocumenter)
|
app.add_autodocumenter(DataDocumenter)
|
||||||
app.add_autodocumenter(DataDeclarationDocumenter)
|
|
||||||
app.add_autodocumenter(GenericAliasDocumenter)
|
app.add_autodocumenter(GenericAliasDocumenter)
|
||||||
app.add_autodocumenter(TypeVarDocumenter)
|
app.add_autodocumenter(TypeVarDocumenter)
|
||||||
app.add_autodocumenter(FunctionDocumenter)
|
app.add_autodocumenter(FunctionDocumenter)
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ AutosummaryEntry = NamedTuple('AutosummaryEntry', [('name', str),
|
|||||||
|
|
||||||
|
|
||||||
def setup_documenters(app: Any) -> None:
|
def setup_documenters(app: Any) -> None:
|
||||||
from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter,
|
from sphinx.ext.autodoc import (AttributeDocumenter, ClassDocumenter, DataDocumenter,
|
||||||
DataDeclarationDocumenter, DataDocumenter,
|
|
||||||
DecoratorDocumenter, ExceptionDocumenter,
|
DecoratorDocumenter, ExceptionDocumenter,
|
||||||
FunctionDocumenter, GenericAliasDocumenter,
|
FunctionDocumenter, GenericAliasDocumenter,
|
||||||
InstanceAttributeDocumenter, MethodDocumenter,
|
InstanceAttributeDocumenter, MethodDocumenter,
|
||||||
@@ -96,8 +95,7 @@ def setup_documenters(app: Any) -> None:
|
|||||||
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
ModuleDocumenter, ClassDocumenter, ExceptionDocumenter, DataDocumenter,
|
||||||
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
FunctionDocumenter, MethodDocumenter, AttributeDocumenter,
|
||||||
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
InstanceAttributeDocumenter, DecoratorDocumenter, PropertyDocumenter,
|
||||||
SlotsAttributeDocumenter, DataDeclarationDocumenter, GenericAliasDocumenter,
|
SlotsAttributeDocumenter, GenericAliasDocumenter, SingledispatchFunctionDocumenter,
|
||||||
SingledispatchFunctionDocumenter,
|
|
||||||
] # type: List[Type[Documenter]]
|
] # type: List[Type[Documenter]]
|
||||||
for documenter in documenters:
|
for documenter in documenters:
|
||||||
app.registry.add_documenter(documenter.objtype, documenter)
|
app.registry.add_documenter(documenter.objtype, documenter)
|
||||||
|
|||||||
@@ -182,7 +182,8 @@ class GraphvizSimple(SphinxDirective):
|
|||||||
'alt': directives.unchanged,
|
'alt': directives.unchanged,
|
||||||
'align': align_spec,
|
'align': align_spec,
|
||||||
'caption': directives.unchanged,
|
'caption': directives.unchanged,
|
||||||
'graphviz_dot': directives.unchanged,
|
'layout': directives.unchanged,
|
||||||
|
'graphviz_dot': directives.unchanged, # an old alias of `layout` option
|
||||||
'name': directives.unchanged,
|
'name': directives.unchanged,
|
||||||
'class': directives.class_option,
|
'class': directives.class_option,
|
||||||
}
|
}
|
||||||
@@ -194,6 +195,8 @@ class GraphvizSimple(SphinxDirective):
|
|||||||
node['options'] = {'docname': self.env.docname}
|
node['options'] = {'docname': self.env.docname}
|
||||||
if 'graphviz_dot' in self.options:
|
if 'graphviz_dot' in self.options:
|
||||||
node['options']['graphviz_dot'] = self.options['graphviz_dot']
|
node['options']['graphviz_dot'] = self.options['graphviz_dot']
|
||||||
|
if 'layout' in self.options:
|
||||||
|
node['options']['graphviz_dot'] = self.options['layout']
|
||||||
if 'alt' in self.options:
|
if 'alt' in self.options:
|
||||||
node['alt'] = self.options['alt']
|
node['alt'] = self.options['alt']
|
||||||
if 'align' in self.options:
|
if 'align' in self.options:
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
{%- block scripts %}
|
{%- block scripts %}
|
||||||
{{ super() }}
|
{{ super() }}
|
||||||
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
|
<script src="{{ pathto('_static/searchtools.js', 1) }}"></script>
|
||||||
|
<script src="{{ pathto('_static/language_data.js', 1) }}"></script>
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
|
<script src="{{ pathto('searchindex.js', 1) }}" defer></script>
|
||||||
|
|||||||
@@ -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, List, Mapping, Optional, Tuple, cast
|
from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, cast
|
||||||
|
|
||||||
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
from sphinx.deprecation import RemovedInSphinx40Warning, RemovedInSphinx50Warning
|
||||||
from sphinx.pycode.ast import ast # for py35-37
|
from sphinx.pycode.ast import ast # for py35-37
|
||||||
@@ -137,6 +137,22 @@ def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
def getall(obj: Any) -> Optional[Sequence[str]]:
|
||||||
|
"""Get __all__ attribute of the module as dict.
|
||||||
|
|
||||||
|
Return None if given *obj* does not have __all__.
|
||||||
|
Raises ValueError if given *obj* have invalid __all__.
|
||||||
|
"""
|
||||||
|
__all__ = safe_getattr(obj, '__all__', None)
|
||||||
|
if __all__ is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
if (isinstance(__all__, (list, tuple)) and all(isinstance(e, str) for e in __all__)):
|
||||||
|
return __all__
|
||||||
|
else:
|
||||||
|
raise ValueError(__all__)
|
||||||
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from test_ext_autodoc import do_autodoc
|
from test_ext_autodoc import do_autodoc
|
||||||
|
|
||||||
@@ -39,3 +41,31 @@ def test_autoattribute_novalue(app):
|
|||||||
' should be documented -- süß',
|
' should be documented -- süß',
|
||||||
'',
|
'',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@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(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Class.attr2')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Class.attr2',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@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(app):
|
||||||
|
actual = do_autodoc(app, 'attribute', 'target.typed_vars.Class.attr4')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:attribute:: Class.attr4',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: int',
|
||||||
|
'',
|
||||||
|
' attr4',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
:license: BSD, see LICENSE for details.
|
:license: BSD, see LICENSE for details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from test_ext_autodoc import do_autodoc
|
from test_ext_autodoc import do_autodoc
|
||||||
|
|
||||||
@@ -39,3 +41,34 @@ def test_autodata_novalue(app):
|
|||||||
' documentation for the integer',
|
' documentation for the integer',
|
||||||
'',
|
'',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autodata_typed_variable(app):
|
||||||
|
actual = do_autodoc(app, 'data', 'target.typed_vars.attr2')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:data:: attr2',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: str',
|
||||||
|
'',
|
||||||
|
' attr2',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(sys.version_info < (3, 6), reason='python 3.6+ is required.')
|
||||||
|
@pytest.mark.sphinx('html', testroot='ext-autodoc')
|
||||||
|
def test_autodata_type_comment(app):
|
||||||
|
actual = do_autodoc(app, 'data', 'target.typed_vars.attr3')
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:data:: attr3',
|
||||||
|
' :module: target.typed_vars',
|
||||||
|
' :type: str',
|
||||||
|
" :value: ''",
|
||||||
|
'',
|
||||||
|
' attr3',
|
||||||
|
'',
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user