diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_build_latex.py | 2 | ||||
-rw-r--r-- | tests/test_domain_cpp.py | 91 | ||||
-rw-r--r-- | tests/test_domain_py.py | 72 | ||||
-rw-r--r-- | tests/test_ext_napoleon.py | 18 | ||||
-rw-r--r-- | tests/test_ext_napoleon_docstring.py | 45 |
5 files changed, 218 insertions, 10 deletions
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 3728b3da7..8f7a2e85a 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1519,7 +1519,7 @@ def test_latex_labels(app, status, warning): def test_latex_figure_in_admonition(app, status, warning): app.builder.build_all() result = (app.outdir / 'python.tex').read_text(encoding='utf8') - assert(r'\begin{figure}[H]' in result) + assert r'\begin{figure}[H]' in result def test_default_latex_documents(): diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index ee8da3d39..5e9bc6aa3 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -841,9 +841,9 @@ def test_domain_cpp_ast_templates(): check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar', {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{{id_0, id_1, id_2}} {key}xyz::bar<id_0, id_1, id_2>', - {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barI4id_04id_14id_2EE'}) + {2: 'I000EXN3abc2ns3fooEI4id_04id_14id_2EEN3xyz3barE'}) check('class', 'abc::ns::foo{{id_0, id_1, ...id_2}} {key}xyz::bar<id_0, id_1, id_2...>', - {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barI4id_04id_1Dp4id_2EE'}) + {2: 'I00DpEXN3abc2ns3fooEI4id_04id_1sp4id_2EEN3xyz3barE'}) check('class', 'template<> Concept{{U}} {key}A<int>::B', {2: 'IEI0EX7ConceptI1UEEN1AIiE1BE'}) @@ -876,6 +876,9 @@ def test_domain_cpp_ast_templates(): # defaulted constrained type parameters check('type', 'template<C T = int&> {key}A', {2: 'I_1CE1A'}, key='using') + # pack expansion after non-type template parameter + check('type', 'template<int (X::*)(bool)...> {key}A', {2: 'I_DpM1XFibEE1A'}, key='using') + def test_domain_cpp_ast_placeholder_types(): check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'}) @@ -901,7 +904,7 @@ def test_domain_cpp_ast_requires_clauses(): 'template<typename T> requires R<T> ' + 'template<typename U> requires S<T> ' + 'void A<T>::f() requires B', - {4: 'I0EIQ1RI1TEEI0EIQaa1SI1TE1BEN1AI1TE1fEvv'}) + {4: 'I0EIQ1RI1TEEI0EIQaa1SI1TE1BEN1A1fEvv'}) check('function', 'template<template<typename T> requires R<T> typename X> ' + 'void f()', @@ -1040,6 +1043,38 @@ def test_domain_cpp_ast_xref_parsing(): check('T f()') +@pytest.mark.parametrize( + 'param,is_pack', + [('typename', False), + ('typename T', False), + ('typename...', True), + ('typename... T', True), + ('int', False), + ('int N', False), + ('int* N', False), + ('int& N', False), + ('int&... N', True), + ('int*... N', True), + ('int...', True), + ('int... N', True), + ('auto', False), + ('auto...', True), + ('int X::*', False), + ('int X::*...', True), + ('int (X::*)(bool)', False), + ('int (X::*x)(bool)', False), + ('int (X::*)(bool)...', True), + ('template<typename> class', False), + ('template<typename> class...', True), + ]) +def test_domain_cpp_template_parameters_is_pack(param: str, is_pack: bool): + def parse_template_parameter(param: str): + ast = parse('type', 'template<' + param + '> X') + return ast.templatePrefix.templates[0].params[0] + ast = parse_template_parameter(param) + assert ast.isPack == is_pack + + # def test_print(): # # used for getting all the ids out for checking # for a in ids: @@ -1395,3 +1430,53 @@ def test_domain_cpp_parse_mix_decl_duplicate(app, warning): 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] == "" + + +# For some reason, using the default testroot of "root" leads to the contents of +# `test-root/objects.txt` polluting the symbol table depending on the test +# execution order. Using a testroot of "config" seems to avoid that problem. +@pytest.mark.sphinx(testroot='config') +def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params): + args, kwargs = app_params + + text1 = (".. cpp:class:: template <typename T> A\n") + text2 = (".. cpp:class:: template <typename T> template <typename U> A<T>::B\n") + + app1 = make_app(*args, **kwargs) + restructuredtext.parse(app=app1, text=text1, docname='text1') + root1 = app1.env.domaindata['cpp']['root_symbol'] + + assert root1.dump(1) == ( + ' ::\n' + ' template<typename T> \n' + ' A: {class} template<typename T> A\t(text1)\n' + ' T: {templateParam} typename T\t(text1)\n' + ) + + app2 = make_app(*args, **kwargs) + restructuredtext.parse(app=app2, text=text2, docname='text2') + root2 = app2.env.domaindata['cpp']['root_symbol'] + + assert root2.dump(1) == ( + ' ::\n' + ' template<typename T> \n' + ' A\n' + ' T\n' + ' template<typename U> \n' + ' B: {class} template<typename T> template<typename U> A<T>::B\t(text2)\n' + ' U: {templateParam} typename U\t(text2)\n' + ) + + root2.merge_with(root1, ['text1'], app2.env) + + assert root2.dump(1) == ( + ' ::\n' + ' template<typename T> \n' + ' A: {class} template<typename T> A\t(text1)\n' + ' T: {templateParam} typename T\t(text1)\n' + ' template<typename U> \n' + ' B: {class} template<typename T> template<typename U> A<T>::B\t(text2)\n' + ' U: {templateParam} typename U\t(text2)\n' + ) + warning = app2._warning.getvalue() + assert 'Internal C++ domain error during symbol merging' not in warning diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index a9a729c1f..3c71d8337 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1201,6 +1201,78 @@ def test_info_field_list_var(app): refdomain="py", reftype="class", reftarget="int", **{"py:class": "Class"}) +def test_info_field_list_napoleon_deliminator_of(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param list_str_var: example description.\n" + " :type list_str_var: list of str\n" + " :param tuple_int_var: example description.\n" + " :type tuple_int_var: tuple of tuple of int\n" + ) + doctree = restructuredtext.parse(app, text) + + # :param list of str list_str_var: + assert_node(doctree[3][1][0][0][1][0][0][0], + ([addnodes.literal_strong, "list_str_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "list"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "example description.")) + + # :param tuple of tuple of int tuple_int_var: + assert_node(doctree[3][1][0][0][1][0][1][0], + ([addnodes.literal_strong, "tuple_int_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "tuple"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "tuple"], + [addnodes.literal_emphasis, " of "], + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "example description.")) + + +def test_info_field_list_napoleon_deliminator_or(app): + text = (".. py:module:: example\n" + ".. py:class:: Class\n" + "\n" + " :param bool_str_var: example description.\n" + " :type bool_str_var: bool or str\n" + " :param str_float_int_var: example description.\n" + " :type str_float_int_var: str or float or int\n" + ) + doctree = restructuredtext.parse(app, text) + + # :param bool or str bool_str_var: + assert_node(doctree[3][1][0][0][1][0][0][0], + ([addnodes.literal_strong, "bool_str_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "bool"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "str"], + ")", + " -- ", + "example description.")) + + # :param str or float or int str_float_int_var: + assert_node(doctree[3][1][0][0][1][0][1][0], + ([addnodes.literal_strong, "str_float_int_var"], + " (", + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "float"], + [addnodes.literal_emphasis, " or "], + [pending_xref, addnodes.literal_emphasis, "int"], + ")", + " -- ", + "example description.")) + + def test_type_field(app): text = (".. py:data:: var1\n" " :type: .int\n" diff --git a/tests/test_ext_napoleon.py b/tests/test_ext_napoleon.py index f4ad04a00..e73c75ffd 100644 --- a/tests/test_ext_napoleon.py +++ b/tests/test_ext_napoleon.py @@ -94,8 +94,10 @@ class SetupTest(TestCase): for name in Config._config_values: has_config = False for method_name, args, _kwargs in app.method_calls: - if(method_name == 'add_config_value' and - args[0] == name): + if ( + method_name == 'add_config_value' and + args[0] == name + ): has_config = True if not has_config: self.fail('Config value was not added to app %s' % name) @@ -104,11 +106,15 @@ class SetupTest(TestCase): has_skip_member = False for method_name, args, _kwargs in app.method_calls: if method_name == 'connect': - if(args[0] == 'autodoc-process-docstring' and - args[1] == _process_docstring): + if ( + args[0] == 'autodoc-process-docstring' and + args[1] == _process_docstring + ): has_process_docstring = True - elif(args[0] == 'autodoc-skip-member' and - args[1] == _skip_member): + elif ( + args[0] == 'autodoc-skip-member' and + args[1] == _skip_member + ): has_skip_member = True if not has_process_docstring: self.fail('autodoc-process-docstring never connected') diff --git a/tests/test_ext_napoleon_docstring.py b/tests/test_ext_napoleon_docstring.py index 6db897932..c2704ebce 100644 --- a/tests/test_ext_napoleon_docstring.py +++ b/tests/test_ext_napoleon_docstring.py @@ -356,6 +356,41 @@ class GoogleDocstringTest(BaseDocstringTest): :Yields: Extended description of yielded value """ + ), ( + """ + Single line summary + + Args: + + arg1 (list of str): Extended + description of arg1. + arg2 (tuple of int): Extended + description of arg2. + arg3 (tuple of list of float): Extended + description of arg3. + arg4 (int, float, or list of bool): Extended + description of arg4. + arg5 (list of int, float, or bool): Extended + description of arg5. + arg6 (list of int or float): Extended + description of arg6. + """, + """ + Single line summary + + :Parameters: * **arg1** (*list of str*) -- Extended + description of arg1. + * **arg2** (*tuple of int*) -- Extended + description of arg2. + * **arg3** (*tuple of list of float*) -- Extended + description of arg3. + * **arg4** (*int, float, or list of bool*) -- Extended + description of arg4. + * **arg5** (*list of int, float, or bool*) -- Extended + description of arg5. + * **arg6** (*list of int or float*) -- Extended + description of arg6. + """ )] def test_sphinx_admonitions(self): @@ -2367,6 +2402,8 @@ definition_after_normal_text : int "defaultdict", "int, float, or complex", "int or float or None, optional", + "list of list of int or float, optional", + "tuple of list of str, float, or int", '{"F", "C", "N"}', "{'F', 'C', 'N'}, default: 'F'", "{'F', 'C', 'N or C'}, default 'F'", @@ -2383,6 +2420,8 @@ definition_after_normal_text : int ["defaultdict"], ["int", ", ", "float", ", or ", "complex"], ["int", " or ", "float", " or ", "None", ", ", "optional"], + ["list", " of ", "list", " of ", "int", " or ", "float", ", ", "optional"], + ["tuple", " of ", "list", " of ", "str", ", ", "float", ", or ", "int"], ["{", '"F"', ", ", '"C"', ", ", '"N"', "}"], ["{", "'F'", ", ", "'C'", ", ", "'N'", "}", ", ", "default", ": ", "'F'"], ["{", "'F'", ", ", "'C'", ", ", "'N or C'", "}", ", ", "default", " ", "'F'"], @@ -2443,6 +2482,7 @@ definition_after_normal_text : int "optional", "str, optional", "int or float or None, default: None", + "list of tuple of str, optional", "int, default None", '{"F", "C", "N"}', "{'F', 'C', 'N'}, default: 'N'", @@ -2455,6 +2495,7 @@ definition_after_normal_text : int "*optional*", ":class:`str`, *optional*", ":class:`int` or :class:`float` or :obj:`None`, *default*: :obj:`None`", + ":class:`list` of :class:`tuple` of :class:`str`, *optional*", ":class:`int`, *default* :obj:`None`", '``{"F", "C", "N"}``', "``{'F', 'C', 'N'}``, *default*: ``'N'``", @@ -2486,6 +2527,8 @@ definition_after_normal_text : int a optional mapping param8 : ... or Ellipsis ellipsis + param9 : tuple of list of int + a parameter with tuple of list of int """) expected = dedent("""\ :param param1: the data to work on @@ -2504,6 +2547,8 @@ definition_after_normal_text : int :type param7: :term:`mapping` of :term:`hashable` to :class:`str`, *optional* :param param8: ellipsis :type param8: :obj:`... <Ellipsis>` or :obj:`Ellipsis` + :param param9: a parameter with tuple of list of int + :type param9: :class:`tuple` of :class:`list` of :class:`int` """) translations = { "dict-like": ":term:`dict-like <mapping>`", |