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 7538ff10be..47142ba960 100644 --- a/pylint/checkers/classes/class_checker.py +++ b/pylint/checkers/classes/class_checker.py @@ -2240,7 +2240,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. @@ -2248,7 +2248,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): + ...