Merge pull request #2825 from AWhetter/link_container_types

Looks great, thanks again for the work!

(BTW, I just noticed "Squash and Merge" is enabled for this repo. Sorry to make you do the extra work, I really do appreciate it)
This commit is contained in:
Rob Ruana 2016-08-03 13:11:25 -04:00 committed by GitHub
commit a8375dd5b4
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