Merge branch '4.x' into 4.0.x

This commit is contained in:
Takeshi KOMIYA
2021-06-13 17:20:01 +09:00
committed by GitHub
245 changed files with 22840 additions and 17391 deletions

View File

@@ -3,6 +3,7 @@
module_attr
C.class_attr
C.instance_attr
C.prop_attr1
C.prop_attr2
C.C2
@@ -51,6 +52,12 @@ class C:
#: value is integer.
class_attr = 42
def __init__(self):
#: This is an instance attribute
#:
#: value is a string
self.instance_attr = "42"
def _prop_attr_get(self):
"""
This is a function docstring

View File

@@ -0,0 +1,4 @@
.. c:function:: void f(int a, int *b)
:param int a:
:param int* b:

View File

@@ -0,0 +1,5 @@
.. cpp:function:: void f()
:throws int:
:throws int*:

View File

@@ -4,5 +4,9 @@ domain-py-smart_reference
.. py:class:: Name
:module: foo
:param name: blah blah
:type name: foo.Name
:param age: blah blah
:type age: foo.Age
.. py:function:: hello(name: foo.Name, age: foo.Age)

View File

@@ -10,4 +10,6 @@
.. autofunction:: target.typehints.incr
.. autofunction:: target.overload.sum
.. autofunction:: target.typehints.tuple_args

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import io
from typing import overload
myint = int
@@ -11,6 +12,10 @@ variable: myint
variable2 = None # type: myint
def read(r: io.BytesIO) -> io.StringIO:
"""docstring"""
def sum(x: myint, y: myint) -> myint:
"""docstring"""
return x + y

View File

@@ -30,3 +30,6 @@ class Quux(List[Union[int, float]]):
Alias = Foo
#: docstring
OtherAlias = Bar

View File

@@ -8,3 +8,4 @@ class Bar(Foo):
def __init__(self):
self.attr2 = None #: docstring bar
self.attr3 = None #: docstring bar
self.attr4 = None

View File

@@ -0,0 +1,2 @@
def foo():
""":meta metadata-only-docstring:"""

View File

@@ -0,0 +1,14 @@
undocumented = 1
#: docstring
documented = 1
undoc_annotated: int
#: docstring
annotated: int
__special__ = 1
#: docstring
__documented_special__ = 1

View File

@@ -15,6 +15,7 @@ def func(arg, kwarg=None):
@func.register(int)
@func.register(float)
def _func_int(arg, kwarg=None):
"""A function for int."""
pass

View File

@@ -10,6 +10,7 @@ class Foo:
pass
@meth.register(int)
@meth.register(float)
def _meth_int(self, arg, kwarg=None):
"""A method for int."""
pass

View File

@@ -5,7 +5,7 @@ Translation Tips
-----------------
.. _download Sphinx: https://pypi.org/project/Sphinx/
.. _Docutils site: http://docutils.sourceforge.net/
.. _Docutils site: https://docutils.sourceforge.io/
.. _Sphinx site: http://sphinx-doc.org/

View File

@@ -13,6 +13,8 @@ Some additional anchors to exercise ignore code
* `Complete nonsense <https://localhost:7777/doesnotexist>`_
* `Example valid local file <conf.py>`_
* `Example invalid local file <path/to/notfound>`_
* https://github.com/sphinx-doc/sphinx#documentation
* https://github.com/sphinx-doc/sphinx#user-content-testing
.. image:: https://www.google.com/image.png
.. figure:: https://www.google.com/image2.png

View File

@@ -0,0 +1 @@
nitpicky = True

View File

@@ -0,0 +1,7 @@
test-nitpicky-warnings
======================
:py:const:`prefix.anything.postfix`
:py:class:`prefix.anything`
:py:class:`anything.postfix`
:js:class:`prefix.anything.postfix`

View File

@@ -65,8 +65,8 @@ def test_defaults_json(app):
"info"]:
assert attr in row
assert len(content.splitlines()) == 10
assert len(rows) == 10
assert len(content.splitlines()) == 12
assert len(rows) == 12
# the output order of the rows is not stable
# due to possible variance in network latency
rowsby = {row["uri"]: row for row in rows}
@@ -87,7 +87,7 @@ def test_defaults_json(app):
assert dnerow['uri'] == 'https://localhost:7777/doesnotexist'
assert rowsby['https://www.google.com/image2.png'] == {
'filename': 'links.txt',
'lineno': 18,
'lineno': 20,
'status': 'broken',
'code': 0,
'uri': 'https://www.google.com/image2.png',
@@ -101,6 +101,10 @@ def test_defaults_json(app):
# images should fail
assert "Not Found for url: https://www.google.com/image.png" in \
rowsby["https://www.google.com/image.png"]["info"]
# The anchor of the URI for github.com is automatically modified
assert 'https://github.com/sphinx-doc/sphinx#documentation' not in rowsby
assert 'https://github.com/sphinx-doc/sphinx#user-content-documentation' in rowsby
assert 'https://github.com/sphinx-doc/sphinx#user-content-testing' in rowsby
@pytest.mark.sphinx(

View File

@@ -34,7 +34,7 @@ def test_all(app, status, warning):
confoverrides={'man_make_section_directory': True})
def test_man_make_section_directory(app, status, warning):
app.build()
assert (app.outdir / '1' / 'python.1').exists()
assert (app.outdir / 'man1' / 'python.1').exists()
@pytest.mark.sphinx('man', testroot='directive-code')

View File

@@ -74,6 +74,11 @@ def test_core_config(app, status, warning):
assert cfg['project'] == cfg.project == 'Sphinx Tests'
def test_config_not_found(tempdir):
with pytest.raises(ConfigError):
Config.read(tempdir)
def test_extension_values():
config = Config()
@@ -311,3 +316,77 @@ def test_check_enum_for_list_failed(logger):
config.init_values()
check_confval_types(None, config)
assert logger.warning.called
nitpick_warnings = [
"WARNING: py:const reference target not found: prefix.anything.postfix",
"WARNING: py:class reference target not found: prefix.anything",
"WARNING: py:class reference target not found: anything.postfix",
"WARNING: js:class reference target not found: prefix.anything.postfix",
]
@pytest.mark.sphinx(testroot='nitpicky-warnings')
def test_nitpick_base(app, status, warning):
app.builder.build_all()
warning = warning.getvalue().strip().split('\n')
assert len(warning) == len(nitpick_warnings)
for actual, expected in zip(warning, nitpick_warnings):
assert expected in actual
@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
'nitpick_ignore': [
('py:const', 'prefix.anything.postfix'),
('py:class', 'prefix.anything'),
('py:class', 'anything.postfix'),
('js:class', 'prefix.anything.postfix'),
],
})
def test_nitpick_ignore(app, status, warning):
app.builder.build_all()
assert not len(warning.getvalue().strip())
@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
'nitpick_ignore_regex': [
(r'py:.*', r'.*postfix'),
(r'.*:class', r'prefix.*'),
]
})
def test_nitpick_ignore_regex1(app, status, warning):
app.builder.build_all()
assert not len(warning.getvalue().strip())
@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
'nitpick_ignore_regex': [
(r'py:.*', r'prefix.*'),
(r'.*:class', r'.*postfix'),
]
})
def test_nitpick_ignore_regex2(app, status, warning):
app.builder.build_all()
assert not len(warning.getvalue().strip())
@pytest.mark.sphinx(testroot='nitpicky-warnings', confoverrides={
'nitpick_ignore_regex': [
# None of these should match
(r'py:', r'.*'),
(r':class', r'.*'),
(r'', r'.*'),
(r'.*', r'anything'),
(r'.*', r'prefix'),
(r'.*', r'postfix'),
(r'.*', r''),
]
})
def test_nitpick_ignore_regex_fullmatch(app, status, warning):
app.builder.build_all()
warning = warning.getvalue().strip().split('\n')
assert len(warning) == len(nitpick_warnings)
for actual, expected in zip(warning, nitpick_warnings):
assert expected in actual

View File

@@ -112,7 +112,7 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
asTextOutput + ';' if asTextOutput is not None else None)
def test_expressions():
def test_domain_c_ast_expressions():
def exprCheck(expr, output=None):
class Config:
c_id_attributes = ["id_attr"]
@@ -269,7 +269,7 @@ def test_expressions():
exprCheck('a or_eq 5')
def test_type_definitions():
def test_domain_c_ast_type_definitions():
check('type', "{key}T", {1: "T"})
check('type', "{key}bool *b", {1: 'b'}, key='typedef')
@@ -290,7 +290,7 @@ def test_type_definitions():
{1: 'gpio_callback_t'}, key='typedef')
def test_macro_definitions():
def test_domain_c_ast_macro_definitions():
check('macro', 'M', {1: 'M'})
check('macro', 'M()', {1: 'M'})
check('macro', 'M(arg)', {1: 'M'})
@@ -306,7 +306,7 @@ def test_macro_definitions():
check('macro', 'M(arg1, arg2..., arg3)', {1: 'M'})
def test_member_definitions():
def test_domain_c_ast_member_definitions():
check('member', 'void a', {1: 'a'})
check('member', '_Bool a', {1: 'a'})
check('member', 'bool a', {1: 'a'})
@@ -364,7 +364,7 @@ def test_member_definitions():
check('member', 'int b : 3', {1: 'b'})
def test_function_definitions():
def test_domain_c_ast_function_definitions():
check('function', 'void f()', {1: 'f'})
check('function', 'void f(int)', {1: 'f'})
check('function', 'void f(int i)', {1: 'f'})
@@ -424,29 +424,29 @@ def test_function_definitions():
check('function', 'void f(void (*p)(int, double), int i)', {1: 'f'})
def test_nested_name():
def test_domain_c_ast_nested_name():
check('struct', '{key}.A', {1: "A"})
check('struct', '{key}.A.B', {1: "A.B"})
check('function', 'void f(.A a)', {1: "f"})
check('function', 'void f(.A.B a)', {1: "f"})
def test_struct_definitions():
def test_domain_c_ast_struct_definitions():
check('struct', '{key}A', {1: 'A'})
def test_union_definitions():
def test_domain_c_ast_union_definitions():
check('union', '{key}A', {1: 'A'})
def test_enum_definitions():
def test_domain_c_ast_enum_definitions():
check('enum', '{key}A', {1: 'A'})
check('enumerator', '{key}A', {1: 'A'})
check('enumerator', '{key}A = 42', {1: 'A'})
def test_anon_definitions():
def test_domain_c_ast_anon_definitions():
check('struct', '@a', {1: "@a"}, asTextOutput='struct [anonymous]')
check('union', '@a', {1: "@a"}, asTextOutput='union [anonymous]')
check('enum', '@a', {1: "@a"}, asTextOutput='enum [anonymous]')
@@ -454,7 +454,7 @@ def test_anon_definitions():
check('struct', '@a.A', {1: "@a.A"}, asTextOutput='struct [anonymous].A')
def test_initializers():
def test_domain_c_ast_initializers():
idsMember = {1: 'v'}
idsFunction = {1: 'f'}
# no init
@@ -473,7 +473,7 @@ def test_initializers():
# TODO: designator-list
def test_attributes():
def test_domain_c_ast_attributes():
# style: C++
check('member', '[[]] int f', {1: 'f'})
check('member', '[ [ ] ] int f', {1: 'f'},
@@ -566,14 +566,14 @@ def extract_role_links(app, filename):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c(app, status, warning):
def test_domain_c_build(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c_namespace(app, status, warning):
def test_domain_c_build_namespace(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "namespace")
assert len(ws) == 0
@@ -583,7 +583,7 @@ def test_build_domain_c_namespace(app, status, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_domain_c_anon_dup_decl(app, status, warning):
def test_domain_c_build_anon_dup_decl(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "anon-dup-decl")
assert len(ws) == 2
@@ -592,7 +592,7 @@ def test_build_domain_c_anon_dup_decl(app, status, warning):
@pytest.mark.sphinx(confoverrides={'nitpicky': True})
def test_build_domain_c_semicolon(app, warning):
def test_domain_c_build_semicolon(app, warning):
text = """
.. c:member:: int member;
.. c:var:: int var;
@@ -611,7 +611,7 @@ def test_build_domain_c_semicolon(app, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_function_param_target(app, warning):
def test_domain_c_build_function_param_target(app, warning):
# the anchor for function parameters should be the function
app.builder.build_all()
ws = filter_warnings(warning, "function_param_target")
@@ -624,12 +624,19 @@ def test_build_function_param_target(app, warning):
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_build_ns_lookup(app, warning):
def test_domain_c_build_ns_lookup(app, warning):
app.builder.build_all()
ws = filter_warnings(warning, "ns_lookup")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True})
def test_domain_c_build_field_role(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "field-role")
assert len(ws) == 0
def _get_obj(app, queryName):
domain = app.env.get_domain('c')
for name, dispname, objectType, docname, anchor, prio in domain.get_objects():
@@ -638,49 +645,8 @@ def _get_obj(app, queryName):
return (queryName, "not", "found")
def test_cfunction(app):
text = (".. c:function:: PyObject* "
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
entry = _get_obj(app, 'PyType_GenericAlloc')
assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
def test_cmember(app):
text = ".. c:member:: PyObject* PyTypeObject.tp_bases"
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="member",
domain="c", objtype="member", noindex=False)
entry = _get_obj(app, 'PyTypeObject.tp_bases')
assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
def test_cvar(app):
text = ".. c:var:: PyObject* PyClass_Type"
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="var",
domain="c", objtype="var", noindex=False)
entry = _get_obj(app, 'PyClass_Type')
assert entry == ('index', 'c.PyClass_Type', 'member')
def test_noindexentry(app):
text = (".. c:function:: void f()\n"
".. c:function:: void g()\n"
" :noindexentry:\n")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])
@pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True})
def test_intersphinx(tempdir, app, status, warning):
def test_domain_c_build_intersphinx(tempdir, app, status, warning):
# a splitting of test_ids_vs_tags0 into the primary directives in a remote project,
# and then the references in the test project
origSource = """\
@@ -728,3 +694,44 @@ _var c:member 1 index.html#c.$ -
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
def test_domain_c_parse_cfunction(app):
text = (".. c:function:: PyObject* "
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="function",
domain="c", objtype="function", noindex=False)
entry = _get_obj(app, 'PyType_GenericAlloc')
assert entry == ('index', 'c.PyType_GenericAlloc', 'function')
def test_domain_c_parse_cmember(app):
text = ".. c:member:: PyObject* PyTypeObject.tp_bases"
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="member",
domain="c", objtype="member", noindex=False)
entry = _get_obj(app, 'PyTypeObject.tp_bases')
assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member')
def test_domain_c_parse_cvar(app):
text = ".. c:var:: PyObject* PyClass_Type"
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1], addnodes.desc, desctype="var",
domain="c", objtype="var", noindex=False)
entry = _get_obj(app, 'PyClass_Type')
assert entry == ('index', 'c.PyClass_Type', 'member')
def test_domain_c_parse_noindexentry(app):
text = (".. c:function:: void f()\n"
".. c:function:: void g()\n"
" :noindexentry:\n")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])

View File

@@ -117,7 +117,7 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
asTextOutput + ';' if asTextOutput is not None else None)
def test_fundamental_types():
def test_domain_cpp_ast_fundamental_types():
# see https://en.cppreference.com/w/cpp/language/types
for t, id_v2 in cppDomain._id_fundamental_v2.items():
def makeIdV1():
@@ -137,7 +137,7 @@ def test_fundamental_types():
check("function", "void f(%s arg)" % t, {1: makeIdV1(), 2: makeIdV2()})
def test_expressions():
def test_domain_cpp_ast_expressions():
def exprCheck(expr, id, id4=None):
ids = 'IE1CIA%s_1aE'
# call .format() on the expr to unescape double curly braces
@@ -351,7 +351,7 @@ def test_expressions():
exprCheck('a(b(c, 1 + d...)..., e(f..., g))', 'cl1aspcl1b1cspplL1E1dEcl1esp1f1gEE')
def test_type_definitions():
def test_domain_cpp_ast_type_definitions():
check("type", "public bool b", {1: "b", 2: "1b"}, "{key}bool b", key='typedef')
check("type", "{key}bool A::b", {1: "A::b", 2: "N1A1bE"}, key='typedef')
check("type", "{key}bool *b", {1: "b", 2: "1b"}, key='typedef')
@@ -396,7 +396,7 @@ def test_type_definitions():
check('type', '{key}T = Q<A::operator bool>', {2: '1T'}, key='using')
def test_concept_definitions():
def test_domain_cpp_ast_concept_definitions():
check('concept', 'template<typename Param> {key}A::B::Concept',
{2: 'I0EN1A1B7ConceptE'})
check('concept', 'template<typename A, typename B, typename ...C> {key}Foo',
@@ -407,7 +407,7 @@ def test_concept_definitions():
parse('concept', 'template<typename T> template<typename U> {key}Foo')
def test_member_definitions():
def test_domain_cpp_ast_member_definitions():
check('member', ' const std::string & name = 42',
{1: "name__ssCR", 2: "4name"}, output='const std::string &name = 42')
check('member', ' const std::string & name', {1: "name__ssCR", 2: "4name"},
@@ -436,7 +436,7 @@ def test_member_definitions():
check('member', 'int b : 1 || new int{0}', {1: 'b__i', 2: '1b'})
def test_function_definitions():
def test_domain_cpp_ast_function_definitions():
check('function', 'void f(volatile int)', {1: "f__iV", 2: "1fVi"})
check('function', 'void f(std::size_t)', {1: "f__std::s", 2: "1fNSt6size_tE"})
check('function', 'operator bool() const', {1: "castto-b-operatorC", 2: "NKcvbEv"})
@@ -624,7 +624,7 @@ def test_function_definitions():
check('function', 'void f(void (*p)(int, double), int i)', {2: '1fPFvidEi'})
def test_operators():
def test_domain_cpp_ast_operators():
check('function', 'void operator new()', {1: "new-operator", 2: "nwv"})
check('function', 'void operator new[]()', {1: "new-array-operator", 2: "nav"})
check('function', 'void operator delete()', {1: "delete-operator", 2: "dlv"})
@@ -684,14 +684,14 @@ def test_operators():
check('function', 'void operator[]()', {1: "subscript-operator", 2: "ixv"})
def test_nested_name():
def test_domain_cpp_ast_nested_name():
check('class', '{key}::A', {1: "A", 2: "1A"})
check('class', '{key}::A::B', {1: "A::B", 2: "N1A1BE"})
check('function', 'void f(::A a)', {1: "f__A", 2: "1f1A"})
check('function', 'void f(::A::B a)', {1: "f__A::B", 2: "1fN1A1BE"})
def test_class_definitions():
def test_domain_cpp_ast_class_definitions():
check('class', 'public A', {1: "A", 2: "1A"}, output='{key}A')
check('class', 'private {key}A', {1: "A", 2: "1A"})
check('class', '{key}A final', {1: 'A', 2: '1A'})
@@ -722,11 +722,11 @@ def test_class_definitions():
{2: 'I_DpiE1TIJX(Is)EEE', 3: 'I_DpiE1TIJX2IsEEE'})
def test_union_definitions():
def test_domain_cpp_ast_union_definitions():
check('union', '{key}A', {2: "1A"})
def test_enum_definitions():
def test_domain_cpp_ast_enum_definitions():
check('enum', '{key}A', {2: "1A"})
check('enum', '{key}A : std::underlying_type<B>::type', {2: "1A"})
check('enum', '{key}A : unsigned int', {2: "1A"})
@@ -737,7 +737,7 @@ def test_enum_definitions():
check('enumerator', '{key}A = std::numeric_limits<unsigned long>::max()', {2: "1A"})
def test_anon_definitions():
def test_domain_cpp_ast_anon_definitions():
check('class', '@a', {3: "Ut1_a"}, asTextOutput='class [anonymous]')
check('union', '@a', {3: "Ut1_a"}, asTextOutput='union [anonymous]')
check('enum', '@a', {3: "Ut1_a"}, asTextOutput='enum [anonymous]')
@@ -748,7 +748,7 @@ def test_anon_definitions():
asTextOutput='int f(int [anonymous])')
def test_templates():
def test_domain_cpp_ast_templates():
check('class', "A<T>", {2: "IE1AI1TE"}, output="template<> {key}A<T>")
# first just check which objects support templating
check('class', "template<> {key}A", {2: "IE1A"})
@@ -854,7 +854,7 @@ def test_templates():
check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using')
def test_requires_clauses():
def test_domain_cpp_ast_requires_clauses():
check('function', 'template<typename T> requires A auto f() -> void requires B',
{4: 'I0EIQaa1A1BE1fvv'})
check('function', 'template<typename T> requires A || B or C void f()',
@@ -863,7 +863,7 @@ def test_requires_clauses():
{4: 'I0EIQooaa1A1Baa1C1DE1fvv'})
def test_template_args():
def test_domain_cpp_ast_template_args():
# from breathe#218
check('function',
"template<typename F> "
@@ -878,7 +878,7 @@ def test_template_args():
key='using')
def test_initializers():
def test_domain_cpp_ast_initializers():
idsMember = {1: 'v__T', 2: '1v'}
idsFunction = {1: 'f__T', 2: '1f1T'}
idsTemplate = {2: 'I_1TE1fv', 4: 'I_1TE1fvv'}
@@ -912,7 +912,7 @@ def test_initializers():
check('member', 'T v = T{}', idsMember)
def test_attributes():
def test_domain_cpp_ast_attributes():
# style: C++
check('member', '[[]] int f', {1: 'f__i', 2: '1f'})
check('member', '[ [ ] ] int f', {1: 'f__i', 2: '1f'},
@@ -960,7 +960,7 @@ def test_attributes():
check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f', 2: '1fv'})
def test_xref_parsing():
def test_domain_cpp_ast_xref_parsing():
def check(target):
class Config:
cpp_id_attributes = ["id_attr"]
@@ -993,7 +993,7 @@ def filter_warnings(warning, file):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_multi_decl_lookup(app, status, warning):
def test_domain_cpp_build_multi_decl_lookup(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "lookup-key-overload")
assert len(ws) == 0
@@ -1003,7 +1003,7 @@ def test_build_domain_cpp_multi_decl_lookup(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_warn_template_param_qualified_name(app, status, warning):
def test_domain_cpp_build_warn_template_param_qualified_name(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "warn-template-param-qualified-name")
assert len(ws) == 2
@@ -1012,14 +1012,14 @@ def test_build_domain_cpp_warn_template_param_qualified_name(app, status, warnin
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_backslash_ok_true(app, status, warning):
def test_domain_cpp_build_backslash_ok_true(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_semicolon(app, status, warning):
def test_domain_cpp_build_semicolon(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "semicolon")
assert len(ws) == 0
@@ -1027,7 +1027,7 @@ def test_build_domain_cpp_semicolon(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp',
confoverrides={'nitpicky': True, 'strip_signature_backslash': True})
def test_build_domain_cpp_backslash_ok_false(app, status, warning):
def test_domain_cpp_build_backslash_ok_false(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 1
@@ -1035,7 +1035,7 @@ def test_build_domain_cpp_backslash_ok_false(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_anon_dup_decl(app, status, warning):
def test_domain_cpp_build_anon_dup_decl(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "anon-dup-decl")
assert len(ws) == 2
@@ -1044,7 +1044,7 @@ def test_build_domain_cpp_anon_dup_decl(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp')
def test_build_domain_cpp_misuse_of_roles(app, status, warning):
def test_domain_cpp_build_misuse_of_roles(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "roles-targets-ok")
assert len(ws) == 0
@@ -1092,7 +1092,7 @@ def test_build_domain_cpp_misuse_of_roles(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': True})
def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning):
def test_domain_cpp_build_with_add_function_parentheses_is_True(app, status, warning):
app.builder.build_all()
def check(spec, text, file):
@@ -1133,7 +1133,7 @@ def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, war
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'add_function_parentheses': False})
def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning):
def test_domain_cpp_build_with_add_function_parentheses_is_False(app, status, warning):
app.builder.build_all()
def check(spec, text, file):
@@ -1174,7 +1174,7 @@ def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, wa
@pytest.mark.sphinx(testroot='domain-cpp')
def test_xref_consistency(app, status, warning):
def test_domain_cpp_build_xref_consistency(app, status, warning):
app.builder.build_all()
test = 'xref_consistency.html'
@@ -1237,33 +1237,15 @@ not found in `{test}`
assert any_role.classes == texpr_role.content_classes['a'], expect
def test_noindexentry(app):
text = (".. cpp:function:: void f()\n"
".. cpp:function:: void g()\n"
" :noindexentry:\n")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])
def test_mix_decl_duplicate(app, warning):
# Issue 8270
text = (".. cpp:struct:: A\n"
".. cpp:function:: void A()\n"
".. cpp:struct:: A\n")
restructuredtext.parse(app, text)
ws = warning.getvalue().split("\n")
assert len(ws) == 5
assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[0]
assert "Declaration is '.. cpp:function:: void A()'." in ws[1]
assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
assert "Declaration is '.. cpp:struct:: A'." in ws[3]
assert ws[4] == ""
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_domain_cpp_build_field_role(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "field-role")
assert len(ws) == 0
@pytest.mark.sphinx(testroot='domain-cpp-intersphinx', confoverrides={'nitpicky': True})
def test_intersphinx(tempdir, app, status, warning):
def test_domain_cpp_build_intersphinx(tempdir, app, status, warning):
origSource = """\
.. cpp:class:: _class
.. cpp:struct:: _struct
@@ -1323,3 +1305,28 @@ _var cpp:member 1 index.html#_CPPv44$ -
app.builder.build_all()
ws = filter_warnings(warning, "index")
assert len(ws) == 0
def test_domain_cpp_parse_noindexentry(app):
text = (".. cpp:function:: void f()\n"
".. cpp:function:: void g()\n"
" :noindexentry:\n")
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C++ function)', '_CPPv41fv', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])
def test_domain_cpp_parse_mix_decl_duplicate(app, warning):
# Issue 8270
text = (".. cpp:struct:: A\n"
".. cpp:function:: void A()\n"
".. cpp:struct:: A\n")
restructuredtext.parse(app, text)
ws = warning.getvalue().split("\n")
assert len(ws) == 5
assert "index.rst:2: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[0]
assert "Declaration is '.. cpp:function:: void A()'." in ws[1]
assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
assert "Declaration is '.. cpp:struct:: A'." in ws[3]
assert ws[4] == ""

