summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2022-07-31 22:23:57 +0200
committerGitHub <noreply@github.com>2022-07-31 22:23:57 +0200
commit397c1703e8ae6349d33f7b99f45b2ccaf581e666 (patch)
treef8cd513abfbd48b606d24bd18ed5ea10be2581b0
parentebd54021ee3a779ee66dfe97e8dbf2a5966f614e (diff)
downloadpylint-git-397c1703e8ae6349d33f7b99f45b2ccaf581e666.tar.gz
Don't emit ``super-init-not-called`` for abstract ``__init__`` methods (#7227)
-rw-r--r--doc/whatsnew/fragments/3975.false_positive3
-rw-r--r--pylint/checkers/classes/class_checker.py10
-rw-r--r--tests/functional/i/init_not_called.py4
-rw-r--r--tests/functional/i/init_not_called.txt1
-rw-r--r--tests/functional/n/non/non_init_parent_called.py2
-rw-r--r--tests/functional/n/non/non_init_parent_called.txt1
-rw-r--r--tests/functional/s/super/super_init_not_called.py43
-rw-r--r--tests/functional/s/super/super_init_not_called.rc4
-rw-r--r--tests/functional/s/super/super_init_not_called.txt7
9 files changed, 61 insertions, 14 deletions
diff --git a/doc/whatsnew/fragments/3975.false_positive b/doc/whatsnew/fragments/3975.false_positive
new file mode 100644
index 000000000..f6c7f1f9d
--- /dev/null
+++ b/doc/whatsnew/fragments/3975.false_positive
@@ -0,0 +1,3 @@
+Don't report ``super-init-not-called`` for abstract ``__init__`` methods.
+
+Closes #3975
diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py
index c345f41b5..fb329c6ee 100644
--- a/pylint/checkers/classes/class_checker.py
+++ b/pylint/checkers/classes/class_checker.py
@@ -2034,7 +2034,7 @@ a metaclass class method.",
# Record that the class' init has been called
parents_with_called_inits.add(node_frame_class(method))
except KeyError:
- if klass not in to_call:
+ if klass not in klass_node.ancestors(recurs=False):
self.add_message(
"non-parent-init-called", node=expr, args=klass.name
)
@@ -2061,9 +2061,6 @@ a metaclass class method.",
if decorated_with(node, ["typing.overload"]):
continue
- cls = node_frame_class(method)
- if klass.name == "object" or (cls and cls.name == "object"):
- continue
self.add_message(
"super-init-not-called",
args=klass.name,
@@ -2196,7 +2193,10 @@ def _ancestors_to_call(
to_call: dict[nodes.ClassDef, bases.UnboundMethod] = {}
for base_node in klass_node.ancestors(recurs=False):
try:
- to_call[base_node] = next(base_node.igetattr(method))
+ init_node: bases.UnboundMethod = next(base_node.igetattr(method))
+ if init_node.is_abstract():
+ continue
+ to_call[base_node] = init_node
except astroid.InferenceError:
continue
return to_call
diff --git a/tests/functional/i/init_not_called.py b/tests/functional/i/init_not_called.py
index a95efadf6..ac4baac6e 100644
--- a/tests/functional/i/init_not_called.py
+++ b/tests/functional/i/init_not_called.py
@@ -59,9 +59,9 @@ class NewStyleC(object):
class AssignedInit(NewStyleC):
- """No init called."""
+ """No init called, but abstract so that is fine."""
- def __init__(self): # [super-init-not-called]
+ def __init__(self):
self.arg = 0
diff --git a/tests/functional/i/init_not_called.txt b/tests/functional/i/init_not_called.txt
index 9015d1e27..b944b5585 100644
--- a/tests/functional/i/init_not_called.txt
+++ b/tests/functional/i/init_not_called.txt
@@ -1,2 +1 @@
super-init-not-called:32:4:32:16:ZZZZ.__init__:__init__ method from base class 'BBBB' is not called:INFERENCE
-super-init-not-called:64:4:64:16:AssignedInit.__init__:__init__ method from base class 'NewStyleC' is not called:INFERENCE
diff --git a/tests/functional/n/non/non_init_parent_called.py b/tests/functional/n/non/non_init_parent_called.py
index 7ad3f1932..0721703b2 100644
--- a/tests/functional/n/non/non_init_parent_called.py
+++ b/tests/functional/n/non/non_init_parent_called.py
@@ -46,6 +46,6 @@ class Super2(dict):
""" Using the same idiom as Super, but without calling
the __init__ method.
"""
- def __init__(self): # [super-init-not-called]
+ def __init__(self):
base = super()
base.__woohoo__() # [no-member]
diff --git a/tests/functional/n/non/non_init_parent_called.txt b/tests/functional/n/non/non_init_parent_called.txt
index 06de9a244..0d9e227c2 100644
--- a/tests/functional/n/non/non_init_parent_called.txt
+++ b/tests/functional/n/non/non_init_parent_called.txt
@@ -2,5 +2,4 @@ import-error:7:0:7:18::Unable to import 'nonexistant':UNDEFINED
non-parent-init-called:15:8:15:26:AAAA.__init__:__init__ method from a non direct base class 'BBBBMixin' is called:UNDEFINED
no-member:23:50:23:77:CCC:Module 'functional.n.non.non_init_parent_called' has no 'BBBB' member:INFERENCE
no-member:28:8:28:35:CCC.__init__:Module 'functional.n.non.non_init_parent_called' has no 'BBBB' member:INFERENCE
-super-init-not-called:49:4:49:16:Super2.__init__:__init__ method from base class 'dict' is not called:INFERENCE
no-member:51:8:51:23:Super2.__init__:Super of 'Super2' has no '__woohoo__' member:INFERENCE
diff --git a/tests/functional/s/super/super_init_not_called.py b/tests/functional/s/super/super_init_not_called.py
index 90a884b0b..f0bfe0329 100644
--- a/tests/functional/s/super/super_init_not_called.py
+++ b/tests/functional/s/super/super_init_not_called.py
@@ -1,6 +1,7 @@
"""Tests for super-init-not-called."""
# pylint: disable=too-few-public-methods, missing-class-docstring
+import abc
import ctypes
@@ -53,5 +54,45 @@ class ChildThree(ParentWithoutInit):
# Regression test as reported in
# https://github.com/PyCQA/pylint/issues/6027
class MyUnion(ctypes.Union):
- def __init__(self): # [super-init-not-called]
+ def __init__(self):
pass
+
+
+# Should not be called on abstract __init__ methods
+# https://github.com/PyCQA/pylint/issues/3975
+class Base:
+ def __init__(self, param: int, param_two: str) -> None:
+ raise NotImplementedError()
+
+
+class Derived(Base):
+ def __init__(self, param: int, param_two: str) -> None:
+ self.param = param + 1
+ self.param_two = param_two[::-1]
+
+
+class AbstractBase(abc.ABC):
+ def __init__(self, param: int) -> None:
+ self.param = param + 1
+
+ def abstract_method(self) -> str:
+ """This needs to be implemented."""
+ raise NotImplementedError()
+
+
+class DerivedFromAbstract(AbstractBase):
+ def __init__(self, param: int) -> None: # [super-init-not-called]
+ print("Called")
+
+ def abstract_method(self) -> str:
+ return "Implemented"
+
+
+class DerivedFrom(UnknownParent): # [undefined-variable]
+ def __init__(self) -> None:
+ print("Called")
+
+
+class DerivedFromUnknownGrandparent(DerivedFrom):
+ def __init__(self) -> None:
+ DerivedFrom.__init__(self)
diff --git a/tests/functional/s/super/super_init_not_called.rc b/tests/functional/s/super/super_init_not_called.rc
new file mode 100644
index 000000000..b8621ee57
--- /dev/null
+++ b/tests/functional/s/super/super_init_not_called.rc
@@ -0,0 +1,4 @@
+[testoptions]
+# ctypes has a different implementation in PyPy and does have an inferable
+# __init__ method for ctypes.Union.
+except_implementations=PyPy
diff --git a/tests/functional/s/super/super_init_not_called.txt b/tests/functional/s/super/super_init_not_called.txt
index aafaa2023..002db0d76 100644
--- a/tests/functional/s/super/super_init_not_called.txt
+++ b/tests/functional/s/super/super_init_not_called.txt
@@ -1,3 +1,4 @@
-undefined-variable:18:23:18:40:UninferableChild:Undefined variable 'UninferableParent':UNDEFINED
-super-init-not-called:49:4:49:16:ChildThree.__init__:__init__ method from base class 'ParentWithoutInit' is not called:INFERENCE
-super-init-not-called:56:4:56:16:MyUnion.__init__:__init__ method from base class 'Union' is not called:INFERENCE
+undefined-variable:19:23:19:40:UninferableChild:Undefined variable 'UninferableParent':UNDEFINED
+super-init-not-called:50:4:50:16:ChildThree.__init__:__init__ method from base class 'ParentWithoutInit' is not called:INFERENCE
+super-init-not-called:84:4:84:16:DerivedFromAbstract.__init__:__init__ method from base class 'AbstractBase' is not called:INFERENCE
+undefined-variable:91:18:91:31:DerivedFrom:Undefined variable 'UnknownParent':UNDEFINED