diff --git a/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt b/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
index 0d92b56764e3..7a3eeeeb3d36 100644
--- a/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
+++ b/python/src/com/jetbrains/python/inspections/PyFinalInspection.kt
@@ -111,6 +111,14 @@ class PyFinalInspection : PyInspection() {
else if (PyUtil.multiResolveTopPriority(node, resolveContext).any { it != node && it is PyTargetExpression && isFinal(it) }) {
registerProblem(node, "'${node.name}' is 'Final' and could not be reassigned")
}
+ else {
+ if (!node.isQualified) {
+ val scopeOwner = ScopeUtil.getScopeOwner(node)
+ if (scopeOwner is PyClass) {
+ checkInheritedClassFinalReassignmentOnClassLevel(node, scopeOwner)
+ }
+ }
+ }
}
if (isFinal(node) && PyUtil.multiResolveTopPriority(node, resolveContext).any { it != node }) {
@@ -265,6 +273,19 @@ class PyFinalInspection : PyInspection() {
}
}
+ private fun checkInheritedClassFinalReassignmentOnClassLevel(target: PyTargetExpression, cls: PyClass) {
+ val name = target.name ?: return
+
+ for (ancestor in cls.getAncestorClasses(myTypeEvalContext)) {
+ val ancestorClassAttribute = ancestor.findClassAttribute(name, false, myTypeEvalContext)
+
+ if (ancestorClassAttribute != null && ancestorClassAttribute.hasAssignedValue() && isFinal(ancestorClassAttribute)) {
+ registerProblem(target, "'${ancestor.name}.$name' is 'Final' and could not be reassigned")
+ break
+ }
+ }
+ }
+
private fun checkFinalIsOuterMost(node: PyReferenceExpression) {
if (isTopLevelInAnnotationOrTypeComment(node)) return
(node.parent as? PySubscriptionExpression)?.let {
diff --git a/python/testData/inspections/PyFinalInspection/ImportedClassFinalReassignment/a.py b/python/testData/inspections/PyFinalInspection/ImportedClassFinalReassignment/a.py
index 50d82886c862..fd060b61e280 100644
--- a/python/testData/inspections/PyFinalInspection/ImportedClassFinalReassignment/a.py
+++ b/python/testData/inspections/PyFinalInspection/ImportedClassFinalReassignment/a.py
@@ -7,4 +7,7 @@ class B(A):
def my_cls_method(cls):
cls.a = 6
-B.a = 7
\ No newline at end of file
+B.a = 7
+
+class C(A):
+ a = 8
\ 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 db2b254ca8bb..cc325b3df6c8 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyFinalInspectionTest.java
@@ -350,8 +350,10 @@ public class PyFinalInspectionTest extends PyInspectionTestCase {
" def my_cls_method(cls):\n" +
" cls.a = 6\n" +
"\n" +
- "" +
- "B.a = 7")
+ "B.a = 7\n" +
+ "\n" +
+ "class C(A):\n" +
+ " a = 8\n")
);
}