Skip to content

Commit

Permalink
Properly support of in docstring type specifiers (#10738)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
  • Loading branch information
ProGamerGov and AA-Turner committed Aug 2, 2022
1 parent f72a25c commit b2fe07e
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 1 deletion.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -30,6 +30,7 @@ Contributors
* Antonio Valentino -- qthelp builder, docstring inheritance
* Antti Kaihola -- doctest extension (skipif option)
* Barry Warsaw -- setup command improvements
* Ben Egan -- Napoleon improvements
* Benjamin Peterson -- unittests
* Blaise Laflamme -- pyramid theme
* Bruce Mitchener -- Minor epub improvement
Expand Down
3 changes: 3 additions & 0 deletions CHANGES
Expand Up @@ -41,6 +41,9 @@ Deprecated
Features added
--------------

* #10738: napoleon: Add support for docstring types using 'of', like
``type of type``. Example: ``tuple of int``.

Bugs fixed
----------

Expand Down
2 changes: 1 addition & 1 deletion sphinx/domains/python.py
Expand Up @@ -380,7 +380,7 @@ def make_xrefs(self, rolename: str, domain: str, target: str,
innernode: Type[TextlikeNode] = nodes.emphasis,
contnode: Node = None, env: BuildEnvironment = None,
inliner: Inliner = None, location: Node = None) -> List[Node]:
delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+|\s*\|\s*|\.\.\.)'
delims = r'(\s*[\[\]\(\),](?:\s*o[rf]\s)?\s*|\s+o[rf]\s+|\s*\|\s*|\.\.\.)'
delims_re = re.compile(delims)
sub_targets = re.split(delims, target)

Expand Down
72 changes: 72 additions & 0 deletions tests/test_domain_py.py
Expand Up @@ -1214,6 +1214,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"
Expand Down
45 changes: 45 additions & 0 deletions tests/test_ext_napoleon_docstring.py
Expand Up @@ -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):
Expand Down Expand Up @@ -2367,6 +2402,8 @@ def test_tokenize_type_spec(self):
"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'",
Expand All @@ -2383,6 +2420,8 @@ def test_tokenize_type_spec(self):
["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'"],
Expand Down Expand Up @@ -2443,6 +2482,7 @@ def test_convert_numpy_type_spec(self):
"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'",
Expand All @@ -2455,6 +2495,7 @@ def test_convert_numpy_type_spec(self):
"*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'``",
Expand Down Expand Up @@ -2486,6 +2527,8 @@ def test_parameter_types(self):
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
Expand All @@ -2504,6 +2547,8 @@ def test_parameter_types(self):
: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>`",
Expand Down

0 comments on commit b2fe07e

Please sign in to comment.