Check if this assignment does not assign to a final attribute. This function performs the check only for name assignments at module and class scope. The assignments to `obj.attr` and `Cls.attr` are checked in checkmember.py.
(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExpr)
| 3919 | self._is_final_def = old_ctx |
| 3920 | |
| 3921 | def check_final(self, s: AssignmentStmt | OperatorAssignmentStmt | AssignmentExpr) -> None: |
| 3922 | """Check if this assignment does not assign to a final attribute. |
| 3923 | |
| 3924 | This function performs the check only for name assignments at module |
| 3925 | and class scope. The assignments to `obj.attr` and `Cls.attr` are checked |
| 3926 | in checkmember.py. |
| 3927 | """ |
| 3928 | if isinstance(s, AssignmentStmt): |
| 3929 | lvs = self.flatten_lvalues(s.lvalues) |
| 3930 | elif isinstance(s, AssignmentExpr): |
| 3931 | lvs = [s.target] |
| 3932 | else: |
| 3933 | lvs = [s.lvalue] |
| 3934 | is_final_decl = s.is_final_def if isinstance(s, AssignmentStmt) else False |
| 3935 | if is_final_decl and (active_class := self.scope.active_class()): |
| 3936 | lv = lvs[0] |
| 3937 | assert isinstance(lv, RefExpr) |
| 3938 | if lv.node is not None: |
| 3939 | assert isinstance(lv.node, Var) |
| 3940 | if ( |
| 3941 | lv.node.final_unset_in_class |
| 3942 | and not lv.node.final_set_in_init |
| 3943 | and not self.is_stub # It is OK to skip initializer in stub files. |
| 3944 | and |
| 3945 | # Avoid extra error messages, if there is no type in Final[...], |
| 3946 | # then we already reported the error about missing r.h.s. |
| 3947 | isinstance(s, AssignmentStmt) |
| 3948 | and s.type is not None |
| 3949 | # Avoid extra error message for NamedTuples, |
| 3950 | # they were reported during semanal |
| 3951 | and not active_class.is_named_tuple |
| 3952 | ): |
| 3953 | self.msg.final_without_value(s) |
| 3954 | for lv in lvs: |
| 3955 | if isinstance(lv, RefExpr) and isinstance(lv.node, Var): |
| 3956 | name = lv.node.name |
| 3957 | cls = self.scope.active_class() |
| 3958 | if cls is not None: |
| 3959 | # These additional checks exist to give more error messages |
| 3960 | # even if the final attribute was overridden with a new symbol |
| 3961 | # (which is itself an error)... |
| 3962 | for base in cls.mro[1:]: |
| 3963 | sym = base.names.get(name) |
| 3964 | # We only give this error if base node is variable, |
| 3965 | # overriding final method will be caught in |
| 3966 | # `check_compatibility_final_super()`. |
| 3967 | if sym and isinstance(sym.node, Var): |
| 3968 | if sym.node.is_final and not is_final_decl: |
| 3969 | self.msg.cant_assign_to_final(name, sym.node.info is None, s) |
| 3970 | # ...but only once |
| 3971 | break |
| 3972 | if lv.node.is_final and not is_final_decl: |
| 3973 | self.msg.cant_assign_to_final(name, lv.node.info is None, s) |
| 3974 | |
| 3975 | def check_assignment_to_slots(self, lvalue: Lvalue) -> None: |
| 3976 | if not isinstance(lvalue, MemberExpr): |
no test coverage detected