Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle asterisks better in Sphinx and Google style docstrings #6212

Merged
merged 1 commit into from
Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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