Skip to content

Warn on incomplete concrete classes that inherit from abstract classes #7955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
41fd241
work and test class
sshane Dec 17, 2022
218a0a2
detect attr
sshane Dec 17, 2022
a73b51f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 17, 2022
855c354
fix with safe_infer :)
sshane Dec 31, 2022
162ca0f
check type
sshane Dec 31, 2022
c663428
formatting
sshane Dec 31, 2022
5457084
order
sshane Dec 31, 2022
34baede
comment
sshane Dec 31, 2022
caa5e27
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 31, 2022
927fb2a
comment
sshane Dec 31, 2022
6228ea1
update test refs
sshane Jan 1, 2023
132f234
fix refs
sshane Jan 1, 2023
2d2eea6
fix detection
sshane Jan 1, 2023
cadd82e
fix refs
sshane Jan 1, 2023
afd9500
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 1, 2023
5519084
same check as before
sshane Jan 1, 2023
bf1b8f6
Update tests/functional/a/abstract/abstract_method.py
sshane Jan 2, 2023
a62b2e5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 2, 2023
936e08f
Merge remote-tracking branch 'upstream/main' into W0223-widening
sshane Jan 11, 2023
c8564bb
update refs
sshane Jan 11, 2023
d78283d
Merge remote-tracking branch 'upstream/main' into W0223-widening
sshane Jun 3, 2024
476e5a5
revert tests for retry
sshane Jun 3, 2024
6fd65a7
stash
sshane Jun 3, 2024
9582aa0
fix tests
sshane Jun 3, 2024
38d3383
check text first
sshane Jun 3, 2024
4398445
still doesn't pass?
sshane Jun 3, 2024
591d1ab
ugh
sshane Jun 4, 2024
686977a
fix comments
sshane Jun 4, 2024
5ff5496
other PR
sshane Jun 4, 2024
f2fd7a0
add count
sshane Jun 4, 2024
334651c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 4, 2024
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
11 changes: 8 additions & 3 deletions pylint/checkers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,9 +1164,14 @@ def class_is_abstract(node: nodes.ClassDef) -> bool:
if meta.name == "ABCMeta" and meta.root().name in ABC_MODULES:
return True

for ancestor in node.ancestors():
if ancestor.name == "ABC" and ancestor.root().name in ABC_MODULES:
# abc.ABC inheritance
# As well as direct abc.ABC inheritance
for base in node.bases:
inferred_base = safe_infer(base)
if (
isinstance(inferred_base, nodes.ClassDef)
and inferred_base.root().name in ABC_MODULES
and inferred_base.name == "ABC"
):
return True

for method in node.methods():
Expand Down
9 changes: 7 additions & 2 deletions pylint/testutils/lint_module_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,15 @@
)
if missing:
msg.append("\nExpected in testdata:")
msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(missing))
msg.extend(

Check warning on line 275 in pylint/testutils/lint_module_test.py

View check run for this annotation

Codecov / codecov/patch

pylint/testutils/lint_module_test.py#L275

Added line #L275 was not covered by tests
f" {msg[0]:3}: {msg[1]} ({cnt})" for msg, cnt in sorted(missing.items())
)
if unexpected:
msg.append("\nUnexpected in testdata:")
msg.extend(f" {msg[0]:3}: {msg[1]}" for msg in sorted(unexpected))
msg.extend(
f" {msg[0]:3}: {msg[1]} ({cnt})"
for msg, cnt in sorted(unexpected.items())
)
error_msg = "\n".join(msg)
if self._config and self._config.getoption("verbose") > 0:
error_msg += "\n\nActual pylint output for this file:\n"
Expand Down
39 changes: 35 additions & 4 deletions tests/functional/a/abstract/abstract_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import abc


class Abstract:
class Abstract(abc.ABC):
def aaaa(self):
"""should be overridden in concrete class"""
raise NotImplementedError()
Expand All @@ -16,7 +16,7 @@ def bbbb(self):
raise NotImplementedError()