View File

@@ -477,23 +477,11 @@ def test_optional_pyfunction_signature(app):
def test_pyexception_signature(app):
text = ".. py:exception:: exceptions.IOError"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_annotation, "exception "],
[desc_addname, "exceptions."],
[desc_name, "IOError"])],
desc_content)]))
assert_node(doctree[1], desc, desctype="exception",
domain="py", objtype="exception", noindex=False)
def test_exceptions_module_is_ignored(app):
text = (".. py:exception:: IOError\n"
" :module: exceptions\n")
text = ".. py:exception:: builtins.IOError"
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_annotation, "exception "],
[desc_addname, "builtins."],
[desc_name, "IOError"])],
desc_content)]))
assert_node(doctree[1], desc, desctype="exception",
@@ -922,7 +910,8 @@ def test_info_field_list(app):
" :param age: blah blah\n"
" :type age: int\n"
" :param items: blah blah\n"
" :type items: Tuple[str, ...]\n")
" :type items: Tuple[str, ...]\n"
" :param Dict[str, str] params: blah blah\n")
doctree = restructuredtext.parse(app, text)
print(doctree)
@@ -936,6 +925,7 @@ def test_info_field_list(app):
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],
[nodes.list_item, nodes.paragraph],
[nodes.list_item, nodes.paragraph])]))
@@ -983,6 +973,63 @@ def test_info_field_list(app):
refdomain="py", reftype="class", reftarget="str",
**{"py:module": "example", "py:class": "Class"})
# :param Dict[str, str] params:
assert_node(doctree[3][1][0][0][1][0][3][0],
([addnodes.literal_strong, "params"],
" (",
[pending_xref, addnodes.literal_emphasis, "Dict"],
[addnodes.literal_emphasis, "["],
[pending_xref, addnodes.literal_emphasis, "str"],
[addnodes.literal_emphasis, ", "],
[pending_xref, addnodes.literal_emphasis, "str"],
[addnodes.literal_emphasis, "]"],
")",
" -- ",
"blah blah"))
assert_node(doctree[3][1][0][0][1][0][3][0][2], pending_xref,
refdomain="py", reftype="class", reftarget="Dict",
**{"py:module": "example", "py:class": "Class"})
assert_node(doctree[3][1][0][0][1][0][3][0][4], pending_xref,
refdomain="py", reftype="class", reftarget="str",
**{"py:module": "example", "py:class": "Class"})
assert_node(doctree[3][1][0][0][1][0][3][0][6], pending_xref,
refdomain="py", reftype="class", reftarget="str",
**{"py:module": "example", "py:class": "Class"})
def test_info_field_list_piped_type(app):
text = (".. py:module:: example\n"
".. py:class:: Class\n"
"\n"
" :param age: blah blah\n"
" :type age: int | str\n")
doctree = restructuredtext.parse(app, text)
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, (nodes.field_name,
nodes.field_body)])]))
assert_node(doctree[3][1][0][0][1],
([nodes.paragraph, ([addnodes.literal_strong, "age"],
" (",
[pending_xref, addnodes.literal_emphasis, "int"],
[addnodes.literal_emphasis, " | "],
[pending_xref, addnodes.literal_emphasis, "str"],
")",
" -- ",
"blah blah")],))
assert_node(doctree[3][1][0][0][1][0][2], pending_xref,
refdomain="py", reftype="class", reftarget="int",
**{"py:module": "example", "py:class": "Class"})
assert_node(doctree[3][1][0][0][1][0][4], pending_xref,
refdomain="py", reftype="class", reftarget="str",
**{"py:module": "example", "py:class": "Class"})
def test_info_field_list_var(app):
text = (".. py:class:: Class\n"
@@ -1100,6 +1147,9 @@ def test_python_python_use_unqualified_type_names(app, status, warning):
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'<span class="pre">Name</span></a></span>' in content)
assert '<span class="n"><span class="pre">foo.Age</span></span>' in content
assert ('<p><strong>name</strong> (<a class="reference internal" href="#foo.Name" '
'title="foo.Name"><em>Name</em></a>) blah blah</p>' in content)
assert '<p><strong>age</strong> (<em>foo.Age</em>) blah blah</p>' in content
@pytest.mark.sphinx('html', testroot='domain-py-python_use_unqualified_type_names',
@@ -1110,6 +1160,9 @@ def test_python_python_use_unqualified_type_names_disabled(app, status, warning)
assert ('<span class="n"><a class="reference internal" href="#foo.Name" title="foo.Name">'
'<span class="pre">foo.Name</span></a></span>' in content)
assert '<span class="n"><span class="pre">foo.Age</span></span>' in content
assert ('<p><strong>name</strong> (<a class="reference internal" href="#foo.Name" '
'title="foo.Name"><em>foo.Name</em></a>) blah blah</p>' in content)
assert '<p><strong>age</strong> (<em>foo.Age</em>) blah blah</p>' in content
@pytest.mark.sphinx('dummy', testroot='domain-py-xref-warning')

View File

@@ -324,6 +324,23 @@ def test_cmdoption(app):
assert domain.progoptions[('ls', '-l')] == ('index', 'cmdoption-ls-l')
def test_cmdoption_for_None(app):
text = (".. program:: ls\n"
".. program:: None\n"
"\n"
".. option:: -l\n")
domain = app.env.get_domain('std')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
[desc, ([desc_signature, ([desc_name, "-l"],
[desc_addname, ()])],
[desc_content, ()])]))
assert_node(doctree[0], addnodes.index,
entries=[('pair', 'command line option; -l', 'cmdoption-l', '', None)])
assert (None, '-l') in domain.progoptions
assert domain.progoptions[(None, '-l')] == ('index', 'cmdoption-l')
def test_multiple_cmdoptions(app):
text = (".. program:: cmd\n"
"\n"

View File

@@ -735,6 +735,34 @@ def test_autodoc_undoc_members(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_undoc_members_for_metadata_only(app):
# metadata only member is not displayed
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.metadata', options)
assert list(actual) == [
'',
'.. py:module:: target.metadata',
'',
]
# metadata only member is displayed when undoc-member given
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'module', 'target.metadata', options)
assert list(actual) == [
'',
'.. py:module:: target.metadata',
'',
'',
'.. py:function:: foo()',
' :module: target.metadata',
'',
' :meta metadata-only-docstring:',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_inherited_members(app):
options = {"members": None,
@@ -2080,6 +2108,7 @@ def test_singledispatch(app):
'',
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: float, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',
@@ -2107,6 +2136,7 @@ def test_singledispatchmethod(app):
'',
'',
' .. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: float, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',
@@ -2125,6 +2155,7 @@ def test_singledispatchmethod_automethod(app):
assert list(actual) == [
'',
'.. py:method:: Foo.meth(arg, kwarg=None)',
' Foo.meth(arg: float, kwarg=None)',
' Foo.meth(arg: int, kwarg=None)',
' Foo.meth(arg: str, kwarg=None)',
' :module: target.singledispatchmethod',

View File

@@ -100,6 +100,17 @@ def test_autoattribute_instance_variable_in_alias(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoattribute_instance_variable_without_comment(app):
actual = do_autodoc(app, 'attribute', 'target.instance_variable.Bar.attr4')
assert list(actual) == [
'',
'.. py:attribute:: Bar.attr4',
' :module: target.instance_variable',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoattribute_slots_variable_list(app):
actual = do_autodoc(app, 'attribute', 'target.slots.Foo.attr')

View File

@@ -10,6 +10,7 @@
"""
import sys
from typing import List, Union
import pytest
@@ -264,6 +265,94 @@ def test_show_inheritance_for_subclass_of_generic_type(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_process_bases(app):
def autodoc_process_bases(app, name, obj, options, bases):
assert name == 'target.classes.Quux'
assert obj.__module__ == 'target.classes'
assert obj.__name__ == 'Quux'
assert options == {'show-inheritance': True,
'members': []}
assert bases == [List[Union[int, float]]]
bases.pop()
bases.extend([int, str])
app.connect('autodoc-process-bases', autodoc_process_bases)
options = {'show-inheritance': None}
actual = do_autodoc(app, 'class', 'target.classes.Quux', options)
if sys.version_info < (3, 7):
assert list(actual) == [
'',
'.. py:class:: Quux(*args, **kwds)',
' :module: target.classes',
'',
' Bases: :class:`int`, :class:`str`',
'',
' A subclass of List[Union[int, float]]',
'',
]
else:
assert list(actual) == [
'',
'.. py:class:: Quux(iterable=(), /)',
' :module: target.classes',
'',
' Bases: :class:`int`, :class:`str`',
'',
' A subclass of List[Union[int, float]]',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_doc_from_class(app):
options = {"members": None,
"class-doc-from": "class"}
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
assert list(actual) == [
'',
'.. py:class:: C()',
' :module: target.autoclass_content',
'',
' A class having __init__, no __new__',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_doc_from_init(app):
options = {"members": None,
"class-doc-from": "init"}
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
assert list(actual) == [
'',
'.. py:class:: C()',
' :module: target.autoclass_content',
'',
' __init__ docstring',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_class_doc_from_both(app):
options = {"members": None,
"class-doc-from": "both"}
actual = do_autodoc(app, 'class', 'target.autoclass_content.C', options)
assert list(actual) == [
'',
'.. py:class:: C()',
' :module: target.autoclass_content',
'',
' A class having __init__, no __new__',
'',
' __init__ docstring',
'',
]
def test_class_alias(app):
def autodoc_process_docstring(*args):
"""A handler always raises an error.
@@ -280,3 +369,15 @@ def test_class_alias(app):
'',
' alias of :class:`target.classes.Foo`',
]
def test_class_alias_having_doccomment(app):
actual = do_autodoc(app, 'class', 'target.classes.OtherAlias')
assert list(actual) == [
'',
'.. py:attribute:: OtherAlias',
' :module: target.classes',
'',
' docstring',
'',
]

View File

@@ -119,6 +119,7 @@ def test_singledispatch(app):
assert list(actual) == [
'',
'.. py:function:: func(arg, kwarg=None)',
' func(arg: float, kwarg=None)',
' func(arg: int, kwarg=None)',
' func(arg: str, kwarg=None)',
' :module: target.singledispatch',

View File

@@ -29,6 +29,95 @@ def test_empty_all(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_automodule(app):
options = {'members': None}
actual = do_autodoc(app, 'module', 'target.module', options)
assert list(actual) == [
'',
'.. py:module:: target.module',
'',
'',
'.. py:data:: annotated',
' :module: target.module',
' :type: int',
'',
' docstring',
'',
'',
'.. py:data:: documented',
' :module: target.module',
' :value: 1',
'',
' docstring',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_automodule_undoc_members(app):
options = {'members': None,
'undoc-members': None}
actual = do_autodoc(app, 'module', 'target.module', options)
assert list(actual) == [
'',
'.. py:module:: target.module',
'',
'',
'.. py:data:: annotated',
' :module: target.module',
' :type: int',
'',
' docstring',
'',
'',
'.. py:data:: documented',
' :module: target.module',
' :value: 1',
'',
' docstring',
'',
'',
'.. py:data:: undoc_annotated',
' :module: target.module',
' :type: int',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_automodule_special_members(app):
options = {'members': None,
'special-members': None}
actual = do_autodoc(app, 'module', 'target.module', options)
assert list(actual) == [
'',
'.. py:module:: target.module',
'',
'',
'.. py:data:: __documented_special__',
' :module: target.module',
' :value: 1',
'',
' docstring',
'',
'',
'.. py:data:: annotated',
' :module: target.module',
' :type: int',
'',
' docstring',
'',
'',
'.. py:data:: documented',
' :module: target.module',
' :value: 1',
'',
' docstring',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc',
confoverrides={'autodoc_mock_imports': ['missing_module',
'missing_package1',

View File

@@ -140,6 +140,57 @@ def test_autoclass_content_init(app):
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_mixed(app):
app.config.autodoc_class_signature = 'mixed'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Bar', options)
assert list(actual) == [
'',
'.. py:class:: Bar(x, y)',
' :module: target.classes',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_separated_init(app):
app.config.autodoc_class_signature = 'separated'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Bar', options)
assert list(actual) == [
'',
'.. py:class:: Bar',
' :module: target.classes',
'',
'',
' .. py:method:: Bar.__init__(x, y)',
' :module: target.classes',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autodoc_class_signature_separated_new(app):
app.config.autodoc_class_signature = 'separated'
options = {"members": None,
"undoc-members": None}
actual = do_autodoc(app, 'class', 'target.classes.Baz', options)
assert list(actual) == [
'',
'.. py:class:: Baz',
' :module: target.classes',
'',
'',
' .. py:method:: Baz.__new__(cls, x, y)',
' :module: target.classes',
' :staticmethod:',
'',
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_autoclass_content_both(app):
app.config.autoclass_content = 'both'
@@ -695,6 +746,14 @@ def test_autodoc_typehints_description(app):
' Tuple[int, int]\n'
in context)
# Overloads still get displyed in the signature
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
'target.overload.sum(x: str, y: str = None) -> str\n'
'\n'
' docstring\n'
in context)
@pytest.mark.sphinx('text', testroot='ext-autodoc',
confoverrides={'autodoc_typehints': "description",
@@ -787,32 +846,72 @@ def test_autodoc_typehints_description_for_invalid_node(app):
restructuredtext.parse(app, text) # raises no error
@pytest.mark.sphinx('text', testroot='ext-autodoc',
confoverrides={'autodoc_typehints': "both"})
def test_autodoc_typehints_both(app):
(app.srcdir / 'index.rst').write_text(
'.. autofunction:: target.typehints.incr\n'
'\n'
'.. autofunction:: target.typehints.tuple_args\n'
'\n'
'.. autofunction:: target.overload.sum\n'
)
app.build()
context = (app.outdir / 'index.txt').read_text()
assert ('target.typehints.incr(a: int, b: int = 1) -> int\n'
'\n'
' Parameters:\n'
' * **a** (*int*) --\n'
'\n'
' * **b** (*int*) --\n'
'\n'
' Return type:\n'
' int\n'
in context)
assert ('target.typehints.tuple_args(x: Tuple[int, Union[int, str]]) -> Tuple[int, int]\n'
'\n'
' Parameters:\n'
' **x** (*Tuple**[**int**, **Union**[**int**, **str**]**]*) --\n'
'\n'
' Return type:\n'
' Tuple[int, int]\n'
in context)
# Overloads still get displyed in the signature
assert ('target.overload.sum(x: int, y: int = 0) -> int\n'
'target.overload.sum(x: float, y: float = 0.0) -> float\n'
'target.overload.sum(x: str, y: str = None) -> str\n'
'\n'
' docstring\n'
in context)
@pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.')
@pytest.mark.sphinx('text', testroot='ext-autodoc')
def test_autodoc_type_aliases(app):
# default
options = {"members": None}
actual = do_autodoc(app, 'module', 'target.annotations', options)
actual = do_autodoc(app, 'module', 'target.autodoc_type_aliases', options)
assert list(actual) == [
'',
'.. py:module:: target.annotations',
'.. py:module:: target.autodoc_type_aliases',
'',
'',
'.. py:class:: Foo()',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr1',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: int',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr2',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: int',
'',
' docstring',
@@ -820,26 +919,32 @@ def test_autodoc_type_aliases(app):
'',
'.. py:function:: mult(x: int, y: int) -> int',
' mult(x: float, y: float) -> float',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: read(r: _io.BytesIO) -> _io.StringIO',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: sum(x: int, y: int) -> int',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:data:: variable',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: int',
'',
' docstring',
'',
'',
'.. py:data:: variable2',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: int',
' :value: None',
'',
@@ -848,28 +953,29 @@ def test_autodoc_type_aliases(app):
]
# define aliases
app.config.autodoc_type_aliases = {'myint': 'myint'}
actual = do_autodoc(app, 'module', 'target.annotations', options)
app.config.autodoc_type_aliases = {'myint': 'myint',
'io.StringIO': 'my.module.StringIO'}
actual = do_autodoc(app, 'module', 'target.autodoc_type_aliases', options)
assert list(actual) == [
'',
'.. py:module:: target.annotations',
'.. py:module:: target.autodoc_type_aliases',
'',
'',
'.. py:class:: Foo()',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr1',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: myint',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr2',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: myint',
'',
' docstring',
@@ -877,26 +983,32 @@ def test_autodoc_type_aliases(app):
'',
'.. py:function:: mult(x: myint, y: myint) -> myint',
' mult(x: float, y: float) -> float',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: read(r: _io.BytesIO) -> my.module.StringIO',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:function:: sum(x: myint, y: myint) -> myint',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
'',
' docstring',
'',
'',
'.. py:data:: variable',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: myint',
'',
' docstring',
'',
'',
'.. py:data:: variable2',
' :module: target.annotations',
' :module: target.autodoc_type_aliases',
' :type: myint',
' :value: None',
'',
@@ -911,10 +1023,10 @@ def test_autodoc_type_aliases(app):
confoverrides={'autodoc_typehints': "description",
'autodoc_type_aliases': {'myint': 'myint'}})
def test_autodoc_typehints_description_and_type_aliases(app):
(app.srcdir / 'annotations.rst').write_text('.. autofunction:: target.annotations.sum')
(app.srcdir / 'autodoc_type_aliases.rst').write_text('.. autofunction:: target.autodoc_type_aliases.sum')
app.build()
context = (app.outdir / 'annotations.txt').read_text()
assert ('target.annotations.sum(x, y)\n'
context = (app.outdir / 'autodoc_type_aliases.txt').read_text()
assert ('target.autodoc_type_aliases.sum(x, y)\n'
'\n'
' docstring\n'
'\n'

View File

@@ -161,6 +161,7 @@ def test_get_items_summary(make_app, app_params):
'emptyLine': "This is the real summary",
'module_attr': 'This is a module attribute',
'C.class_attr': 'This is a class attribute',
'C.instance_attr': 'This is an instance attribute',
'C.prop_attr1': 'This is a function docstring',
'C.prop_attr2': 'This is a attribute docstring',
'C.C2': 'This is a nested inner class docstring',
@@ -329,6 +330,7 @@ def test_autosummary_generate(app, status, warning):
' ~Foo.CONSTANT3\n'
' ~Foo.CONSTANT4\n'
' ~Foo.baz\n'
' ~Foo.value\n'
' \n' in Foo)
FooBar = (app.srcdir / 'generated' / 'autosummary_dummy_module.Foo.Bar.rst').read_text()

View File

@@ -196,14 +196,6 @@ def test_missing_reference_pydomain(tempdir, app, status, warning):
rn = missing_reference(app, app.env, node, contnode)
assert rn.astext() == 'Foo.bar'
# pending_xref_condition="resolved"
node = addnodes.pending_xref('', reftarget='Foo.bar', refdomain='py', reftype='attr')
node['py:module'] = 'module1'
node += addnodes.pending_xref_condition('', 'Foo.bar', condition='resolved')
node += addnodes.pending_xref_condition('', 'module1.Foo.bar', condition='*')
rn = missing_reference(app, app.env, node, nodes.Text('dummy-cont-node'))
assert rn.astext() == 'Foo.bar'
def test_missing_reference_stddomain(tempdir, app, status, warning):
inv_file = tempdir / 'inventory'

View File

@@ -410,7 +410,7 @@ def test_text_admonitions(app):
app.build()
# --- admonitions
# #1206: gettext did not translate admonition directive's title
# seealso: http://docutils.sourceforge.net/docs/ref/rst/directives.html#admonitions
# seealso: https://docutils.sourceforge.io/docs/ref/rst/directives.html#admonitions
result = (app.outdir / 'admonitions.txt').read_text()
directives = (
"attention", "caution", "danger", "error", "hint",

View File

@@ -9,7 +9,7 @@
"""
# adapted from an example of bibliographic metadata at
# http://docutils.sourceforge.net/docs/user/rst/demo.txt
# https://docutils.sourceforge.io/docs/user/rst/demo.txt
import pytest

View File

@@ -8,31 +8,48 @@
:license: BSD, see LICENSE for details.
"""
from sphinx.util.docstrings import extract_metadata, prepare_commentdoc, prepare_docstring
from sphinx.util.docstrings import prepare_commentdoc, prepare_docstring, separate_metadata
def test_extract_metadata():
metadata = extract_metadata(":meta foo: bar\n"
":meta baz:\n")
def test_separate_metadata():
# metadata only
text = (":meta foo: bar\n"
":meta baz:\n")
docstring, metadata = separate_metadata(text)
assert docstring == ''
assert metadata == {'foo': 'bar', 'baz': ''}
# non metadata field list item
text = (":meta foo: bar\n"
":param baz:\n")
docstring, metadata = separate_metadata(text)
assert docstring == ':param baz:\n'
assert metadata == {'foo': 'bar'}
# field_list like text following just after paragaph is not a field_list
metadata = extract_metadata("blah blah blah\n"
":meta foo: bar\n"
":meta baz:\n")
text = ("blah blah blah\n"
":meta foo: bar\n"
":meta baz:\n")
docstring, metadata = separate_metadata(text)
assert docstring == text
assert metadata == {}
# field_list like text following after blank line is a field_list
metadata = extract_metadata("blah blah blah\n"
"\n"
":meta foo: bar\n"
":meta baz:\n")
text = ("blah blah blah\n"
"\n"
":meta foo: bar\n"
":meta baz:\n")
docstring, metadata = separate_metadata(text)
assert docstring == "blah blah blah\n\n"
assert metadata == {'foo': 'bar', 'baz': ''}
# non field_list item breaks field_list
metadata = extract_metadata(":meta foo: bar\n"
"blah blah blah\n"
":meta baz:\n")
text = (":meta foo: bar\n"
"blah blah blah\n"
":meta baz:\n")
docstring, metadata = separate_metadata(text)
assert docstring == ("blah blah blah\n"
":meta baz:\n")
assert metadata == {'foo': 'bar'}

View File

@@ -10,6 +10,7 @@
import ast
import datetime
import enum
import functools
import sys
import types
@@ -19,7 +20,26 @@ import _testcapi
import pytest
from sphinx.util import inspect
from sphinx.util.inspect import stringify_signature
from sphinx.util.inspect import TypeAliasNamespace, stringify_signature
def test_TypeAliasNamespace():
import logging.config
type_alias = TypeAliasNamespace({'logging.Filter': 'MyFilter',
'logging.Handler': 'MyHandler',
'logging.handlers.SyslogHandler': 'MySyslogHandler'})
assert type_alias['logging'].Filter == 'MyFilter'
assert type_alias['logging'].Handler == 'MyHandler'
assert type_alias['logging'].handlers.SyslogHandler == 'MySyslogHandler'
assert type_alias['logging'].Logger == logging.Logger
assert type_alias['logging'].config == logging.config
with pytest.raises(KeyError):
assert type_alias['log']
with pytest.raises(KeyError):
assert type_alias['unknown']
def test_signature():
@@ -183,10 +203,7 @@ def test_signature_annotations():
# Instance annotations
sig = inspect.signature(f11)
if sys.version_info < (3, 10):
assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
else:
assert stringify_signature(sig) == '(x: CustomAnnotation(), y: 123) -> None'
assert stringify_signature(sig) == '(x: CustomAnnotation, y: 123) -> None'
# tuple with more than two items
sig = inspect.signature(f12)
@@ -500,6 +517,14 @@ def test_dict_customtype():
assert "<CustomType(2)>: 2" in description
def test_object_description_enum():
class MyEnum(enum.Enum):
FOO = 1
BAR = 2
assert inspect.object_description(MyEnum.FOO) == "MyEnum.FOO"
def test_getslots():
class Foo:
pass
@@ -658,7 +683,10 @@ def test_unpartial():
def test_getdoc_inherited_decorated_method():
class Foo:
def meth(self):
"""docstring."""
"""
docstring
indented text
"""
class Bar(Foo):
@functools.lru_cache()
@@ -667,7 +695,7 @@ def test_getdoc_inherited_decorated_method():
pass
assert inspect.getdoc(Bar.meth, getattr, False, Bar, "meth") is None
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == "docstring."
assert inspect.getdoc(Bar.meth, getattr, True, Bar, "meth") == Foo.meth.__doc__
def test_is_builtin_class_method():

View File

@@ -133,6 +133,12 @@ def test_restify_type_ForwardRef():
assert restify(ForwardRef("myint")) == ":class:`myint`"
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
def test_restify_type_Literal():
from typing import Literal # type: ignore
assert restify(Literal[1, "2", "\r"]) == ":obj:`~typing.Literal`\\ [1, '2', '\\r']"
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
def test_restify_type_union_operator():
assert restify(int | None) == "Optional[:class:`int`]" # type: ignore
@@ -237,6 +243,12 @@ def test_stringify_type_hints_alias():
assert stringify(MyTuple) == "Tuple[str, str]" # type: ignore
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
def test_stringify_type_Literal():
from typing import Literal # type: ignore
assert stringify(Literal[1, "2", "\r"]) == "Literal[1, '2', '\\r']"
@pytest.mark.skipif(sys.version_info < (3, 10), reason='python 3.10+ is required.')
def test_stringify_type_union_operator():
assert stringify(int | None) == "Optional[int]" # type: ignore

View File

@@ -15,7 +15,7 @@ from sphinx.writers.latex import rstdim_to_latexdim
def test_rstdim_to_latexdim():
# Length units docutils supported
# http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units
# https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#length-units
assert rstdim_to_latexdim('160em') == '160em'
assert rstdim_to_latexdim('160px') == '160\\sphinxpxdimen'
assert rstdim_to_latexdim('160in') == '160in'