If this is a locally valid final declaration, set the corresponding flag on `Var`.
(self, s: AssignmentStmt)
| 3827 | return |
| 3828 | |
| 3829 | def store_final_status(self, s: AssignmentStmt) -> None: |
| 3830 | """If this is a locally valid final declaration, set the corresponding flag on `Var`.""" |
| 3831 | if s.is_final_def: |
| 3832 | if len(s.lvalues) == 1 and isinstance(s.lvalues[0], RefExpr): |
| 3833 | node = s.lvalues[0].node |
| 3834 | if isinstance(node, Var): |
| 3835 | node.is_final = True |
| 3836 | if s.type: |
| 3837 | node.final_value = constant_fold_expr(s.rvalue, self.cur_mod_id) |
| 3838 | if self.is_class_scope() and ( |
| 3839 | isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs |
| 3840 | ): |
| 3841 | node.final_unset_in_class = True |
| 3842 | else: |
| 3843 | for lval in self.flatten_lvalues(s.lvalues): |
| 3844 | # Special case: we are working with an `Enum`: |
| 3845 | # |
| 3846 | # class MyEnum(Enum): |
| 3847 | # key = 'some value' |
| 3848 | # |
| 3849 | # Here `key` is implicitly final. In runtime, code like |
| 3850 | # |
| 3851 | # MyEnum.key = 'modified' |
| 3852 | # |
| 3853 | # will fail with `AttributeError: Cannot reassign members.` |
| 3854 | # That's why we need to replicate this. |
| 3855 | if ( |
| 3856 | isinstance(lval, NameExpr) |
| 3857 | and isinstance(self.type, TypeInfo) |
| 3858 | and self.type.is_enum |
| 3859 | ): |
| 3860 | cur_node = self.type.names.get(lval.name, None) |
| 3861 | if ( |
| 3862 | cur_node |
| 3863 | and isinstance(cur_node.node, Var) |
| 3864 | and not (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs) |
| 3865 | ): |
| 3866 | # Double underscored members are writable on an `Enum`. |
| 3867 | # (Except read-only `__members__` but that is handled in type checker) |
| 3868 | cur_node.node.is_final = s.is_final_def = not is_dunder(cur_node.node.name) |
| 3869 | |
| 3870 | # Special case: deferred initialization of a final attribute in __init__. |
| 3871 | # In this case we just pretend this is a valid final definition to suppress |
| 3872 | # errors about assigning to final attribute. |
| 3873 | if isinstance(lval, MemberExpr) and self.is_self_member_ref(lval): |
| 3874 | assert self.type, "Self member outside a class" |
| 3875 | cur_node = self.type.names.get(lval.name, None) |
| 3876 | if cur_node and isinstance(cur_node.node, Var) and cur_node.node.is_final: |
| 3877 | assert self.function_stack |
| 3878 | current_function = self.function_stack[-1] |
| 3879 | if ( |
| 3880 | current_function.name == "__init__" |
| 3881 | and cur_node.node.final_unset_in_class |
| 3882 | and not cur_node.node.final_set_in_init |
| 3883 | and not (isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs) |
| 3884 | ): |
| 3885 | cur_node.node.final_set_in_init = True |
| 3886 | s.is_final_def = True |
no test coverage detected