diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index 8acc54cf..074a7f73 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -331,9 +331,19 @@ def _parse(self): section = (s.capitalize() for s in section.split(' ')) section = ' '.join(section) if self.get(section): - msg = ("The section %s appears twice in the docstring." % - section) - raise ValueError(msg) + if hasattr(self, '_obj'): + # we know where the docs came from: + try: + filename = inspect.getsourcefile(self._obj) + except TypeError: + filename = None + msg = ("The section %s appears twice in " + "the docstring of %s in %s." % + (section, self._obj, filename)) + raise ValueError(msg) + else: + msg = ("The section %s appears twice" % section) + raise ValueError(msg) if section in ('Parameters', 'Returns', 'Yields', 'Raises', 'Warns', 'Other Parameters', 'Attributes', diff --git a/numpydoc/numpydoc.py b/numpydoc/numpydoc.py index eab47569..7deecc55 100644 --- a/numpydoc/numpydoc.py +++ b/numpydoc/numpydoc.py @@ -113,7 +113,6 @@ def mangle_signature(app, what, name, obj, options, sig, retann): if not hasattr(obj, '__doc__'): return - doc = SphinxDocString(pydoc.getdoc(obj)) sig = doc['Signature'] or getattr(obj, '__text_signature__', None) if sig: diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 8fae0dfb..297a0acb 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -1,8 +1,8 @@ # -*- encoding:utf-8 -*- from __future__ import division, absolute_import, print_function -import sys, textwrap -import io +import sys +import textwrap import jinja2 @@ -12,8 +12,10 @@ ClassDoc, ParseError ) -from numpydoc.docscrape_sphinx import SphinxDocString, SphinxClassDoc -from nose.tools import * +from numpydoc.docscrape_sphinx import (SphinxDocString, SphinxClassDoc, + SphinxFunctionDoc) +from nose.tools import (assert_equal, assert_raises, assert_list_equal, + assert_true) if sys.version_info[0] >= 3: sixu = lambda s: s @@ -232,6 +234,55 @@ def test_section_twice(): """ assert_raises(ValueError, NumpyDocString, doc_text) + # if we have a numpydoc object, we know where the error came from + class Dummy(object): + """ + Dummy class. + + Notes + ----- + First note. + + Notes + ----- + Second note. + + """ + def spam(self, a, b): + """Spam\n\nSpam spam.""" + pass + + def ham(self, c, d): + """Cheese\n\nNo cheese.""" + pass + + def dummy_func(arg): + """ + Dummy function. + + Notes + ----- + First note. + + Notes + ----- + Second note. + """ + + try: + SphinxClassDoc(Dummy) + except ValueError as e: + # python 3 version or python 2 version + assert_true("test_section_twice..Dummy" in str(e) + or 'test_docscrape.Dummy' in str(e)) + + try: + SphinxFunctionDoc(dummy_func) + except ValueError as e: + # python 3 version or python 2 version + assert_true("test_section_twice..dummy_func" in str(e) + or 'function dummy_func' in str(e)) + def test_notes(): assert doc['Notes'][0].startswith('Instead') @@ -969,6 +1020,8 @@ def test_templated_sections(): """) + + if __name__ == "__main__": import nose nose.run()