Skip to content

Commit

Permalink
Merge pull request #106 from jnothman/member-definition-list
Browse files Browse the repository at this point in the history
Format attributes like parameters
  • Loading branch information
stefanv authored Sep 9, 2017
2 parents 057ef57 + c917226 commit b215bed
Show file tree
Hide file tree
Showing 2 changed files with 206 additions and 23 deletions.
130 changes: 123 additions & 7 deletions numpydoc/docscrape_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,136 @@ def _str_returns(self, name='Returns'):
out += ['']
return out

def _str_param_list(self, name):
def _process_param(self, param, desc, autosum):
"""Determine how to display a parameter
Emulates autosummary behavior if autosum is not None.
Parameters
----------
param : str
The name of the parameter
desc : list of str
The parameter description as given in the docstring. This is
ignored when autosummary logic applies.
autosum : list or None
If a list, autosummary-style behaviour will apply for params
that are attributes of the class and have a docstring.
Names for autosummary generation will be appended to this list.
If None, autosummary is disabled.
Returns
-------
display_param : str
The marked up parameter name for display. This may include a link
to the corresponding attribute's own documentation.
desc : list of str
A list of description lines. This may be identical to the input
``desc``, if ``autosum is None`` or ``param`` is not a class
attribute, or it will be a summary of the class attribute's
docstring.
Notes
-----
This does not have the autosummary functionality to display a method's
signature, and hence is not used to format methods. It may be
complicated to incorporate autosummary's signature mangling, as it
relies on Sphinx's plugin mechanism.
"""
param = param.strip()
display_param = '**%s**' % param

if autosum is None:
return display_param, desc

param_obj = getattr(self._obj, param, None)
if not (callable(param_obj)
or isinstance(param_obj, property)
or inspect.isgetsetdescriptor(param_obj)):
param_obj = None
obj_doc = pydoc.getdoc(param_obj)

if not (param_obj and obj_doc):
return display_param, desc

prefix = getattr(self, '_name', '')
if prefix:
autosum_prefix = '~%s.' % prefix
link_prefix = '%s.' % prefix
else:
autosum_prefix = ''
link_prefix = ''

# Referenced object has a docstring
autosum.append(" %s%s" % (autosum_prefix, param))
display_param = ':obj:`%s <%s%s>`' % (param,
link_prefix,
param)
if obj_doc:
# Overwrite desc. Take summary logic of autosummary
desc = re.split('\n\s*\n', obj_doc.strip(), 1)[0]
# XXX: Should this have DOTALL?
# It does not in autosummary
m = re.search(r"^([A-Z].*?\.)(?:\s|$)",
' '.join(desc.split()))
if m:
desc = m.group(1).strip()
else:
desc = desc.partition('\n')[0]
desc = desc.split('\n')
return display_param, desc

def _str_param_list(self, name, fake_autosummary=False):
"""Generate RST for a listing of parameters or similar
Parameter names are displayed as bold text, and descriptions
are in blockquotes. Descriptions may therefore contain block
markup as well.
Parameters
----------
name : str
Section name (e.g. Parameters)
fake_autosummary : bool
When True, the parameter names may correspond to attributes of the
object beign documented, usually ``property`` instances on a class.
In this case, names will be linked to fuller descriptions.
Returns
-------
rst : list of str
"""
out = []
if self[name]:
if fake_autosummary:
autosum = []
else:
autosum = None

out += self._str_field_list(name)
out += ['']
for param, param_type, desc in self[name]:
display_param, desc = self._process_param(param, desc, autosum)

if param_type:
out += self._str_indent(['**%s** : %s' % (param.strip(),
param_type)])
out += self._str_indent(['%s : %s' % (display_param,
param_type)])
else:
out += self._str_indent(['**%s**' % param.strip()])
out += self._str_indent([display_param])
if desc:
out += ['']
out += [''] # produces a blockquote, rather than a dt/dd
out += self._str_indent(desc, 8)
out += ['']

