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 :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: .. _python-roles:
Cross-referencing Python objects Cross-referencing Python objects

View File

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

View File

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

View File

@ -101,6 +101,27 @@ class PyXrefMixin(object):
break break
return result 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): class PyField(PyXrefMixin, Field):
pass pass

View File

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

View File

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

View File

@ -222,6 +222,24 @@ class GoogleDocstringTest(BaseDocstringTest):
""" """
Single line summary 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: Yield:
str:Extended str:Extended
description of yielded value description of yielded value