From e63a3779624c1ac35c6e37859f1bfebd5e97e513 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 13:22:52 -0400 Subject: [PATCH 1/9] BUG: Allow no . at end if indented --- numpydoc/tests/test_validate.py | 5 ++++- numpydoc/validate.py | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 3f162139..2a0c93e9 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 diff --git a/numpydoc/validate.py b/numpydoc/validate.py index f268d8b8..9379bfbe 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -257,10 +257,13 @@ def extended_summary(self): @property def doc_parameters(self): + return self._get_doc_parameters() + + def _get_doc_parameters(self, joiner=""): parameters = collections.OrderedDict() for names, type_, desc in self.doc["Parameters"]: for name in names.split(", "): - parameters[name] = (type_, "".join(desc)) + parameters[name] = (type_, joiner.join(desc)) return parameters @property @@ -329,8 +332,8 @@ 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] + def parameter_desc(self, param, joiner=""): + desc = self._get_doc_parameters(joiner)[param][1] # Find and strip out any sphinx directives for directive in DIRECTIVES: full_directive = ".. {}".format(directive) @@ -541,12 +544,16 @@ def validate(func_name): wrong_type=wrong_type, ) ) - if not doc.parameter_desc(param): + this_desc = doc.parameter_desc(param, "\n").rstrip("\n") + if not this_desc: errs.append(error("PR07", param_name=param)) else: - if doc.parameter_desc(param)[0].isalpha() and not doc.parameter_desc(param)[0].isupper(): + if this_desc[0].isalpha() and not this_desc[0].isupper(): errs.append(error("PR08", param_name=param)) - if doc.parameter_desc(param)[-1] != ".": + # Not ending in "." is only an error if the last bit is not + # indented (e.g., quote or code block) + if this_desc[-1] != "." and \ + not this_desc.split("\n")[-1].startswith(" "): errs.append(error("PR09", param_name=param)) if doc.is_function_or_method: From 33d35e80b65ad240ac6229b7fbaff1cbab080378 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 13:58:45 -0400 Subject: [PATCH 2/9] BUG: Returns, too --- numpydoc/tests/test_validate.py | 4 +++- numpydoc/validate.py | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 2a0c93e9..284b5fa8 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -92,7 +92,9 @@ def sample(self): Returns ------- float - Random number generated. + Random number generated, e.g.:: + + 1.0 See Also -------- diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 9379bfbe..e533b8c0 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -567,10 +567,11 @@ def validate(func_name): if not desc: errs.append(error("RT03")) else: - desc = " ".join(desc) + desc = "\n".join(desc) if desc[0].isalpha() and not desc[0].isupper(): errs.append(error("RT04")) - if not desc.endswith("."): + if not desc.endswith(".") and \ + not desc.split("\n")[-1].startswith(" "): errs.append(error("RT05")) if not doc.yields and "yield" in doc.method_source: From 5efa1d14d0a29935be4c302bf88fbd9aafd95188 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 14:43:07 -0400 Subject: [PATCH 3/9] MAINT: Simplify --- numpydoc/tests/test_validate.py | 4 ++-- numpydoc/validate.py | 29 ++++++++++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 284b5fa8..8b7842b2 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -92,9 +92,9 @@ def sample(self): Returns ------- float - Random number generated, e.g.:: + Random number generated. - 1.0 + - Make sure you set a seed for reproducibility See Also -------- diff --git a/numpydoc/validate.py b/numpydoc/validate.py index e533b8c0..1b3690e3 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): """ @@ -257,13 +260,10 @@ def extended_summary(self): @property def doc_parameters(self): - return self._get_doc_parameters() - - def _get_doc_parameters(self, joiner=""): parameters = collections.OrderedDict() for names, type_, desc in self.doc["Parameters"]: for name in names.split(", "): - parameters[name] = (type_, joiner.join(desc)) + parameters[name] = (type_, desc) return parameters @property @@ -332,14 +332,15 @@ def directives_without_two_colons(self): def parameter_type(self, param): return self.doc_parameters[param][0] - def parameter_desc(self, param, joiner=""): - desc = self._get_doc_parameters(joiner)[param][1] + def parameter_desc(self, param): + desc = "\n".join(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)] + desc = desc[: desc.index(full_directive)].rstrip("\n") + desc = desc.split("\n") return desc @property @@ -544,17 +545,19 @@ def validate(func_name): wrong_type=wrong_type, ) ) - this_desc = doc.parameter_desc(param, "\n").rstrip("\n") - if not this_desc: + this_desc = doc.parameter_desc(param) + if not ''.join(this_desc): errs.append(error("PR07", param_name=param)) else: - if this_desc[0].isalpha() and not this_desc[0].isupper(): + if this_desc[0][0].isalpha() and not this_desc[0][0].isupper(): errs.append(error("PR08", param_name=param)) # Not ending in "." is only an error if the last bit is not # indented (e.g., quote or code block) - if this_desc[-1] != "." and \ - not this_desc.split("\n")[-1].startswith(" "): + if this_desc[-1][-1] != "." and \ + not this_desc[-1].startswith(IGNORE_STARTS): errs.append(error("PR09", param_name=param)) + if param == 'axis': + raise RuntimeError if doc.is_function_or_method: if not doc.returns: @@ -571,7 +574,7 @@ def validate(func_name): if desc[0].isalpha() and not desc[0].isupper(): errs.append(error("RT04")) if not desc.endswith(".") and \ - not desc.split("\n")[-1].startswith(" "): + not desc.split("\n")[-1].startswith(IGNORE_STARTS): errs.append(error("RT05")) if not doc.yields and "yield" in doc.method_source: From 86a7004dfa24a9b55847c3d9651504dd3a20bb01 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 14:46:16 -0400 Subject: [PATCH 4/9] MAINT: Clean up the returns check, too --- numpydoc/validate.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 1b3690e3..156e1a8f 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -570,11 +570,10 @@ def validate(func_name): if not desc: errs.append(error("RT03")) else: - desc = "\n".join(desc) - if desc[0].isalpha() and not desc[0].isupper(): + if desc[0][0].isalpha() and not desc[0][0].isupper(): errs.append(error("RT04")) - if not desc.endswith(".") and \ - not desc.split("\n")[-1].startswith(IGNORE_STARTS): + if not desc[-1].endswith(".") and \ + not desc[-1].startswith(IGNORE_STARTS): errs.append(error("RT05")) if not doc.yields and "yield" in doc.method_source: From e809788af1a2f69429de4107ace7c207e7293532 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 14:50:11 -0400 Subject: [PATCH 5/9] BUG: Debug cruft --- numpydoc/validate.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 156e1a8f..27b10a94 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -546,7 +546,7 @@ def validate(func_name): ) ) this_desc = doc.parameter_desc(param) - if not ''.join(this_desc): + if not "".join(this_desc): errs.append(error("PR07", param_name=param)) else: if this_desc[0][0].isalpha() and not this_desc[0][0].isupper(): @@ -556,8 +556,6 @@ def validate(func_name): if this_desc[-1][-1] != "." and \ not this_desc[-1].startswith(IGNORE_STARTS): errs.append(error("PR09", param_name=param)) - if param == 'axis': - raise RuntimeError if doc.is_function_or_method: if not doc.returns: From e0e2cab93743c6093dacc3eaef8169704d763ec7 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 14:51:42 -0400 Subject: [PATCH 6/9] MAINT: Unify --- numpydoc/validate.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 27b10a94..78256ebe 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -545,16 +545,16 @@ def validate(func_name): wrong_type=wrong_type, ) ) - this_desc = doc.parameter_desc(param) - if not "".join(this_desc): + desc = doc.parameter_desc(param) + if not "".join(desc): errs.append(error("PR07", param_name=param)) else: - if this_desc[0][0].isalpha() and not this_desc[0][0].isupper(): + if desc[0][0].isalpha() and not desc[0][0].isupper(): errs.append(error("PR08", param_name=param)) # Not ending in "." is only an error if the last bit is not # indented (e.g., quote or code block) - if this_desc[-1][-1] != "." and \ - not this_desc[-1].startswith(IGNORE_STARTS): + if not desc[-1].endswith(".") and \ + not desc[-1].startswith(IGNORE_STARTS): errs.append(error("PR09", param_name=param)) if doc.is_function_or_method: From c4ca99ba169ca46912bdbce0ff5ac584230ae27f Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 15:04:51 -0400 Subject: [PATCH 7/9] MAINT: Unify sanitizing and processing of desc --- numpydoc/tests/test_validate.py | 2 ++ numpydoc/validate.py | 58 +++++++++++++++------------------ 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/numpydoc/tests/test_validate.py b/numpydoc/tests/test_validate.py index 8b7842b2..ec2bbc9b 100644 --- a/numpydoc/tests/test_validate.py +++ b/numpydoc/tests/test_validate.py @@ -120,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 78256ebe..5ff9df07 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -332,17 +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 = "\n".join(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)].rstrip("\n") - desc = desc.split("\n") - return desc - @property def see_also(self): result = collections.OrderedDict() @@ -412,6 +401,29 @@ def deprecated(self): return ".. deprecated:: " in (self.summary + self.extended_summary) +def _check_desc(desc, errs, kinds, **kwargs): + assert len(kinds) == 3 + # 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") + + if not "".join(desc): + errs.append(error(kinds[0], **kwargs)) + else: + if desc[0][0].isalpha() and not desc[0][0].isupper(): + errs.append(error(kinds[1], **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(kinds[2], **kwargs)) + + def validate(func_name): """ Validate the docstring. @@ -520,7 +532,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: @@ -545,17 +557,8 @@ def validate(func_name): wrong_type=wrong_type, ) ) - desc = doc.parameter_desc(param) - if not "".join(desc): - errs.append(error("PR07", param_name=param)) - else: - if desc[0][0].isalpha() and not desc[0][0].isupper(): - errs.append(error("PR08", param_name=param)) - # 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("PR09", param_name=param)) + _check_desc(kind_desc[1], errs, + ("PR07", "PR08", "PR09"), param_name=param) if doc.is_function_or_method: if not doc.returns: @@ -565,14 +568,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: - if desc[0][0].isalpha() and not desc[0][0].isupper(): - errs.append(error("RT04")) - if not desc[-1].endswith(".") and \ - not desc[-1].startswith(IGNORE_STARTS): - errs.append(error("RT05")) + _check_desc(desc, errs, ("RT03", "RT04", "RT05")) if not doc.yields and "yield" in doc.method_source: errs.append(error("YD01")) From 5e9102044f1851c342c34978631a2347830336b2 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 15:21:16 -0400 Subject: [PATCH 8/9] STY: Cleaner --- numpydoc/validate.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/numpydoc/validate.py b/numpydoc/validate.py index 5ff9df07..add8da54 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -401,8 +401,7 @@ def deprecated(self): return ".. deprecated:: " in (self.summary + self.extended_summary) -def _check_desc(desc, errs, kinds, **kwargs): - assert len(kinds) == 3 +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: @@ -412,16 +411,18 @@ def _check_desc(desc, errs, kinds, **kwargs): desc = desc[: desc.index(full_directive)].rstrip("\n") desc = desc.split("\n") + errs = list() if not "".join(desc): - errs.append(error(kinds[0], **kwargs)) + errs.append(error(code_no_desc, **kwargs)) else: if desc[0][0].isalpha() and not desc[0][0].isupper(): - errs.append(error(kinds[1], **kwargs)) + 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(kinds[2], **kwargs)) + errs.append(error(code_no_period, **kwargs)) + return errs def validate(func_name): @@ -557,8 +558,8 @@ def validate(func_name): wrong_type=wrong_type, ) ) - _check_desc(kind_desc[1], errs, - ("PR07", "PR08", "PR09"), param_name=param) + errs += _check_desc( + kind_desc[1], "PR07", "PR08", "PR09", param_name=param) if doc.is_function_or_method: if not doc.returns: @@ -568,7 +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: - _check_desc(desc, errs, ("RT03", "RT04", "RT05")) + errs += _check_desc(desc, "RT03", "RT04", "RT05") if not doc.yields and "yield" in doc.method_source: errs.append(error("YD01")) From b0133be5ad00931376e4bc27f0c9526c907680fc Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Fri, 25 Oct 2019 15:22:31 -0400 Subject: [PATCH 9/9] STY: extend rather than += --- numpydoc/validate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/numpydoc/validate.py b/numpydoc/validate.py index add8da54..fe0473c6 100644 --- a/numpydoc/validate.py +++ b/numpydoc/validate.py @@ -558,8 +558,8 @@ def validate(func_name): wrong_type=wrong_type, ) ) - errs += _check_desc( - kind_desc[1], "PR07", "PR08", "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: @@ -569,7 +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: - errs += _check_desc(desc, "RT03", "RT04", "RT05") + errs.extend(_check_desc(desc, "RT03", "RT04", "RT05")) if not doc.yields and "yield" in doc.method_source: errs.append(error("YD01"))