diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/whatsnew/2.0.rst | 24 | ||||
-rw-r--r-- | pylint/extensions/_check_docs_utils.py | 52 | ||||
-rw-r--r-- | pylint/extensions/docparams.py | 51 | ||||
-rw-r--r-- | pylint/test/extensions/test_check_return_docs.py | 145 | ||||
-rw-r--r-- | pylint/test/extensions/test_check_yields_docs.py | 69 |
6 files changed, 245 insertions, 101 deletions
@@ -143,6 +143,11 @@ Release date: tba * UnknownMessage and EmptyReport are renamed to UnknownMessageError and EmptyReportError. + * Warnings 'missing-returns-type-doc' and 'missing-yields-type-doc' + have each been split into two warnings - 'missing-[return|yield]-doc' + and 'missing-[return|yield]-type-doc'. + + What's new in Pylint 1.6.3? =========================== diff --git a/doc/whatsnew/2.0.rst b/doc/whatsnew/2.0.rst index 9e5a0ba5c..8ec21d29c 100644 --- a/doc/whatsnew/2.0.rst +++ b/doc/whatsnew/2.0.rst @@ -89,7 +89,31 @@ New checkers def not_useless_1(self, first, *args): return super(Impl, self).not_useless_1(first + some_value, *args) +* The warnings ``missing-returns-doc`` and ``missing-yields-doc`` have each + been replaced with two new warnings - ``missing-[return|yield]-doc`` and + ``missing-[return|yield]-type-doc``. Having these as separate warnings + allows the user to choose whether their documentation style requires + text descriptions of function return/yield, specification of return/yield + types, or both. + .. code-block:: python + + # This will raise missing-return-type-doc but not missing-return-doc + def my_sphinx_style_func(self): + """This is a Sphinx-style docstring. + + :returns: Always False + """ + return False + + # This will raise missing-return-doc but not missing-return-type-doc + def my_google_style_func(self): + """This is a Google-style docstring. + + Returns: + bool: + """ + return False Other Changes ============= diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py index f6ad4be13..61b7a86d8 100644 --- a/pylint/extensions/_check_docs_utils.py +++ b/pylint/extensions/_check_docs_utils.py @@ -137,9 +137,15 @@ class Docstring(object): def has_returns(self): return False + def has_rtype(self): + return False + def has_yields(self): return False + def has_yields_type(self): + return False + def match_param_docs(self): return set(), set() @@ -231,8 +237,13 @@ class SphinxDocstring(Docstring): if not self.doc: return False - return bool(self.re_rtype_in_docstring.search(self.doc) and - self.re_returns_in_docstring.search(self.doc)) + return bool(self.re_returns_in_docstring.search(self.doc)) + + def has_rtype(self): + if not self.doc: + return False + + return bool(self.re_rtype_in_docstring.search(self.doc)) def match_param_docs(self): params_with_doc = set() @@ -334,9 +345,24 @@ class GoogleDocstring(Docstring): if not match: continue - return_type = match.group(1) return_desc = match.group(2) - if return_type and return_desc: + if return_desc: + return True + + return False + + def has_rtype(self): + if not self.doc: + return False + + entries = self._parse_section(self.re_returns_section) + for entry in entries: + match = self.re_returns_line.match(entry) + if not match: + continue + + return_type = match.group(1) + if return_type: return True return False @@ -351,13 +377,27 @@ class GoogleDocstring(Docstring): if not match: continue - yield_type = match.group(1) yield_desc = match.group(2) - if yield_type and yield_desc: + if yield_desc: return True return False + def has_yields_type(self): + if not self.doc: + return False + + entries = self._parse_section(self.re_yields_section) + for entry in entries: + match = self.re_yields_line.match(entry) + if not match: + continue + + yield_type = match.group(1) + if yield_type: + return True + + return False def exceptions(self): types = set() diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py index cf83421e0..8b3a80e24 100644 --- a/pylint/extensions/docparams.py +++ b/pylint/extensions/docparams.py @@ -54,18 +54,32 @@ class DocstringParameterChecker(BaseChecker): 'W9006': ('"%s" not documented as being raised', 'missing-raises-doc', 'Please document exceptions for all raised exception types.'), - 'W9007': ('Missing return type documentation', - 'missing-returns-doc', - 'Please add documentation about what this method returns.'), 'W9008': ('Redundant returns documentation', 'redundant-returns-doc', - 'Please remove the return documentation from this method.'), - 'W9009': ('Missing yield type documentation', - 'missing-yields-doc', - 'Please add documentation about what this generator yields.'), + 'Please remove the return/rtype documentation from this method.'), 'W9010': ('Redundant yields documentation', 'redundant-yields-doc', 'Please remove the yields documentation from this method.'), + 'W9011': ('Missing return documentation', + 'missing-return-doc', + 'Please add documentation about what this method returns.', + {'old_names': [('W9007', 'missing-returns-doc')]}), + 'W9012': ('Missing return type documentation', + 'missing-return-type-doc', + 'Please document the type returned by this method.', + # we can't use the same old_name for two different warnings + # {'old_names': [('W9007', 'missing-returns-doc')]}, + ), + 'W9013': ('Missing yield documentation', + 'missing-yield-doc', + 'Please add documentation about what this generator yields.', + {'old_names': [('W9009', 'missing-yields-doc')]}), + 'W9014': ('Missing yield type documentation', + 'missing-yield-type-doc', + 'Please document the type yielded by this method.', + # we can't use the same old_name for two different warnings + # {'old_names': [('W9009', 'missing-yields-doc')]}, + ), } options = (('accept-no-param-doc', @@ -141,7 +155,7 @@ class DocstringParameterChecker(BaseChecker): return return_nodes = node.nodes_of_class(astroid.Return) - if (node_doc.has_returns() and + if ((node_doc.has_returns() or node_doc.has_rtype()) and not any(utils.returns_something(ret_node) for ret_node in return_nodes)): self.add_message( 'redundant-returns-doc', @@ -151,7 +165,8 @@ class DocstringParameterChecker(BaseChecker): if not node_doc.supports_yields: return - if node_doc.has_yields() and not node.is_generator(): + if ((node_doc.has_yields() or node_doc.has_yields_type()) and + not node.is_generator()): self.add_message( 'redundant-yields-doc', node=node) @@ -189,7 +204,13 @@ class DocstringParameterChecker(BaseChecker): if not doc.has_returns(): self.add_message( - 'missing-returns-doc', + 'missing-return-doc', + node=func_node + ) + + if not doc.has_rtype(): + self.add_message( + 'missing-return-type-doc', node=func_node ) @@ -204,12 +225,20 @@ class DocstringParameterChecker(BaseChecker): if doc.supports_yields: doc_has_yields = doc.has_yields() + doc_has_yields_type = doc.has_yields_type() else: doc_has_yields = doc.has_returns() + doc_has_yields_type = doc.has_rtype() if not doc_has_yields: self.add_message( - 'missing-yields-doc', + 'missing-yield-doc', + node=func_node + ) + + if not doc_has_yields_type: + self.add_message( + 'missing-yield-type-doc', node=func_node ) diff --git a/pylint/test/extensions/test_check_return_docs.py b/pylint/test/extensions/test_check_return_docs.py index c2966c601..4be854ac9 100644 --- a/pylint/test/extensions/test_check_return_docs.py +++ b/pylint/test/extensions/test_check_return_docs.py @@ -36,9 +36,8 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node), + Message(msg_id='missing-return-type-doc', node=node)): self.checker.visit_return(return_node) def test_ignores_unknown_style(self): @@ -61,9 +60,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-type-doc', node=node)): self.checker.visit_return(return_node) def test_warn_partial_sphinx_returns_type(self): @@ -77,9 +74,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warn_missing_sphinx_returns(self): @@ -94,9 +89,8 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node), + Message(msg_id='missing-return-type-doc', node=node)): self.checker.visit_return(return_node) def test_warn_partial_google_returns(self): @@ -111,9 +105,22 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-type-doc', node=node)): + self.checker.visit_return(return_node) + + def test_warn_partial_google_returns_type(self): + node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + + Returns: + bool: + """ + return False + ''') + return_node = node.body[0] + with self.assertAddsMessages( + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warn_missing_google_returns(self): @@ -128,9 +135,29 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node), + Message(msg_id='missing-return-type-doc', node=node)): + self.checker.visit_return(return_node) + + def test_warn_partial_numpy_returns_type(self): + node = astroid.extract_node(''' + def my_func(self, doc_type): + """This is a docstring. + + Arguments + --------- + doc_type : str + Numpy + + Returns + ------- + bool + """ + return False + ''') + return_node = node.body[0] + with self.assertAddsMessages( + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warn_missing_numpy_returns(self): @@ -147,9 +174,8 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node), + Message(msg_id='missing-return-type-doc', node=node)): self.checker.visit_return(return_node) def test_find_sphinx_returns(self): @@ -327,9 +353,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warns_google_return_list_of_custom_class_without_description(self): @@ -338,15 +362,13 @@ class DocstringCheckerReturnTest(CheckerTestCase): """This is a docstring. Returns: - list(:class:`mymodule.Class`) + list(:class:`mymodule.Class`): """ return [mymodule.Class()] ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warns_numpy_return_list_of_custom_class_without_description(self): @@ -362,9 +384,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): ''') return_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-returns-doc', - node=node)): + Message(msg_id='missing-return-doc', node=node)): self.checker.visit_return(return_node) def test_warns_sphinx_redundant_return_doc(self): @@ -373,14 +393,24 @@ class DocstringCheckerReturnTest(CheckerTestCase): """This is a docstring. :returns: One + """ + return None + ''') + with self.assertAddsMessages( + Message(msg_id='redundant-returns-doc', node=node)): + self.checker.visit_functiondef(node) + + def test_warns_sphinx_redundant_rtype_doc(self): + node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + :rtype: int """ return None ''') with self.assertAddsMessages( - Message( - msg_id='redundant-returns-doc', - node=node)): + Message(msg_id='redundant-returns-doc', node=node)): self.checker.visit_functiondef(node) def test_warns_google_redundant_return_doc(self): @@ -389,14 +419,26 @@ class DocstringCheckerReturnTest(CheckerTestCase): """This is a docstring. Returns: - int: One + One """ return None ''') with self.assertAddsMessages( - Message( - msg_id='redundant-returns-doc', - node=node)): + Message(msg_id='redundant-returns-doc', node=node)): + self.checker.visit_functiondef(node) + + def test_warns_google_redundant_rtype_doc(self): + node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + + Returns: + int: + """ + return None + ''') + with self.assertAddsMessages( + Message(msg_id='redundant-returns-doc', node=node)): self.checker.visit_functiondef(node) def test_warns_numpy_redundant_return_doc(self): @@ -412,9 +454,22 @@ class DocstringCheckerReturnTest(CheckerTestCase): return None ''') with self.assertAddsMessages( - Message( - msg_id='redundant-returns-doc', - node=node)): + Message(msg_id='redundant-returns-doc', node=node)): + self.checker.visit_functiondef(node) + + def test_warns_numpy_redundant_rtype_doc(self): + node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + + Returns + ------- + int + """ + return None + ''') + with self.assertAddsMessages( + Message(msg_id='redundant-returns-doc', node=node)): self.checker.visit_functiondef(node) def test_ignores_sphinx_redundant_return_doc_multiple_returns(self): @@ -494,9 +549,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): yield 1 ''') with self.assertAddsMessages( - Message( - msg_id='redundant-returns-doc', - node=node)): + Message(msg_id='redundant-returns-doc', node=node)): self.checker.visit_functiondef(node) def test_warns_numpy_redundant_return_doc_yield(self): @@ -512,9 +565,7 @@ class DocstringCheckerReturnTest(CheckerTestCase): yield 1 ''') with self.assertAddsMessages( - Message( - msg_id='redundant-returns-doc', - node=node)): + Message(msg_id='redundant-returns-doc', node=node)): self.checker.visit_functiondef(node) diff --git a/pylint/test/extensions/test_check_yields_docs.py b/pylint/test/extensions/test_check_yields_docs.py index 98fb82780..42455aa4b 100644 --- a/pylint/test/extensions/test_check_yields_docs.py +++ b/pylint/test/extensions/test_check_yields_docs.py @@ -36,9 +36,8 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node), + Message(msg_id='missing-yield-type-doc', node=node)): self.checker.visit_yield(yield_node) def test_ignores_unknown_style(self): @@ -61,9 +60,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-type-doc', node=node)): self.checker.visit_yield(yield_node) def test_warn_partial_sphinx_yields_type(self): @@ -77,9 +74,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node)): self.checker.visit_yield(yield_node) def test_warn_missing_sphinx_yields(self): @@ -94,9 +89,8 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node), + Message(msg_id='missing-yield-type-doc', node=node)): self.checker.visit_yield(yield_node) def test_warn_partial_google_yields(self): @@ -111,9 +105,22 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-type-doc', node=node)): + self.checker.visit_yield(yield_node) + + def test_warn_partial_google_yields_type(self): + node = astroid.extract_node(''' + def my_func(self): + """This is a docstring. + + Yields: + bool: + """ + yield False + ''') + yield_node = node.body[0] + with self.assertAddsMessages( + Message(msg_id='missing-yield-doc', node=node)): self.checker.visit_yield(yield_node) def test_warn_missing_google_yields(self): @@ -128,9 +135,8 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node), + Message(msg_id='missing-yield-type-doc', node=node)): self.checker.visit_yield(yield_node) def test_warn_missing_numpy_yields(self): @@ -147,9 +153,8 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node), + Message(msg_id='missing-yield-type-doc', node=node)): self.checker.visit_yield(yield_node) def test_find_sphinx_yields(self): @@ -286,9 +291,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node)): self.checker.visit_yield(yield_node) def test_warns_google_yield_list_of_custom_class_without_description(self): @@ -297,15 +300,13 @@ class DocstringCheckerYieldTest(CheckerTestCase): """This is a docstring. Yields: - list(:class:`mymodule.Class`) + list(:class:`mymodule.Class`): """ yield [mymodule.Class()] ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node)): self.checker.visit_yield(yield_node) def test_warns_numpy_yield_list_of_custom_class_without_description(self): @@ -321,9 +322,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): ''') yield_node = node.body[0] with self.assertAddsMessages( - Message( - msg_id='missing-yields-doc', - node=node)): + Message(msg_id='missing-yield-doc', node=node)): self.checker.visit_yield(yield_node) # No such thing as redundant yield documentation for sphinx because it @@ -377,9 +376,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): return 1 ''') with self.assertAddsMessages( - Message( - msg_id='redundant-yields-doc', - node=node)): + Message(msg_id='redundant-yields-doc', node=node)): self.checker.visit_functiondef(node) def test_warns_numpy_redundant_yield_doc_return(self): @@ -395,9 +392,7 @@ class DocstringCheckerYieldTest(CheckerTestCase): return 1 ''') with self.assertAddsMessages( - Message( - msg_id='redundant-yields-doc', - node=node)): + Message(msg_id='redundant-yields-doc', node=node)): self.checker.visit_functiondef(node) |