diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
index c9c2eb49e9e0..69c066cd4bb3 100644
--- a/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
+++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java
@@ -28,14 +28,7 @@ import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiReference;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.inspections.quickfix.AddCallSuperQuickFix;
-import com.jetbrains.python.psi.LanguageLevel;
-import com.jetbrains.python.psi.PyCallExpression;
-import com.jetbrains.python.psi.PyClass;
-import com.jetbrains.python.psi.PyExpression;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.PyQualifiedExpression;
-import com.jetbrains.python.psi.PyRecursiveElementVisitor;
-import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.TypeEvalContext;
import java.util.Objects;
import java.util.Optional;
@@ -67,7 +60,7 @@ public class PyMissingConstructorInspection extends PyInspection {
if (superClasses.length == 0 ||
superClasses.length == 1 && OBJECT.equals(superClasses[0].getText()) ||
- !superHasConstructor(node, myTypeEvalContext)) {
+ !superHasNonAbstractConstructor(node, myTypeEvalContext)) {
return;
}
@@ -86,14 +79,16 @@ public class PyMissingConstructorInspection extends PyInspection {
}
}
- private static boolean superHasConstructor(@NotNull PyClass cls, @NotNull TypeEvalContext context) {
+ private static boolean superHasNonAbstractConstructor(@NotNull PyClass cls, @NotNull TypeEvalContext context) {
final String className = cls.getName();
for (PyClass baseClass : cls.getAncestorClasses(context)) {
if (!PyUtil.isObjectClass(baseClass) &&
- !Objects.equals(className, baseClass.getName()) &&
- baseClass.findMethodByName(INIT, false, context) != null) {
- return true;
+ !Objects.equals(className, baseClass.getName())) {
+ var initMethod = baseClass.findMethodByName(INIT, false, context);
+ if (initMethod != null && !PyKnownDecoratorUtil.hasAbstractDecorator(initMethod, context)) {
+ return true;
+ }
}
}
diff --git a/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py b/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py
new file mode 100644
index 000000000000..007e9236cfb3
--- /dev/null
+++ b/python/testData/inspections/PyMissingConstructorInspection/abstractConstructor.py
@@ -0,0 +1,46 @@
+from abc import abstractmethod
+
+class A1:
+ @abstractmethod
+ def __init__(self):
+ pass
+
+
+class A2:
+ @abstractmethod
+ def __init__(self):
+ pass
+
+
+class B1:
+ def __init__(self):
+ pass
+
+
+class B2:
+ pass
+
+
+class C1(A1):
+ def __init__(self):
+ pass
+
+
+class C2(A1, A2):
+ def __init__(self):
+ pass
+
+
+class C3(A1, B1):
+ def __init__(self):
+ pass
+
+
+class C4(B1, A1):
+ def __init__(self):
+ pass
+
+
+class D(B2, A1):
+ def __init__(self):
+ pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java b/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java
index 3c19e6dccab0..8bcfd0aec800 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyMissingConstructorTest.java
@@ -71,6 +71,11 @@ public class PyMissingConstructorTest extends PyTestCase {
doTest();
}
+ // PY-33265
+ public void testAbstractConstructor() {
+ doTest();
+ }
+
private void doTest() {
myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py");
myFixture.enableInspections(PyMissingConstructorInspection.class);