summaryrefslogtreecommitdiff
path: root/pylint/checkers/design_analysis.py
diff options
context:
space:
mode:
authorRebecca Turner <rbt@sent.as>2021-07-28 15:45:19 -0400
committerGitHub <noreply@github.com>2021-07-28 21:45:19 +0200
commit24d03e9410520849494db1bde84334769217b3d4 (patch)
tree7b9dc434a13cf864044bb7716971136a07c05ab8 /pylint/checkers/design_analysis.py
parent67f40566c11168616079ece444fc558a81d336d2 (diff)
downloadpylint-git-24d03e9410520849494db1bde84334769217b3d4.tar.gz
Add ignored-parents option to design checker (#4758)
* Add ignored-parents option to design checker This allows users to specify classes to ignore while counting parent classes. Partially closes #3057 Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Diffstat (limited to 'pylint/checkers/design_analysis.py')
-rw-r--r--pylint/checkers/design_analysis.py46
1 files changed, 42 insertions, 4 deletions
diff --git a/pylint/checkers/design_analysis.py b/pylint/checkers/design_analysis.py
index a02969410..26cd77cc5 100644
--- a/pylint/checkers/design_analysis.py
+++ b/pylint/checkers/design_analysis.py
@@ -25,6 +25,7 @@
import re
from collections import defaultdict
+from typing import FrozenSet, List, Set, cast
import astroid
from astroid import nodes
@@ -237,6 +238,35 @@ def _count_methods_in_class(node):
return all_methods
+def _get_parents(
+ node: nodes.ClassDef, ignored_parents: FrozenSet[str]
+) -> Set[nodes.ClassDef]:
+ r"""Get parents of ``node``, excluding ancestors of ``ignored_parents``.
+
+ If we have the following inheritance diagram:
+
+ F
+ /
+ D E
+ \/
+ B C
+ \/
+ A # class A(B, C): ...
+
+ And ``ignored_parents`` is ``{"E"}``, then this function will return
+ ``{A, B, C, D}`` -- both ``E`` and its ancestors are excluded.
+ """
+ parents: Set[nodes.ClassDef] = set()
+ to_explore = cast(List[nodes.ClassDef], list(node.ancestors(recurs=False)))
+ while to_explore:
+ parent = to_explore.pop()
+ if parent.qname() in ignored_parents:
+ continue
+ parents.add(parent)
+ to_explore.extend(parent.ancestors(recurs=False)) # type: ignore
+ return parents
+
+
class MisdesignChecker(BaseChecker):
"""checks for sign of poor/misdesign:
* number of methods, attributes, local variables...
@@ -308,6 +338,15 @@ class MisdesignChecker(BaseChecker):
},
),
(
+ "ignored-parents",
+ {
+ "default": (),
+ "type": "csv",
+ "metavar": "<comma separated list of class names>",
+ "help": "List of qualified class names to ignore when countint class parents (see R0901)",
+ },
+ ),
+ (
"max-attributes",
{
"default": 7,
@@ -379,11 +418,10 @@ class MisdesignChecker(BaseChecker):
)
def visit_classdef(self, node: nodes.ClassDef):
"""check size of inheritance hierarchy and number of instance attributes"""
- nb_parents = sum(
- 1
- for ancestor in node.ancestors()
- if ancestor.qname() not in STDLIB_CLASSES_IGNORE_ANCESTOR
+ parents = _get_parents(
+ node, STDLIB_CLASSES_IGNORE_ANCESTOR.union(self.config.ignored_parents)
)
+ nb_parents = len(parents)
if nb_parents > self.config.max_parents:
self.add_message(
"too-many-ancestors",