Skip to content

Commit

Permalink
Handle asterisks better in Sphinx and Google style docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielNoord committed Apr 7, 2022
1 parent ba08c9d commit 3b744d1
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 59 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ What's New in Pylint 2.13.6?
============================
Release date: TBA

* Asterisks are no longer required in Sphinx and Google style parameter documentation
for ``missing-param-doc`` and are parsed correctly.

Closes #5815
Closes #5406


What's New in Pylint 2.13.5?
Expand Down
6 changes: 6 additions & 0 deletions doc/whatsnew/2.13.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ Other Changes

Closes #5614

* Asterisks are no longer required in Sphinx and Google style parameter documentation
for ``missing-param-doc`` and are parsed correctly.

Closes #5815
Closes #5406

* Use the ``tomli`` package instead of ``toml`` to parse ``.toml`` files.

Closes #5885
Expand Down
7 changes: 5 additions & 2 deletions pylint/extensions/_check_docs_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ class SphinxDocstring(Docstring):
\s+
)?
((\\\*{{1,2}}\w+)|(\w+)) # Parameter name with potential asterisks
((\\\*{{0,2}}\w+)|(\w+)) # Parameter name with potential asterisks
\s* # whitespace
: # final colon
"""
Expand Down Expand Up @@ -469,7 +469,7 @@ class GoogleDocstring(Docstring):

re_param_line = re.compile(
rf"""
\s* (\*{{0,2}}\w+) # identifier potentially with asterisks
\s* ((?:\\?\*{{0,2}})?\w+) # identifier potentially with asterisks
\s* ( [(]
{re_multiple_type}
(?:,\s+optional)?
Expand Down Expand Up @@ -647,6 +647,9 @@ def match_param_docs(self):
continue

param_name = match.group(1)
# Remove escape characters necessary for asterisks
param_name = param_name.replace("\\", "")

param_type = match.group(2)
param_desc = match.group(3)

Expand Down
56 changes: 31 additions & 25 deletions pylint/extensions/docparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

"""Pylint plugin for checking in Sphinx, Google, or Numpy style docstrings."""
import re
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Optional, Set

import astroid
from astroid import nodes
Expand Down Expand Up @@ -369,33 +369,36 @@ def visit_yieldfrom(self, node: nodes.YieldFrom) -> None:

def _compare_missing_args(
self,
found_argument_names,
message_id,
not_needed_names,
expected_argument_names,
warning_node,
):
found_argument_names: Set[str],
message_id: str,
not_needed_names: Set[str],
expected_argument_names: Set[str],
warning_node: nodes.NodeNG,
) -> None:
"""Compare the found argument names with the expected ones and
generate a message if there are arguments missing.
:param found_argument_names: argument names found in the docstring
:type found_argument_names: set
:param message_id: pylint message id
:type message_id: str
:param not_needed_names: names that may be omitted
:type not_needed_names: set
:param expected_argument_names: Expected argument names
:type expected_argument_names: set
:param warning_node: The node to be analyzed
:type warning_node: :class:`astroid.scoped_nodes.Node`
"""
missing_argument_names = (
potential_missing_argument_names = (
expected_argument_names - found_argument_names
) - not_needed_names

# Handle variadic and keyword args without asterisks
missing_argument_names = set()
for name in potential_missing_argument_names:
if name.replace("*", "") in found_argument_names:
continue
missing_argument_names.add(name)

if missing_argument_names:
self.add_message(
message_id,
Expand All @@ -405,32 +408,35 @@ def _compare_missing_args(

def _compare_different_args(
self,
found_argument_names,
message_id,
not_needed_names,
expected_argument_names,
warning_node,
):
found_argument_names: Set[str],
message_id: str,
not_needed_names: Set[str],
expected_argument_names: Set[str],
warning_node: nodes.NodeNG,
) -> None:
"""Compare the found argument names with the expected ones and
generate a message if there are extra arguments found.
:param found_argument_names: argument names found in the docstring
:type found_argument_names: set
:param message_id: pylint message id
:type message_id: str
:param not_needed_names: names that may be omitted
:type not_needed_names: set
:param expected_argument_names: Expected argument names
:type expected_argument_names: set
:param warning_node: The node to be analyzed
:type warning_node: :class:`astroid.scoped_nodes.Node`
"""
# Handle variadic and keyword args without asterisks
modified_expected_argument_names: Set[str] = set()
for name in expected_argument_names:
if name.replace("*", "") in found_argument_names:
modified_expected_argument_names.add(name.replace("*", ""))
else:
modified_expected_argument_names.add(name)

differing_argument_names = (
(expected_argument_names ^ found_argument_names)
(modified_expected_argument_names ^ found_argument_names)
- not_needed_names
- expected_argument_names
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ def test_non_builtin_annotations_in_google_docstring(
"""


def test_non_builtin_annotations_for_returntype_in_google_docstring(bottomleft: Point, topright: Point) -> Point:
def test_non_builtin_annotations_for_returntype_in_google_docstring(
bottomleft: Point, topright: Point
) -> Point:
"""Example of a function with missing Google style parameter
documentation in the docstring.
Args:
Expand Down Expand Up @@ -324,6 +326,30 @@ def test_finds_kwargs_without_type_google(named_arg, **kwargs):
return named_arg


def test_finds_kwargs_without_asterisk_google(named_arg, **kwargs):
"""The docstring
Args:
named_arg (object): Returned
kwargs: Keyword arguments
Returns:
object or None: Maybe named_arg
"""
if kwargs:
return named_arg


def test_finds_escaped_args_google(value: int, *args: Any) -> None:
"""This is myfunc.
Args:
\\*args: this is args
value: this is value
"""
print(*args, value)


def test_finds_args_with_xref_type_google(named_arg, **kwargs):
"""The docstring
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
missing-param-doc:24:0:24:48:test_missing_func_params_in_google_docstring:"""y"" missing in parameter documentation":UNDEFINED
missing-type-doc:24:0:24:48:test_missing_func_params_in_google_docstring:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-type-doc:80:0:80:73:test_missing_func_params_with_partial_annotations_in_google_docstring:"""x"" missing in parameter type documentation":UNDEFINED
differing-param-doc:129:0:129:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""these"" differing in parameter documentation":UNDEFINED
differing-type-doc:129:0:129:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""these"" differing in parameter type documentation":UNDEFINED
missing-param-doc:129:0:129:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""that"" missing in parameter documentation":UNDEFINED
missing-type-doc:129:0:129:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""that"" missing in parameter type documentation":UNDEFINED
missing-param-doc:146:4:146:54:Foo.test_missing_method_params_in_google_docstring:"""y"" missing in parameter documentation":UNDEFINED
missing-type-doc:146:4:146:54:Foo.test_missing_method_params_in_google_docstring:"""x, y"" missing in parameter type documentation":UNDEFINED
differing-param-doc:177:0:177:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg1, zarg1"" differing in parameter documentation":UNDEFINED
differing-type-doc:177:0:177:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg1, zarg1"" differing in parameter type documentation":UNDEFINED
missing-param-doc:177:0:177:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg, zarg"" missing in parameter documentation":UNDEFINED
missing-type-doc:177:0:177:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg, zarg"" missing in parameter type documentation":UNDEFINED
differing-param-doc:192:0:192:58:test_wrong_name_of_func_params_in_google_docstring_two:"""yarg1"" differing in parameter documentation":UNDEFINED
differing-type-doc:192:0:192:58:test_wrong_name_of_func_params_in_google_docstring_two:"""yarg1"" differing in parameter type documentation":UNDEFINED
missing-param-doc:219:0:219:14:ClassFoo:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:219:0:219:14:ClassFoo:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:237:4:237:16:ClassFoo.__init__:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:237:4:237:16:ClassFoo.__init__:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:249:0:249:14:ClassFoo:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:249:0:249:14:ClassFoo:"""x, y"" missing in parameter type documentation":UNDEFINED
multiple-constructor-doc:249:0:249:14:ClassFoo:"""ClassFoo"" has constructor parameters documented in class and __init__":UNDEFINED
missing-param-doc:263:4:263:16:ClassFoo.__init__:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:263:4:263:16:ClassFoo.__init__:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:273:0:273:34:test_warns_missing_args_google:"""*args"" missing in parameter documentation":UNDEFINED
missing-param-doc:286:0:286:36:test_warns_missing_kwargs_google:"""**kwargs"" missing in parameter documentation":UNDEFINED
differing-param-doc:131:0:131:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""these"" differing in parameter documentation":UNDEFINED
differing-type-doc:131:0:131:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""these"" differing in parameter type documentation":UNDEFINED
missing-param-doc:131:0:131:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""that"" missing in parameter documentation":UNDEFINED
missing-type-doc:131:0:131:65:test_func_params_and_wrong_keyword_params_in_google_docstring:"""that"" missing in parameter type documentation":UNDEFINED
missing-param-doc:148:4:148:54:Foo.test_missing_method_params_in_google_docstring:"""y"" missing in parameter documentation":UNDEFINED
missing-type-doc:148:4:148:54:Foo.test_missing_method_params_in_google_docstring:"""x, y"" missing in parameter type documentation":UNDEFINED
differing-param-doc:179:0:179:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg1, zarg1"" differing in parameter documentation":UNDEFINED
differing-type-doc:179:0:179:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg1, zarg1"" differing in parameter type documentation":UNDEFINED
missing-param-doc:179:0:179:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg, zarg"" missing in parameter documentation":UNDEFINED
missing-type-doc:179:0:179:58:test_wrong_name_of_func_params_in_google_docstring_one:"""xarg, zarg"" missing in parameter type documentation":UNDEFINED
differing-param-doc:194:0:194:58:test_wrong_name_of_func_params_in_google_docstring_two:"""yarg1"" differing in parameter documentation":UNDEFINED
differing-type-doc:194:0:194:58:test_wrong_name_of_func_params_in_google_docstring_two:"""yarg1"" differing in parameter type documentation":UNDEFINED
missing-param-doc:221:0:221:14:ClassFoo:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:221:0:221:14:ClassFoo:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:239:4:239:16:ClassFoo.__init__:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:239:4:239:16:ClassFoo.__init__:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:251:0:251:14:ClassFoo:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:251:0:251:14:ClassFoo:"""x, y"" missing in parameter type documentation":UNDEFINED
multiple-constructor-doc:251:0:251:14:ClassFoo:"""ClassFoo"" has constructor parameters documented in class and __init__":UNDEFINED
missing-param-doc:265:4:265:16:ClassFoo.__init__:"""x"" missing in parameter documentation":UNDEFINED
missing-type-doc:265:4:265:16:ClassFoo.__init__:"""x, y"" missing in parameter type documentation":UNDEFINED
missing-param-doc:275:0:275:34:test_warns_missing_args_google:"""*args"" missing in parameter documentation":UNDEFINED
missing-param-doc:288:0:288:36:test_warns_missing_kwargs_google:"""**kwargs"" missing in parameter documentation":UNDEFINED
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,25 @@ def test_finds_kwargs_without_type_numpy(named_arg, **kwargs):
return named_arg


def test_finds_kwargs_without_asterisk_numpy(named_arg, **kwargs):
"""The docstring
Args
----
named_arg : object
Returned
kwargs :
Keyword arguments
Returns
-------
object or None
Maybe named_arg
"""
if kwargs:
return named_arg


def my_func(
named_arg_one,
named_arg_two,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,42 @@ def test_finds_kwargs_without_type_sphinx( # [inconsistent-return-statements]
return named_arg


def test_finds_args_without_type_sphinx( # [inconsistent-return-statements]
named_arg, *args
):
r"""The Sphinx docstring
We can leave the asterisk out.
:param named_arg: Returned
:type named_arg: object
:param args: Optional arguments
:returns: Maybe named_arg
:rtype: object or None
"""
if args:
return named_arg


def test_finds_kwargs_without_type_sphinx( # [inconsistent-return-statements]
named_arg, **kwargs
):
r"""The Sphinx docstring
We can leave the asterisk out.
:param named_arg: Returned
:type named_arg: object
:param kwargs: Keyword arguments
:returns: Maybe named_arg
:rtype: object or None
"""
if kwargs:
return named_arg


class Foo:
"""test_finds_missing_raises_from_setter_sphinx
Example of a setter having missing raises documentation in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ inconsistent-return-statements:201:0:201:41:test_finds_kwargs_without_type_sphin
missing-param-doc:201:0:201:41:test_finds_kwargs_without_type_sphinx:"""**kwargs"" missing in parameter documentation":UNDEFINED
inconsistent-return-statements:218:0:218:39:test_finds_args_without_type_sphinx:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
inconsistent-return-statements:237:0:237:41:test_finds_kwargs_without_type_sphinx:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
missing-raises-doc:263:4:263:11:Foo.foo:"""AttributeError"" not documented as being raised":UNDEFINED
unreachable:289:8:289:17:Foo.foo:Unreachable code:UNDEFINED
missing-param-doc:292:4:292:11:Foo.foo:"""value"" missing in parameter documentation":UNDEFINED
missing-raises-doc:292:4:292:11:Foo.foo:"""AttributeError"" not documented as being raised":UNDEFINED
missing-type-doc:292:4:292:11:Foo.foo:"""value"" missing in parameter type documentation":UNDEFINED
unreachable:328:8:328:17:Foo.foo:Unreachable code:UNDEFINED
useless-param-doc:332:4:332:55:Foo.test_useless_docs_ignored_argument_names_sphinx:"""_, _ignored"" useless ignored parameter documentation":UNDEFINED
useless-type-doc:332:4:332:55:Foo.test_useless_docs_ignored_argument_names_sphinx:"""_"" useless ignored parameter type documentation":UNDEFINED
inconsistent-return-statements:256:0:256:39:test_finds_args_without_type_sphinx:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
inconsistent-return-statements:274:0:274:41:test_finds_kwargs_without_type_sphinx:Either all return statements in a function should return an expression, or none of them should.:UNDEFINED
missing-raises-doc:299:4:299:11:Foo.foo:"""AttributeError"" not documented as being raised":UNDEFINED
unreachable:325:8:325:17:Foo.foo:Unreachable code:UNDEFINED
missing-param-doc:328:4:328:11:Foo.foo:"""value"" missing in parameter documentation":UNDEFINED
missing-raises-doc:328:4:328:11:Foo.foo:"""AttributeError"" not documented as being raised":UNDEFINED
missing-type-doc:328:4:328:11:Foo.foo:"""value"" missing in parameter type documentation":UNDEFINED
unreachable:364:8:364:17:Foo.foo:Unreachable code:UNDEFINED
useless-param-doc:368:4:368:55:Foo.test_useless_docs_ignored_argument_names_sphinx:"""_, _ignored"" useless ignored parameter documentation":UNDEFINED
useless-type-doc:368:4:368:55:Foo.test_useless_docs_ignored_argument_names_sphinx:"""_"" useless ignored parameter type documentation":UNDEFINED

0 comments on commit 3b744d1

Please sign in to comment.