summaryrefslogtreecommitdiff
path: root/pylint
diff options
context:
space:
mode:
authorkasium <15907922+kasium@users.noreply.github.com>2021-12-15 00:19:44 +0100
committerGitHub <noreply@github.com>2021-12-15 00:19:44 +0100
commit26c9042825a6549c9f889b2cd17b5e08ba784ab2 (patch)
tree27973a03c916fc438402e0631f5d52be1ef49ae5 /pylint
parentaf8cc2e75018d34fbbed08d4bfa3380e80f89b4d (diff)
downloadpylint-git-26c9042825a6549c9f889b2cd17b5e08ba784ab2.tar.gz
Enable missing-raises-doc to understand class hierarchies (#5278)
Before, missing-raises-doc could not understand class hierarchies but just compared names. So, when a method documented a raise of a parent class, but a child exception was raised, the check failed. With this change, if the name compare doesn't help, the exception class hierarchy is checked. For that, possible_exc_types was changed to return classes instead of names Resolved #4955 Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com> Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
Diffstat (limited to 'pylint')
-rw-r--r--pylint/extensions/_check_docs_utils.py29
-rw-r--r--pylint/extensions/docparams.py15
2 files changed, 28 insertions, 16 deletions
diff --git a/pylint/extensions/_check_docs_utils.py b/pylint/extensions/_check_docs_utils.py
index bfab0e1e4..2a26fe0e2 100644
--- a/pylint/extensions/_check_docs_utils.py
+++ b/pylint/extensions/_check_docs_utils.py
@@ -120,7 +120,7 @@ def _split_multiple_exc_types(target: str) -> List[str]:
return re.split(delimiters, target)
-def possible_exc_types(node):
+def possible_exc_types(node: nodes.NodeNG) -> Set[nodes.ClassDef]:
"""
Gets all of the possible raised exception types for the given raise node.
@@ -130,28 +130,30 @@ def possible_exc_types(node):
:param node: The raise node to find exception types for.
- :type node: nodes.NodeNG
:returns: A list of exception types possibly raised by :param:`node`.
- :rtype: set(str)
"""
excs = []
if isinstance(node.exc, nodes.Name):
inferred = utils.safe_infer(node.exc)
if inferred:
- excs = [inferred.name]
+ excs = [inferred]
elif node.exc is None:
handler = node.parent
while handler and not isinstance(handler, nodes.ExceptHandler):
handler = handler.parent
if handler and handler.type:
- inferred_excs = astroid.unpack_infer(handler.type)
- excs = (exc.name for exc in inferred_excs if exc is not astroid.Uninferable)
+ try:
+ for exc in astroid.unpack_infer(handler.type):
+ if exc is not astroid.Uninferable:
+ excs.append(exc)
+ except astroid.InferenceError:
+ pass
else:
target = _get_raise_target(node)
if isinstance(target, nodes.ClassDef):
- excs = [target.name]
+ excs = [target]
elif isinstance(target, nodes.FunctionDef):
for ret in target.nodes_of_class(nodes.Return):
if ret.frame() != target:
@@ -159,15 +161,14 @@ def possible_exc_types(node):
continue
val = utils.safe_infer(ret.value)
- if (
- val
- and isinstance(val, (astroid.Instance, nodes.ClassDef))
- and utils.inherit_from_std_ex(val)
- ):
- excs.append(val.name)
+ if val and utils.inherit_from_std_ex(val):
+ if isinstance(val, nodes.ClassDef):
+ excs.append(val)
+ elif isinstance(val, astroid.Instance):
+ excs.append(val.getattr("__class__")[0])
try:
- return {exc for exc in excs if not utils.node_ignores_exception(node, exc)}
+ return {exc for exc in excs if not utils.node_ignores_exception(node, exc.name)}
except astroid.InferenceError:
return set()
diff --git a/pylint/extensions/docparams.py b/pylint/extensions/docparams.py
index e7b293988..a0c66f16c 100644
--- a/pylint/extensions/docparams.py
+++ b/pylint/extensions/docparams.py
@@ -309,14 +309,25 @@ class DocstringParameterChecker(BaseChecker):
doc = utils.docstringify(func_node.doc, self.config.default_docstring_type)
if not doc.matching_sections():
if doc.doc:
- self._handle_no_raise_doc(expected_excs, func_node)
+ missing = {exc.name for exc in expected_excs}
+ self._handle_no_raise_doc(missing, func_node)
return
found_excs_full_names = doc.exceptions()
# Extract just the class name, e.g. "error" from "re.error"
found_excs_class_names = {exc.split(".")[-1] for exc in found_excs_full_names}
- missing_excs = expected_excs - found_excs_class_names
+
+ missing_excs = set()
+ for expected in expected_excs:
+ for found_exc in found_excs_class_names:
+ if found_exc == expected.name:
+ break
+ if any(found_exc == ancestor.name for ancestor in expected.ancestors()):
+ break
+ else:
+ missing_excs.add(expected.name)
+
self._add_raise_message(missing_excs, func_node)
def visit_return(self, node: nodes.Return) -> None: