in tools/pylint-extensions/azure-pylint-guidelines-checker/pylint_guidelines_checker.py [0:0]
def check_parameters(self, node):
"""Parse the docstring for any params and types
and compares it to the function's parameters.
Throws a pylint error if...
1. Missing param in docstring.
2. Missing a param type in the docstring.
3. Missing a return doc in the docstring when a function returns something.
4. Missing an rtype in the docstring when a function returns something.
5. Extra params in docstring that aren't function parameters. Change to keywords.
6. Docstring has a keyword that isn't a keyword-only argument in the function signature.
:param node: ast.ClassDef or ast.FunctionDef
:return: None
"""
arg_names = []
method_keyword_only_args = []
vararg_name = None
# specific case for constructor where docstring found in class def
if isinstance(node, astroid.ClassDef):
for constructor in node.body:
if (
isinstance(constructor, astroid.FunctionDef)
and constructor.name == "__init__"
):
arg_names = [arg.name for arg in constructor.args.args]
method_keyword_only_args = [
arg.name for arg in constructor.args.kwonlyargs
]
vararg_name = node.args.vararg
break
if isinstance(node, astroid.FunctionDef):
arg_names = [arg.name for arg in node.args.args]
method_keyword_only_args = [arg.name for arg in node.args.kwonlyargs]
vararg_name = node.args.vararg
try:
# check for incorrect type :class to prevent splitting
docstring = node.doc_node.value.replace(":class:", "CLASS ")
# not every method will have a docstring so don't crash here, just return
docstring = docstring.split(":")
except AttributeError:
return
# If there is a vararg, treat it as a param
if vararg_name:
arg_names.append(vararg_name)
docparams = {}
docstring_keyword_args = {}
for idx, line in enumerate(docstring):
# check for keyword args in docstring
docstring_keyword_args.update(
self._find_keyword(line, docstring, idx, docstring_keyword_args)
)
# check for params in docstring
docparams.update(self._find_param(line, docstring, idx, docparams))
# check that all params are documented
missing_params = []
for param in arg_names:
if param == "self" or param == "cls":
continue
if param not in docparams:
missing_params.append(param)
# check that all keyword-only args are documented
missing_kwonly_args = list(
set(docstring_keyword_args) ^ set(method_keyword_only_args)
)
if missing_params:
self.add_message(
msgid="docstring-missing-param",
args=(", ".join(missing_params)),
node=node,
confidence=None,
)
if missing_kwonly_args:
self.add_message(
msgid="docstring-keyword-should-match-keyword-only",
args=(", ".join(missing_kwonly_args)),
node=node,
confidence=None,
)
# check that all types are formatted correctly
add_keyword_type_warnings = [
keyword
for keyword, doc_type in docstring_keyword_args.items()
if doc_type and "CLASS" in doc_type
]
if len(add_keyword_type_warnings) > 0:
self.add_message(
msgid="docstring-type-do-not-use-class",
args=(", ".join(add_keyword_type_warnings)),
node=node,
confidence=None,
)
add_docparams_type_warnings = [
param
for param, doc_type in docparams.items()
if doc_type and "CLASS" in doc_type
]
if len(add_docparams_type_warnings) > 0:
self.add_message(
msgid="docstring-type-do-not-use-class",
args=(", ".join(add_docparams_type_warnings)),
node=node,
confidence=None,
)
# check if we have a type for each param and check if documented params that should be keywords
missing_types = []
should_be_keywords = []
for param in docparams:
if docparams[param] is None:
missing_types.append(param)
if param not in arg_names:
should_be_keywords.append(param)
if missing_types:
self.add_message(
msgid="docstring-missing-type",
args=(", ".join(missing_types)),
node=node,
confidence=None,
)
if should_be_keywords:
self.add_message(
msgid="docstring-should-be-keyword",
args=(", ".join(should_be_keywords)),
node=node,
confidence=None,
)