diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 3f162139..ec2bbc9b 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -36,7 +36,10 @@ def plot(self, kind, color="blue", **kwargs): Parameters ---------- kind : str - Kind of matplotlib plot. + Kind of matplotlib plot, e.g.:: + + 'foo' + color : str, default 'blue' Color name or rgb code. **kwargs @@ -91,6 +94,8 @@ def sample(self): float Random number generated. + - Make sure you set a seed for reproducibility + See Also -------- related : Something related. @@ -115,6 +120,8 @@ def random_letters(self): letters : str String of random letters. + .. versionadded:: 0.1 + See Also -------- related : Something related. diff --git a/numpydoc/validate.py b/numpydoc/validate.py index f268d8b8..fe0473c6 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -90,6 +90,9 @@ "EX01": "No examples section found", } +# Ignore these when evaluating end-of-line-"." checks +IGNORE_STARTS = (" ", "* ", "- ") + def error(code, **kwargs): """ @@ -260,7 +263,7 @@ def doc_parameters(self): parameters = collections.OrderedDict() for names, type_, desc in self.doc["Parameters"]: for name in names.split(", "): - parameters[name] = (type_, "".join(desc)) + parameters[name] = (type_, desc) return parameters @property @@ -329,16 +332,6 @@ def directives_without_two_colons(self): def parameter_type(self, param): return self.doc_parameters[param][0] - def parameter_desc(self, param): - desc = self.doc_parameters[param][1] - # Find and strip out any sphinx directives - for directive in DIRECTIVES: - full_directive = ".. {}".format(directive) - if full_directive in desc: - # Only retain any description before the directive - desc = desc[: desc.index(full_directive)] - return desc - @property def see_also(self): result = collections.OrderedDict() @@ -408,6 +401,30 @@ def deprecated(self): return ".. deprecated:: " in (self.summary + self.extended_summary) +def _check_desc(desc, code_no_desc, code_no_upper, code_no_period, **kwargs): + # Find and strip out any sphinx directives + desc = "\n".join(desc) + for directive in DIRECTIVES: + full_directive = ".. {}".format(directive) + if full_directive in desc: + # Only retain any description before the directive + desc = desc[: desc.index(full_directive)].rstrip("\n") + desc = desc.split("\n") + + errs = list() + if not "".join(desc): + errs.append(error(code_no_desc, **kwargs)) + else: + if desc[0][0].isalpha() and not desc[0][0].isupper(): + errs.append(error(code_no_upper, **kwargs)) + # Not ending in "." is only an error if the last bit is not + # indented (e.g., quote or code block) + if not desc[-1].endswith(".") and \ + not desc[-1].startswith(IGNORE_STARTS): + errs.append(error(code_no_period, **kwargs)) + return errs + + def validate(func_name): """ Validate the docstring. @@ -516,7 +533,7 @@ def validate(func_name): # PR03: Wrong parameters order errs += doc.parameter_mismatches - for param in doc.doc_parameters: + for param, kind_desc in doc.doc_parameters.items(): if not param.startswith("*"): # Check can ignore var / kwargs if not doc.parameter_type(param): if ":" in param: @@ -541,13 +558,8 @@ def validate(func_name): wrong_type=wrong_type, ) ) - if not doc.parameter_desc(param): - errs.append(error("PR07", param_name=param)) - else: - if doc.parameter_desc(param)[0].isalpha() and not doc.parameter_desc(param)[0].isupper(): - errs.append(error("PR08", param_name=param)) - if doc.parameter_desc(param)[-1] != ".": - errs.append(error("PR09", param_name=param)) + errs.extend(_check_desc( + kind_desc[1], "PR07", "PR08", "PR09", param_name=param)) if doc.is_function_or_method: if not doc.returns: @@ -557,14 +569,7 @@ def validate(func_name): if len(doc.returns) == 1 and doc.returns[0].name: errs.append(error("RT02")) for name_or_type, type_, desc in doc.returns: - if not desc: - errs.append(error("RT03")) - else: - desc = " ".join(desc) - if desc[0].isalpha() and not desc[0].isupper(): - errs.append(error("RT04")) - if not desc.endswith("."): - errs.append(error("RT05")) + errs.extend(_check_desc(desc, "RT03", "RT04", "RT05")) if not doc.yields and "yield" in doc.method_source: errs.append(error("YD01"))