From 7b0bc1986eb0f4ec620677914327345b63864b00 Mon Sep 17 00:00:00 2001 From: Mark Byrne <31762852+mbyrnepr2@users.noreply.github.com> Date: Sat, 12 Nov 2022 22:23:28 +0100 Subject: [PATCH] Fix a crash when looking up an __init__ method (#7744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix a crash when a child class with an ``__init__`` method inherits from a parent class with an ``__init__`` class attribute. * `continue` if not a method. * Update pylint/checkers/classes/class_checker.py * Rename fragment Closes #7742 Co-authored-by: Pierre Sassoulas Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com> --- doc/whatsnew/fragments/7742.bugfix | 3 +++ pylint/checkers/classes/class_checker.py | 6 ++++-- tests/functional/i/init_not_called.py | 11 +++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 doc/whatsnew/fragments/7742.bugfix diff --git a/doc/whatsnew/fragments/7742.bugfix b/doc/whatsnew/fragments/7742.bugfix new file mode 100644 index 0000000000..7e3c930899 --- /dev/null +++ b/doc/whatsnew/fragments/7742.bugfix @@ -0,0 +1,3 @@ +Fix a crash when a child class with an ``__init__`` method inherits from a parent class with an ``__init__`` class attribute. + +Closes #7742 diff --git a/pylint/checkers/classes/class_checker.py b/pylint/checkers/classes/class_checker.py index 3bfef75490..eb157187e2 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -2224,7 +2224,7 @@ def _is_mandatory_method_param(self, node: nodes.NodeNG) -> bool: def _ancestors_to_call( - klass_node: nodes.ClassDef, method: str = "__init__" + klass_node: nodes.ClassDef, method_name: str = "__init__" ) -> dict[nodes.ClassDef, bases.UnboundMethod]: """Return a dictionary where keys are the list of base classes providing the queried method, and so that should/may be called from the method node. @@ -2232,7 +2232,9 @@ def _ancestors_to_call( to_call: dict[nodes.ClassDef, bases.UnboundMethod] = {} for base_node in klass_node.ancestors(recurs=False): try: - init_node: bases.UnboundMethod = next(base_node.igetattr(method)) + init_node = next(base_node.igetattr(method_name)) + if not isinstance(init_node, astroid.UnboundMethod): + continue if init_node.is_abstract(): continue to_call[base_node] = init_node diff --git a/tests/functional/i/init_not_called.py b/tests/functional/i/init_not_called.py index c310ae8369..ee8c4f5a1f 100644 --- a/tests/functional/i/init_not_called.py +++ b/tests/functional/i/init_not_called.py @@ -84,3 +84,14 @@ def __init__(self, num: float): def __init__(self, num): super().__init__(round(num)) + + +# https://github.com/PyCQA/pylint/issues/7742 +# Crash when parent class has a class attribute named `__init__` +class NoInitMethod: + __init__ = 42 + + +class ChildNoInitMethod(NoInitMethod): + def __init__(self): + ...