diff options
author | Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> | 2022-07-31 22:23:57 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-31 22:23:57 +0200 |
commit | 397c1703e8ae6349d33f7b99f45b2ccaf581e666 (patch) | |
tree | f8cd513abfbd48b606d24bd18ed5ea10be2581b0 | |
parent | ebd54021ee3a779ee66dfe97e8dbf2a5966f614e (diff) | |
download | pylint-git-397c1703e8ae6349d33f7b99f45b2ccaf581e666.tar.gz |
Don't emit ``super-init-not-called`` for abstract ``__init__`` methods (#7227)
-rw-r--r-- | doc/whatsnew/fragments/3975.false_positive | 3 | ||||
-rw-r--r-- | pylint/checkers/classes/class_checker.py | 10 | ||||
-rw-r--r-- | tests/functional/i/init_not_called.py | 4 | ||||
-rw-r--r-- | tests/functional/i/init_not_called.txt | 1 | ||||
-rw-r--r-- | tests/functional/n/non/non_init_parent_called.py | 2 | ||||
-rw-r--r-- | tests/functional/n/non/non_init_parent_called.txt | 1 | ||||
-rw-r--r-- | tests/functional/s/super/super_init_not_called.py | 43 | ||||
-rw-r--r-- | tests/functional/s/super/super_init_not_called.rc | 4 | ||||
-rw-r--r-- | tests/functional/s/super/super_init_not_called.txt | 7 |
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 |