Skip to content

Commit be70fe5

Browse files
committed
Fix a false negative for abstract-method when an abstract class is the direct ancestor of a derived class.
Closes pylint-dev#9979
1 parent c0ecd70 commit be70fe5

File tree

6 files changed

+26
-4
lines changed

6 files changed

+26
-4
lines changed

doc/data/messages/a/abstract-method/bad/abstract_method.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import abc
22

33

4-
class WildAnimal:
4+
class WildAnimal(abc.ABC):
55
@abc.abstractmethod
66
def make_sound(self):
77
pass

doc/data/messages/a/abstract-method/good/abstract_method.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import abc
22

33

4-
class WildAnimal:
4+
class WildAnimal(abc.ABC):
55
@abc.abstractmethod
66
def make_sound(self):
77
pass
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a false negative for ``abstract-method`` when an abstract class is the direct ancestor of a derived class.
2+
3+
Closes #9979

pylint/checkers/utils.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1166,8 +1166,11 @@ def class_is_abstract(node: nodes.ClassDef) -> bool:
11661166
if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES:
11671167
return True
11681168

1169-
for ancestor in node.ancestors():
1170-
if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES:
1169+
for ancestor in node.ancestors(recurs=False):
1170+
if (
1171+
ancestor.name == "ABC"
1172+
and ancestor.root().name in ABC_MODULES
1173+
):
11711174
# abc.ABC inheritance
11721175
return True
11731176

tests/functional/a/abstract/abstract_method.py

+15
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,18 @@ class GoodComplexMRO(Container, Iterator, Sizable, Hashable):
105105
# +1: [abstract-method, abstract-method, abstract-method]
106106
class BadComplexMro(Container, Iterator, AbstractSizable):
107107
pass
108+
109+
110+
class Base(abc.ABC):
111+
@abc.abstractmethod
112+
def do_something(self):
113+
pass
114+
115+
116+
# +1: [abstract-method]
117+
class Derived(Base):
118+
""" Test that a class derived from an abstract class correctly triggers
119+
`abstract-method`.
120+
"""
121+
def do_something_else(self):
122+
pass

tests/functional/a/abstract/abstract_method.txt

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ abstract-method:87:0:87:14:Iterator:Method '__len__' is abstract in class 'Struc
1414
abstract-method:106:0:106:19:BadComplexMro:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'BadComplexMro':INFERENCE
1515
abstract-method:106:0:106:19:BadComplexMro:Method '__len__' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
1616
abstract-method:106:0:106:19:BadComplexMro:Method 'length' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
17+
abstract-method:117:0:117:13:Derived:Method 'do_something' is abstract in class 'Base' but is not overridden in child class 'Derived':INFERENCE

0 commit comments

Comments
 (0)