diff --git a/numpydoc/docscrape.py b/numpydoc/docscrape.py index e5c07f59..348f63a5 100644 --- a/numpydoc/docscrape.py +++ b/numpydoc/docscrape.py @@ -95,7 +95,7 @@ def is_unindented(line): return self.read_to_condition(is_unindented) def peek(self, n=0): - if self._l + n < len(self._str): + if 0 <= self._l + n < len(self._str): return self[self._l + n] else: return "" @@ -204,14 +204,34 @@ def _strip(self, doc): return doc[i : len(doc) - j] + def read_to_next_empty_line(self): + data = [] + while True: + old_line = self._doc._l + if self._is_at_section() and self._doc.peek(-1).strip() != "": + section_name = self._doc.peek() + self._error_location( + "missing empty line before the %s section" % section_name, + error=False, + ) + self._doc._l = old_line + + current = self._doc.read() + if not current.strip(): + break + + data.append(current) + + return data + def _read_to_next_section(self): - section = self._doc.read_to_next_empty_line() + section = self.read_to_next_empty_line() while not self._is_at_section() and not self._doc.eof(): if not self._doc.peek(-1).strip(): # previous line was empty section += [""] - section += self._doc.read_to_next_empty_line() + section += self.read_to_next_empty_line() return section @@ -376,7 +396,7 @@ def _parse_summary(self): # If several signatures present, take the last one while True: - summary = self._doc.read_to_next_empty_line() + summary = self.read_to_next_empty_line() summary_str = " ".join([s.strip() for s in summary]).strip() compiled = re.compile(r"^([\w., ]+=)?\s*[\w\.]+\(.*\)$") if compiled.match(summary_str): diff --git a/numpydoc/tests/test_docscrape.py b/numpydoc/tests/test_docscrape.py index 227f8724..762887f7 100644 --- a/numpydoc/tests/test_docscrape.py +++ b/numpydoc/tests/test_docscrape.py @@ -1010,6 +1010,43 @@ def test_no_summary(): ) +@pytest.mark.parametrize( + ["docstring", "expected_warning"], + ( + ( + """\ + Parameters without separating empty line: + Parameters + ---------- + data + some parameter + """, + "missing empty line before the Parameters section", + ), + ( + """\ + Parameters + ---------- + data + some parameter + Returns + ------- + int + """, + "missing empty line before the Returns section", + ), + ), + ids=[ + "missing empty line after summary", + "missing empty line between sections", + ], +) +def test_section_detection_warnings(docstring, expected_warning): + warning_re = ".*%s.*" % expected_warning + with pytest.warns(UserWarning, match=warning_re): + _ = NumpyDocString(docstring) + + def test_unicode(): doc = SphinxDocString( """