class AbstractB(Abstract):
class AbstractB(Abstract, abc.ABC):
"""Abstract class.

this class is checking that it does not output an error msg for
Expand Down Expand Up @@ -44,12 +44,43 @@ class AbstractD(AbstractB, metaclass=abc.ABCMeta):
"""


class Concrete(Abstract): # [abstract-method]
"""Concrete class"""
class ConcreteA(AbstractC): # [abstract-method, abstract-method, abstract-method]
"""
Incomplete concrete class.

Should trigger a warning for unimplemented abstract
methods due to lack of explicit abc.ABC inheritance.
"""


class ConcreteB(Abstract): # [abstract-method]
"""
Incomplete concrete class.

Should trigger a warning for unimplemented abstract
methods due to lack of explicit abc.ABC inheritance.
"""

def aaaa(self):
"""overridden form Abstract"""


class ConcreteC(AbstractC):
"""
Complete concrete class

Should not trigger a warning as all
abstract methods are implemented.
"""
def aaaa(self):
"""overridden form Abstract"""

def bbbb(self):
"""overridden form Abstract"""

def cccc(self):
"""overridden form AbstractB"""


class Structure(metaclass=abc.ABCMeta):
@abc.abstractmethod
Expand Down
35 changes: 19 additions & 16 deletions tests/functional/a/abstract/abstract_method.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
abstract-method:47:0:47:14:Concrete:Method 'bbbb' is abstract in class 'Abstract' but is not overridden in child class 'Concrete':INFERENCE
abstract-method:70:0:70:15:Container:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:70:0:70:15:Container:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:70:0:70:15:Container:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:76:0:76:13:Sizable:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:76:0:76:13:Sizable:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:76:0:76:13:Sizable:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:82:0:82:14:Hashable:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:82:0:82:14:Hashable:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:82:0:82:14:Hashable:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:87:0:87:14:Iterator:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:87:0:87:14:Iterator:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:87:0:87:14:Iterator:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:106:0:106:19:BadComplexMro:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'BadComplexMro':INFERENCE
abstract-method:106:0:106:19:BadComplexMro:Method '__len__' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
abstract-method:106:0:106:19:BadComplexMro:Method 'length' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
abstract-method:47:0:47:15:ConcreteA:Method 'aaaa' is abstract in class 'Abstract' but is not overridden in child class 'ConcreteA':INFERENCE
abstract-method:47:0:47:15:ConcreteA:Method 'bbbb' is abstract in class 'Abstract' but is not overridden in child class 'ConcreteA':INFERENCE
abstract-method:47:0:47:15:ConcreteA:Method 'cccc' is abstract in class 'AbstractB' but is not overridden in child class 'ConcreteA':INFERENCE
abstract-method:56:0:56:15:ConcreteB:Method 'bbbb' is abstract in class 'Abstract' but is not overridden in child class 'ConcreteB':INFERENCE
abstract-method:101:0:101:15:Container:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:101:0:101:15:Container:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:101:0:101:15:Container:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Container':INFERENCE
abstract-method:107:0:107:13:Sizable:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:107:0:107:13:Sizable:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:107:0:107:13:Sizable:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Sizable':INFERENCE
abstract-method:113:0:113:14:Hashable:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:113:0:113:14:Hashable:Method '__iter__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:113:0:113:14:Hashable:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Hashable':INFERENCE
abstract-method:118:0:118:14:Iterator:Method '__contains__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:118:0:118:14:Iterator:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:118:0:118:14:Iterator:Method '__len__' is abstract in class 'Structure' but is not overridden in child class 'Iterator':INFERENCE
abstract-method:137:0:137:19:BadComplexMro:Method '__hash__' is abstract in class 'Structure' but is not overridden in child class 'BadComplexMro':INFERENCE
abstract-method:137:0:137:19:BadComplexMro:Method '__len__' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
abstract-method:137:0:137:19:BadComplexMro:Method 'length' is abstract in class 'AbstractSizable' but is not overridden in child class 'BadComplexMro':INFERENCE
Loading