Python domain can now link container types automatically

Google types now greedily match the closing parenthesis.

Also removed name from returns section in Google docstrings.
Instead, everything before the colon is treated as the type.
This commit is contained in:
Ashley Whetter 2016-08-02 21:29:07 +01:00 committed by Ashley Whetter
parent 86c9767ced
commit cfd4e51802
7 changed files with 78 additions and 25 deletions

View File

@ -359,6 +359,18 @@ single word, like this::
:param int priority: The priority of the message, can be a number 1-5
.. versionadded:: 1.5
Container types such as lists and dictionaries can be linked automatically
using the following syntax::
:type priorities: list(int)
:type priorities: list[int]
:type mapping: dict(str, int)
:type mapping: dict[str, int]
:type point: tuple(float, float)
:type point: tuple[float, float]
.. _python-roles:
Cross-referencing Python objects

View File

@ -211,14 +211,14 @@ class ExampleClass(object):
param1 (str): Description of `param1`.
param2 (:obj:`int`, optional): Description of `param2`. Multiple
lines are supported.
param3 (:obj:`list` of :obj:`str`): Description of `param3`.
param3 (list(str)): Description of `param3`.
"""
self.attr1 = param1
self.attr2 = param2
self.attr3 = param3 #: Doc comment *inline* with attribute
#: list of str: Doc comment *before* attribute, with type specified
#: list(str): Doc comment *before* attribute, with type specified
self.attr4 = ['attr4']
self.attr5 = None
@ -231,7 +231,7 @@ class ExampleClass(object):
@property
def readwrite_property(self):
""":obj:`list` of :obj:`str`: Properties with both a getter and setter
"""list(str): Properties with both a getter and setter
should only be documented in their getter method.
If the setter method contains notable behavior, it should be

View File

@ -260,7 +260,7 @@ class ExampleClass(object):
----------
param1 : str
Description of `param1`.
param2 : :obj:`list` of :obj:`str`
param2 : list(str)
Description of `param2`. Multiple
lines are supported.
param3 : :obj:`int`, optional
@ -271,7 +271,7 @@ class ExampleClass(object):
self.attr2 = param2
self.attr3 = param3 #: Doc comment *inline* with attribute
#: list of str: Doc comment *before* attribute, with type specified
#: list(str): Doc comment *before* attribute, with type specified
self.attr4 = ["attr4"]
self.attr5 = None
@ -284,7 +284,7 @@ class ExampleClass(object):
@property
def readwrite_property(self):
""":obj:`list` of :obj:`str`: Properties with both a getter and setter
"""list(str): Properties with both a getter and setter
should only be documented in their getter method.
If the setter method contains notable behavior, it should be

View File

@ -101,6 +101,27 @@ class PyXrefMixin(object):
break
return result
def make_xrefs(self, rolename, domain, target, innernode=nodes.emphasis,
contnode=None):
delims = '(\s*[\[\]\(\),]\s*)'
delims_re = re.compile(delims)
sub_targets = re.split(delims, target)
split_contnode = bool(contnode and contnode.astext() == target)
results = []
for sub_target in sub_targets:
if split_contnode:
contnode = nodes.Text(sub_target)
if delims_re.match(sub_target):
results.append(contnode or innernode(sub_target, sub_target))
else:
results.append(self.make_xref(rolename, domain, sub_target,
innernode, contnode))
return results
class PyField(PyXrefMixin, Field):
pass

View File

@ -24,7 +24,7 @@ from sphinx.util.pycompat import UnicodeMixin
_directive_regex = re.compile(r'\.\. \S+::')
_google_section_regex = re.compile(r'^(\s|\w)+:\s*$')
_google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.+?)\s*\)')
_google_typed_arg_regex = re.compile(r'\s*(.+?)\s*\(\s*(.*[^\s]+)\s*\)')
_numpy_section_regex = re.compile(r'^[=\-`:\'"~^_*+#<>]{2,}\s*$')
_single_colon_regex = re.compile(r'(?<!:):(?!:)')
_xref_regex = re.compile(r'(:\w+:\S+:`.+?`|:\S+:`.+?`|`.+?`)')
@ -172,7 +172,7 @@ class GoogleDocstring(UnicodeMixin):
Returns
-------
:obj:`list` of :obj:`str`
list(str)
The lines of the docstring in a list.
"""
@ -254,12 +254,7 @@ class GoogleDocstring(UnicodeMixin):
else:
_desc = lines[1:]
match = _google_typed_arg_regex.match(before)
if match:
_name = match.group(1)
_type = match.group(2)
else:
_type = before
_type = before
_desc = self.__class__(_desc, self._config).lines()
return [(_name, _type, _desc,)]
@ -817,7 +812,7 @@ class NumpyDocstring(GoogleDocstring):
Returns
-------
:obj:`list` of :obj:`str`
list(str)
The lines of the docstring in a list.
"""

View File

@ -62,6 +62,10 @@ class Field(object):
refnode += contnode or innernode(target, target)
return refnode
def make_xrefs(self, rolename, domain, target,
innernode=addnodes.literal_emphasis, contnode=None):
return [self.make_xref(rolename, domain, target, innernode, contnode)]
def make_entry(self, fieldarg, content):
return (fieldarg, content)
@ -70,14 +74,15 @@ class Field(object):
fieldname = nodes.field_name('', self.label)
if fieldarg:
fieldname += nodes.Text(' ')
fieldname += self.make_xref(self.rolename, domain,
fieldarg, nodes.Text)
fieldname.extend(self.make_xrefs(self.rolename, domain,
fieldarg, nodes.Text))
if len(content) == 1 and (
isinstance(content[0], nodes.Text) or
(isinstance(content[0], nodes.inline) and len(content[0]) == 1 and
isinstance(content[0][0], nodes.Text))):
content = [self.make_xref(self.bodyrolename, domain,
content[0].astext(), contnode=content[0])]
content = self.make_xrefs(self.bodyrolename, domain,
content[0].astext(), contnode=content[0])
fieldbody = nodes.field_body('', nodes.paragraph('', '', *content))
return nodes.field('', fieldname, fieldbody)
@ -108,14 +113,16 @@ class GroupedField(Field):
listnode = self.list_type()
for fieldarg, content in items:
par = nodes.paragraph()
par += self.make_xref(self.rolename, domain, fieldarg,
addnodes.literal_strong)
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
addnodes.literal_strong))
par += nodes.Text(' -- ')
par += content
listnode += nodes.list_item('', par)
if len(items) == 1 and self.can_collapse:
fieldbody = nodes.field_body('', listnode[0][0])
return nodes.field('', fieldname, fieldbody)
fieldbody = nodes.field_body('', listnode)
return nodes.field('', fieldname, fieldbody)
@ -150,8 +157,8 @@ class TypedField(GroupedField):
def make_field(self, types, domain, items):
def handle_item(fieldarg, content):
par = nodes.paragraph()
par += self.make_xref(self.rolename, domain, fieldarg,
addnodes.literal_strong)
par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
addnodes.literal_strong))
if fieldarg in types:
par += nodes.Text(' (')
# NOTE: using .pop() here to prevent a single type node to be
@ -160,8 +167,8 @@ class TypedField(GroupedField):
fieldtype = types.pop(fieldarg)
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
typename = u''.join(n.astext() for n in fieldtype)
par += self.make_xref(self.typerolename, domain, typename,
addnodes.literal_emphasis)
par.extend(self.make_xrefs(self.typerolename, domain, typename,
addnodes.literal_emphasis))
else:
par += fieldtype
par += nodes.Text(')')

View File

@ -222,6 +222,24 @@ class GoogleDocstringTest(BaseDocstringTest):
"""
Single line summary
Args:
arg1 (list(int)): Description
arg2 (list[int]): Description
arg3 (dict(str, int)): Description
arg4 (dict[str, int]): Description
""",
"""
Single line summary
:Parameters: * **arg1** (*list(int)*) -- Description
* **arg2** (*list[int]*) -- Description
* **arg3** (*dict(str, int)*) -- Description
* **arg4** (*dict[str, int]*) -- Description
"""
), (
"""
Single line summary
Yield:
str:Extended
description of yielded value