if fake_autosummary and autosum:
if self.class_members_toctree:
autosum.insert(0, ' :toctree:')
autosum.insert(0, '.. autosummary::')
out += ['..', ' HACK to make autogen generate docs:']
out += self._str_indent(autosum, 4)
out += ['']

return out

@property
Expand Down Expand Up @@ -130,7 +245,7 @@ def _str_member_list(self, name):
or inspect.isgetsetdescriptor(param_obj)):
param_obj = None

if param_obj and (pydoc.getdoc(param_obj) or not desc):
if param_obj and pydoc.getdoc(param_obj):
# Referenced object has a docstring
autosum += [" %s%s" % (prefix, param)]
else:
Expand Down Expand Up @@ -250,7 +365,8 @@ def __str__(self, indent=0, func_role="obj"):
'notes': self._str_section('Notes'),
'references': self._str_references(),
'examples': self._str_examples(),
'attributes': self._str_member_list('Attributes'),
'attributes': self._str_param_list('Attributes',
fake_autosummary=True),
'methods': self._str_member_list('Methods'),
}
ns = dict((k, '\n'.join(v)) for k, v in ns.items())
Expand Down
99 changes: 83 additions & 16 deletions numpydoc/tests/test_docscrape.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
from nose.tools import (assert_equal, assert_raises, assert_list_equal,
assert_true)

assert_list_equal.__self__.maxDiff = None

if sys.version_info[0] >= 3:
sixu = lambda s: s
else:
Expand Down Expand Up @@ -897,8 +899,17 @@ def test_duplicate_signature():
Current time.
y : ndarray
Current variable values.
x : float
Some parameter
* hello
* world
an_attribute : float
The docstring is printed instead
no_docstring : str
But a description
no_docstring2 : str
multiline_sentence
midword_period
no_period
Methods
-------
Expand Down Expand Up @@ -934,8 +945,17 @@ def test_class_members_doc():
Current time.
y : ndarray
Current variable values.
x : float
Some parameter
* hello
* world
an_attribute : float
The docstring is printed instead
no_docstring : str
But a description
no_docstring2 : str
multiline_sentence
midword_period
no_period
Methods
-------
Expand All @@ -952,10 +972,38 @@ def test_class_members_doc():
def test_class_members_doc_sphinx():
class Foo:
@property
def x(self):
def an_attribute(self):
"""Test attribute"""
return None

@property
def no_docstring(self):
return None

@property
def no_docstring2(self):
return None

@property
def multiline_sentence(self):
"""This is a
sentence. It spans multiple lines."""
return None

@property
def midword_period(self):
"""The sentence for numpy.org."""
return None

@property
def no_period(self):
"""This does not have a period
so we truncate its summary to the first linebreak
Apparently.
"""
return None

doc = SphinxClassDoc(Foo, class_doc_txt)
non_blank_line_by_line_compare(str(doc),
"""
Expand All @@ -975,17 +1023,36 @@ def x(self):
For usage examples, see `ode`.
.. rubric:: Attributes
.. autosummary::
:toctree:
x
===== ==========
**t** (float) Current time.
**y** (ndarray) Current variable values.
===== ==========
:Attributes:
**t** : float
Current time.
**y** : ndarray
Current variable values.
* hello
* world
:obj:`an_attribute <an_attribute>` : float
Test attribute
**no_docstring** : str
But a description
**no_docstring2** : str
:obj:`multiline_sentence <multiline_sentence>`
This is a sentence.
:obj:`midword_period <midword_period>`
The sentence for numpy.org.
:obj:`no_period <no_period>`
This does not have a period
..
HACK to make autogen generate docs:
.. autosummary::
:toctree:
an_attribute
multiline_sentence
midword_period
no_period
.. rubric:: Methods
Expand Down

0 comments on commit b215bed

Please sign in to comment.