From 5f641a1e073aba3d747898ddc6cd9eb061804888 Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Mon, 7 Aug 2017 09:56:43 +1000 Subject: [PATCH 1/5] Format attributes like parameters - Provide links to autogen when possible - Use fake (commented) autosummary nodes (as already used in scipy docs) --- numpydoc/docscrape_sphinx.py | 64 +++++++++++++++++++++++++++++--- numpydoc/tests/test_docscrape.py | 22 +++++++---- 2 files changed, 73 insertions(+), 13 deletions(-) diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index d8a495e0..8aade392 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -79,21 +79,74 @@ def _str_returns(self, name='Returns'): out += [''] return out - def _str_param_list(self, name): + def _str_param_list(self, name, fake_autosummary=False): out = [] if self[name]: + if fake_autosummary: + prefix = getattr(self, '_name', '') + if prefix: + autosum_prefix = '~%s.' % prefix + link_prefix = '%s.' % prefix + else: + autosum_prefix = '' + link_prefix = '' + autosum = [] + out += self._str_field_list(name) out += [''] for param, param_type, desc in self[name]: + param = param.strip() + + display_param = '**%s**' % param + + if fake_autosummary: + 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 param_obj and (obj_doc or not desc): + # Referenced object has a docstring + autosum += [" %s%s" % (autosum_prefix, param)] + # TODO: add signature to display as in autosummary + # Difficult because autosummary's processing + # involves sphinx's plugin mechanism, for + # directives only + 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|$)", desc) + if m: + desc = m.group(1).strip() + else: + desc = desc.partition('\n')[0] + desc = desc.split('\n') + 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 += 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 @@ -250,7 +303,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()) diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 297a0acb..bc5f191e 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -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: @@ -975,17 +977,21 @@ def x(self): For usage examples, see `ode`. - .. rubric:: Attributes + :Attributes: - .. autosummary:: - :toctree: + **t** : float + Current time. + **y** : ndarray + Current variable values. + :obj:`x ` : float + Test attribute - x + .. + HACK to make autogen generate docs: + .. autosummary:: + :toctree: - ===== ========== - **t** (float) Current time. - **y** (ndarray) Current variable values. - ===== ========== + x .. rubric:: Methods From f09abc8e90a6dffab37581c57fb89500031b33d3 Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Mon, 21 Aug 2017 18:54:26 +1000 Subject: [PATCH 2/5] Test autosummary-like behaviour And remove quirky 'or not desc' case --- numpydoc/docscrape_sphinx.py | 7 +-- numpydoc/tests/test_docscrape.py | 75 +++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 8aade392..98d41257 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -107,7 +107,7 @@ def _str_param_list(self, name, fake_autosummary=False): param_obj = None obj_doc = pydoc.getdoc(param_obj) - if param_obj and (obj_doc or not desc): + if param_obj and obj_doc: # Referenced object has a docstring autosum += [" %s%s" % (autosum_prefix, param)] # TODO: add signature to display as in autosummary @@ -122,7 +122,8 @@ def _str_param_list(self, name, fake_autosummary=False): 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|$)", desc) + m = re.search(r"^([A-Z].*?\.)(?:\s|$)", + ' '.join(desc.split())) if m: desc = m.group(1).strip() else: @@ -183,7 +184,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: diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index bc5f191e..2ae51c66 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -899,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 ------- @@ -936,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 ------- @@ -954,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), """ @@ -983,15 +1029,30 @@ def x(self): Current time. **y** : ndarray Current variable values. - :obj:`x ` : float + + * hello + * world + :obj:`an_attribute ` : float Test attribute + **no_docstring** : str + But a description + **no_docstring2** : str + :obj:`multiline_sentence ` + This is a sentence. + :obj:`midword_period ` + The sentence for numpy.org. + :obj:`no_period ` + This does not have a period .. HACK to make autogen generate docs: .. autosummary:: :toctree: - x + an_attribute + multiline_sentence + midword_period + no_period .. rubric:: Methods From a1e7eba8fc0a49d1fd5d1ed76b83ed5d63979ea6 Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Thu, 7 Sep 2017 19:08:16 +1000 Subject: [PATCH 3/5] Some cleaning and docs --- numpydoc/docscrape_sphinx.py | 116 ++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 98d41257..1583d280 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -79,56 +79,90 @@ def _str_returns(self, name='Returns'): out += [''] return out + 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 + 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. + """ + 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): out = [] if self[name]: if fake_autosummary: - prefix = getattr(self, '_name', '') - if prefix: - autosum_prefix = '~%s.' % prefix - link_prefix = '%s.' % prefix - else: - autosum_prefix = '' - link_prefix = '' autosum = [] + else: + autosum = None out += self._str_field_list(name) out += [''] for param, param_type, desc in self[name]: - param = param.strip() - - display_param = '**%s**' % param - - if fake_autosummary: - 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 param_obj and obj_doc: - # Referenced object has a docstring - autosum += [" %s%s" % (autosum_prefix, param)] - # TODO: add signature to display as in autosummary - # Difficult because autosummary's processing - # involves sphinx's plugin mechanism, for - # directives only - 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') + display_param, desc = self._process_param(param, desc, autosum) if param_type: out += self._str_indent(['%s : %s' % (display_param, From 7fa273c0b16812563d8b58fb42cdbe28cda67d2d Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Thu, 7 Sep 2017 20:19:52 +1000 Subject: [PATCH 4/5] More docs --- numpydoc/docscrape_sphinx.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 1583d280..52857af6 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -89,7 +89,8 @@ def _process_param(self, param, desc, autosum): param : str The name of the parameter desc : list of str - The parameter description as given in the docstring + 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. @@ -152,6 +153,25 @@ def _process_param(self, param, desc, autosum): 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: @@ -170,7 +190,7 @@ def _str_param_list(self, name, fake_autosummary=False): else: 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 += [''] From c917226ebdae56780ddf5fe5824a5bdf2bc1a771 Mon Sep 17 00:00:00 2001 From: Joel Nothman Date: Thu, 7 Sep 2017 20:23:06 +1000 Subject: [PATCH 5/5] Note on methods --- numpydoc/docscrape_sphinx.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/numpydoc/docscrape_sphinx.py b/numpydoc/docscrape_sphinx.py index 52857af6..a555ed06 100644 --- a/numpydoc/docscrape_sphinx.py +++ b/numpydoc/docscrape_sphinx.py @@ -108,6 +108,13 @@ def _process_param(self, param, desc, autosum): ``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