diff --git a/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt b/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
index 93555e207b7f..5bb9a401b4aa 100644
--- a/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
+++ b/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
@@ -30,11 +30,11 @@ class PyFinalInspection : PyInspection() {
override fun visitPyClass(node: PyClass) {
super.visitPyClass(node)
- node.superClassExpressions.forEach {
- val cls = (myTypeEvalContext.getType(it) as? PyClassType)?.pyClass
- if (cls != null && isFinal(cls)) {
- registerProblem(it, "'${cls.name}' is marked as '@final' and should not be subclassed")
- }
+ node.getSuperClasses(myTypeEvalContext).filter { isFinal(it) }.let { finalSuperClasses ->
+ if (finalSuperClasses.isEmpty()) return@let
+
+ val postfix = " ${if (finalSuperClasses.size == 1) "is" else "are"} marked as '@final' and should not be subclassed"
+ registerProblem(node.nameIdentifier, finalSuperClasses.joinToString(postfix = postfix) { "'${it.name}'" ?: "" })
}
if (PyiUtil.isInsideStub(node)) {
diff --git a/python/testData/inspections/PyFinalInspection/SubclassingFinalClass/a.py b/python/testData/inspections/PyFinalInspection/SubclassingFinalClass/a.py
index 2b484bc609c8..a03a8b0198c3 100644
--- a/python/testData/inspections/PyFinalInspection/SubclassingFinalClass/a.py
+++ b/python/testData/inspections/PyFinalInspection/SubclassingFinalClass/a.py
@@ -1,3 +1,3 @@
from b import A
-class B(A):
+class B(A):
pass
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java
index 04e9d392f2a3..a19e927d426a 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java
@@ -16,8 +16,33 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
"@final\n" +
"class A:\n" +
" pass\n" +
- "class B(A):\n" +
+ "class B(A):\n" +
" pass");
+
+ doTestByText("from typing_extensions import final\n" +
+ "@final\n" +
+ "class A:\n" +
+ " pass\n" +
+ "@final\n" +
+ "class B:\n" +
+ " pass\n" +
+ "class C(A, B):\n" +
+ " pass");
+ }
+
+ // PY-34945
+ public void testFinalClassAsMetaclass() {
+ runWithLanguageLevel(
+ LanguageLevel.PYTHON30,
+ () -> doTestByText("from typing_extensions import final\n" +
+ "\n" +
+ "@final\n" +
+ "class MT(type):\n" +
+ " pass\n" +
+ "\n" +
+ "class A(metaclass=MT):\n" +
+ " pass")
+ );
}
// PY-34945
@@ -416,7 +441,7 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
" a = 2")
);
}
-
+
// PY-34945
public void testNonLocalReassignment() {
runWithLanguageLevel(