Skip to content

Commit 24b5159

Browse files
cdce8pPierre-Sassoulas
authored andcommitted
Fix too-many-ancestors
1 parent 9228c16 commit 24b5159

File tree

5 files changed

+126
-8
lines changed

5 files changed

+126
-8
lines changed

ChangeLog

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
Pylint's ChangeLog
33
------------------
44

5-
What's New in Pylint 2.8.2?
5+
What's New in Pylint 2.8.3?
66
===========================
77
Release date: 2021-04-26
88

99
..
1010
Put new features and bugfixes here and also in 'doc/whatsnew/2.9.rst'
1111

12+
* Fix false-positive ``too-many-ancestors`` when inheriting from builtin classes,
13+
especially from the ``collections.abc`` module
14+
15+
Closes #4166
16+
Closes #4415
17+
18+
19+
What's New in Pylint 2.8.2?
20+
===========================
21+
Release date: 2021-04-26
22+
1223
* Keep ``__pkginfo__.numversion`` a tuple to avoid breaking pylint-django.
1324

1425
Closes #4405
@@ -22,9 +33,6 @@ What's New in Pylint 2.8.1?
2233
===========================
2334
Release date: 2021-04-25
2435

25-
..
26-
Put new features and bugfixes here and also in 'doc/whatsnew/2.9.rst'
27-
2836
* Add numversion back (temporarily) in ``__pkginfo__`` because it broke Pylama and revert the unnecessary
2937
``pylint.version`` breaking change.
3038

doc/whatsnew/2.9.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ Other Changes
1616
=============
1717

1818
* Pylint's tags are now the standard form ``vX.Y.Z`` and not ``pylint-X.Y.Z`` anymore.
19+
20+
* Fix false-positive ``too-many-ancestors`` when inheriting from builtin classes,
21+
especially from the ``collections.abc`` module

pylint/checkers/design_analysis.py

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from collections import defaultdict
2727

2828
import astroid
29+
from astroid import nodes
2930

3031
from pylint import utils
3132
from pylint.checkers import BaseChecker
@@ -97,6 +98,87 @@
9798
TYPING_NAMEDTUPLE = "typing.NamedTuple"
9899
TYPING_TYPEDDICT = "typing.TypedDict"
99100

101+
# Set of stdlib classes to ignore when calculating number of ancestors
102+
STDLIB_CLASSES_IGNORE_ANCESTOR = frozenset(
103+
(
104+
"builtins.object",
105+
"builtins.tuple",
106+
"builtins.dict",
107+
"builtins.list",
108+
"builtins.set",
109+
"bulitins.frozenset",
110+
"collections.ChainMap",
111+
"collections.Counter",
112+
"collections.OrderedDict",
113+
"collections.UserDict",
114+
"collections.UserList",
115+
"collections.UserString",
116+
"collections.defaultdict",
117+
"collections.deque",
118+
"collections.namedtuple",
119+
"_collections_abc.Awaitable",
120+
"_collections_abc.Coroutine",
121+
"_collections_abc.AsyncIterable",
122+
"_collections_abc.AsyncIterator",
123+
"_collections_abc.AsyncGenerator",
124+
"_collections_abc.Hashable",
125+
"_collections_abc.Iterable",
126+
"_collections_abc.Iterator",
127+
"_collections_abc.Generator",
128+
"_collections_abc.Reversible",
129+
"_collections_abc.Sized",
130+
"_collections_abc.Container",
131+
"_collections_abc.Collection",
132+
"_collections_abc.Set",
133+
"_collections_abc.MutableSet",
134+
"_collections_abc.Mapping",
135+
"_collections_abc.MutableMapping",
136+
"_collections_abc.MappingView",
137+
"_collections_abc.KeysView",
138+
"_collections_abc.ItemsView",
139+
"_collections_abc.ValuesView",
140+
"_collections_abc.Sequence",
141+
"_collections_abc.MutableSequence",
142+
"_collections_abc.ByteString",
143+
"typing.Tuple",
144+
"typing.List",
145+
"typing.Dict",
146+
"typing.Set",
147+
"typing.FrozenSet",
148+
"typing.Deque",
149+
"typing.DefaultDict",
150+
"typing.OrderedDict",
151+
"typing.Counter",
152+
"typing.ChainMap",
153+
"typing.Awaitable",
154+
"typing.Coroutine",
155+
"typing.AsyncIterable",
156+
"typing.AsyncIterator",
157+
"typing.AsyncGenerator",
158+
"typing.Iterable",
159+
"typing.Iterator",
160+
"typing.Generator",
161+
"typing.Reversible",
162+
"typing.Container",
163+
"typing.Collection",
164+
"typing.AbstractSet",
165+
"typing.MutableSet",
166+
"typing.Mapping",
167+
"typing.MutableMapping",
168+
"typing.Sequence",
169+
"typing.MutableSequence",
170+
"typing.ByteString",
171+
"typing.MappingView",
172+
"typing.KeysView",
173+
"typing.ItemsView",
174+
"typing.ValuesView",
175+
"typing.ContextManager",
176+
"typing.AsyncContextManger",
177+
"typing.Hashable",
178+
"typing.Sized",
179+
)
180+
)
181+
100182

101183
def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
102184
"""Check if a class is exempt from too-few-public-methods"""
@@ -294,9 +376,13 @@ def _ignored_argument_names(self):
294376
"too-few-public-methods",
295377
"too-many-public-methods",
296378
)
297-
def visit_classdef(self, node):
379+
def visit_classdef(self, node: nodes.ClassDef):
298380
"""check size of inheritance hierarchy and number of instance attributes"""
299-
nb_parents = len(list(node.ancestors()))
381+
nb_parents = sum(
382+
1
383+
for ancestor in node.ancestors()
384+
if ancestor.qname() not in STDLIB_CLASSES_IGNORE_ANCESTOR
385+
)
300386
if nb_parents > self.config.max_parents:
301387
self.add_message(
302388
"too-many-ancestors",

tests/functional/t/too/too_many_ancestors.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# pylint: disable=missing-docstring, too-few-public-methods, useless-object-inheritance
2+
from collections.abc import MutableSequence
23

34
class Aaaa(object):
45
pass
@@ -22,3 +23,23 @@ class Iiii(Aaaa, Bbbb, Cccc, Dddd, Eeee, Ffff, Gggg, Hhhh): # [too-many-ancestor
2223

2324
class Jjjj(Iiii): # [too-many-ancestors]
2425
pass
26+
27+
28+
# https://github.com/PyCQA/pylint/issues/4166
29+
# https://github.com/PyCQA/pylint/issues/4415
30+
class ItemSequence(MutableSequence):
31+
"""Minimal MutableSequence."""
32+
def __getitem__(self, key):
33+
return key
34+
35+
def __setitem__(self, key, value):
36+
_ = key, value
37+
38+
def __delitem__(self, key):
39+
_ = key
40+
41+
def insert(self, index, value):
42+
_ = index, value
43+
44+
def __len__(self):
45+
return 1
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
too-many-ancestors:20:0:Iiii:Too many ancestors (9/7)
2-
too-many-ancestors:23:0:Jjjj:Too many ancestors (10/7)
1+
too-many-ancestors:21:0:Iiii:Too many ancestors (8/7)
2+
too-many-ancestors:24:0:Jjjj:Too many ancestors (9/7)

0 commit comments

Comments
 (0)