Strip Final[...] if present in an assignment. This is done to invoke type inference during type checking phase for this assignment. Also, Final[...] doesn't affect type in any way -- it is rather an access qualifier for given `Var`. Also perform various consistency
(self, s: AssignmentStmt)
| 3741 | hook(DynamicClassDefContext(call, lval.name, self)) |
| 3742 | |
| 3743 | def unwrap_final(self, s: AssignmentStmt) -> bool: |
| 3744 | """Strip Final[...] if present in an assignment. |
| 3745 | |
| 3746 | This is done to invoke type inference during type checking phase for this |
| 3747 | assignment. Also, Final[...] doesn't affect type in any way -- it is rather an |
| 3748 | access qualifier for given `Var`. |
| 3749 | |
| 3750 | Also perform various consistency checks. |
| 3751 | |
| 3752 | Returns True if Final[...] was present. |
| 3753 | """ |
| 3754 | if not s.unanalyzed_type or not self.is_final_type(s.unanalyzed_type): |
| 3755 | return False |
| 3756 | assert isinstance(s.unanalyzed_type, UnboundType) |
| 3757 | if len(s.unanalyzed_type.args) > 1: |
| 3758 | self.fail("Final[...] takes at most one type argument", s.unanalyzed_type) |
| 3759 | invalid_bare_final = False |
| 3760 | if not s.unanalyzed_type.args: |
| 3761 | s.type = None |
| 3762 | if ( |
| 3763 | isinstance(s.rvalue, TempNode) |
| 3764 | and s.rvalue.no_rhs |
| 3765 | # Filter duplicate errors, we already reported this: |
| 3766 | and not (self.type and self.type.is_named_tuple) |
| 3767 | ): |
| 3768 | invalid_bare_final = True |
| 3769 | self.fail("Type in Final[...] can only be omitted if there is an initializer", s) |
| 3770 | else: |
| 3771 | s.type = s.unanalyzed_type.args[0] |
| 3772 | |
| 3773 | if ( |
| 3774 | s.type is not None |
| 3775 | and self.options.python_version < (3, 13) |
| 3776 | and self.is_classvar(s.type) |
| 3777 | ): |
| 3778 | self.fail("Variable should not be annotated with both ClassVar and Final", s) |
| 3779 | return False |
| 3780 | |
| 3781 | if len(s.lvalues) != 1 or not isinstance(s.lvalues[0], RefExpr): |
| 3782 | self.fail("Invalid final declaration", s) |
| 3783 | return False |
| 3784 | lval = s.lvalues[0] |
| 3785 | assert isinstance(lval, RefExpr) |
| 3786 | |
| 3787 | # Reset inferred status if it was set due to simple literal rvalue on previous iteration. |
| 3788 | # TODO: this is a best-effort quick fix, we should avoid the need to manually sync this, |
| 3789 | # see https://github.com/python/mypy/issues/6458. |
| 3790 | if lval.is_new_def: |
| 3791 | lval.is_inferred_def = s.type is None |
| 3792 | |
| 3793 | if self.loop_depth[-1] > 0: |
| 3794 | self.fail("Cannot use Final inside a loop", s) |
| 3795 | if self.type and self.type.is_protocol: |
| 3796 | if self.is_class_scope(): |
| 3797 | self.msg.protocol_members_cant_be_final(s) |
| 3798 | if ( |
| 3799 | isinstance(s.rvalue, TempNode) |
| 3800 | and s.rvalue.no_rhs |
no test coverage detected