Skip to content
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
16 changes: 12 additions & 4 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,24 @@
Pylint's ChangeLog
------------------

What's New in Pylint 2.8.2?
What's New in Pylint 2.8.3?
===========================
Release date: 2021-04-26

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

* Fix false-positive ``too-many-ancestors`` when inheriting from builtin classes,
especially from the ``collections.abc`` module

Closes #4166
Closes #4415


What's New in Pylint 2.8.2?
===========================
Release date: 2021-04-26

* Keep ``__pkginfo__.numversion`` a tuple to avoid breaking pylint-django.

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

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

* Add numversion back (temporarily) in ``__pkginfo__`` because it broke Pylama and revert the unnecessary
``pylint.version`` breaking change.

Expand Down
3 changes: 3 additions & 0 deletions doc/whatsnew/2.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ Other Changes
=============

* Pylint's tags are now the standard form ``vX.Y.Z`` and not ``pylint-X.Y.Z`` anymore.

* Fix false-positive ``too-many-ancestors`` when inheriting from builtin classes,
especially from the ``collections.abc`` module
90 changes: 88 additions & 2 deletions pylint/checkers/design_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from collections import defaultdict

import astroid
from astroid import nodes

from pylint import utils
from pylint.checkers import BaseChecker
Expand Down Expand Up @@ -97,6 +98,87 @@
TYPING_NAMEDTUPLE = "typing.NamedTuple"
TYPING_TYPEDDICT = "typing.TypedDict"

# Set of stdlib classes to ignore when calculating number of ancestors
STDLIB_CLASSES_IGNORE_ANCESTOR = frozenset(
(
"builtins.object",
"builtins.tuple",
"builtins.dict",
"builtins.list",
"builtins.set",
"bulitins.frozenset",
"collections.ChainMap",
"collections.Counter",
"collections.OrderedDict",
"collections.UserDict",
"collections.UserList",
"collections.UserString",
"collections.defaultdict",
"collections.deque",
"collections.namedtuple",
"_collections_abc.Awaitable",
"_collections_abc.Coroutine",
"_collections_abc.AsyncIterable",
"_collections_abc.AsyncIterator",
"_collections_abc.AsyncGenerator",
"_collections_abc.Hashable",
"_collections_abc.Iterable",
"_collections_abc.Iterator",
"_collections_abc.Generator",
"_collections_abc.Reversible",
"_collections_abc.Sized",
"_collections_abc.Container",
"_collections_abc.Collection",
"_collections_abc.Set",
"_collections_abc.MutableSet",
"_collections_abc.Mapping",
"_collections_abc.MutableMapping",
"_collections_abc.MappingView",
"_collections_abc.KeysView",
"_collections_abc.ItemsView",
"_collections_abc.ValuesView",
"_collections_abc.Sequence",
"_collections_abc.MutableSequence",
"_collections_abc.ByteString",
"typing.Tuple",
"typing.List",
"typing.Dict",
"typing.Set",
"typing.FrozenSet",
"typing.Deque",
"typing.DefaultDict",
"typing.OrderedDict",
"typing.Counter",
"typing.ChainMap",
"typing.Awaitable",
"typing.Coroutine",
"typing.AsyncIterable",
"typing.AsyncIterator",
"typing.AsyncGenerator",
"typing.Iterable",
"typing.Iterator",
"typing.Generator",
"typing.Reversible",
"typing.Container",
"typing.Collection",
"typing.AbstractSet",
"typing.MutableSet",
"typing.Mapping",
"typing.MutableMapping",
"typing.Sequence",
"typing.MutableSequence",
"typing.ByteString",
"typing.MappingView",
"typing.KeysView",
"typing.ItemsView",
"typing.ValuesView",
"typing.ContextManager",
"typing.AsyncContextManger",
"typing.Hashable",
"typing.Sized",
)
)


def _is_exempt_from_public_methods(node: astroid.ClassDef) -> bool:
"""Check if a class is exempt from too-few-public-methods"""
Expand Down Expand Up @@ -294,9 +376,13 @@ def _ignored_argument_names(self):
"too-few-public-methods",
"too-many-public-methods",
)
def visit_classdef(self, node):
def visit_classdef(self, node: nodes.ClassDef):
"""check size of inheritance hierarchy and number of instance attributes"""
nb_parents = len(list(node.ancestors()))
nb_parents = sum(
1
for ancestor in node.ancestors()
if ancestor.qname() not in STDLIB_CLASSES_IGNORE_ANCESTOR
)
if nb_parents > self.config.max_parents:
self.add_message(
"too-many-ancestors",
Expand Down
21 changes: 21 additions & 0 deletions tests/functional/t/too/too_many_ancestors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# pylint: disable=missing-docstring, too-few-public-methods, useless-object-inheritance
from collections.abc import MutableSequence

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

class Jjjj(Iiii): # [too-many-ancestors]
pass


# https://github.com/PyCQA/pylint/issues/4166
# https://github.com/PyCQA/pylint/issues/4415
class ItemSequence(MutableSequence):
"""Minimal MutableSequence."""
def __getitem__(self, key):
return key

def __setitem__(self, key, value):
_ = key, value

def __delitem__(self, key):
_ = key

def insert(self, index, value):
_ = index, value

def __len__(self):
return 1
4 changes: 2 additions & 2 deletions tests/functional/t/too/too_many_ancestors.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
too-many-ancestors:20:0:Iiii:Too many ancestors (9/7)
too-many-ancestors:23:0:Jjjj:Too many ancestors (10/7)
too-many-ancestors:21:0:Iiii:Too many ancestors (8/7)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually fixed that test case too, the correct number IS 8 !

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends if you con't object, but it's certainly not false.

too-many-ancestors:24:0:Jjjj:Too many ancestors (9/7)