Skip to content

Commit

Permalink
Fix false positive for Final local scope variable in Protocol (#17308)
Browse files Browse the repository at this point in the history
This PR fixes and closes #17281 ,which reported a false positive when
using Final within the local function scope of a protocol method.

With these changes:
- Local variables within protocol methods can be marked as Final.
- Protocol members still cannot be marked as Final

Modified ``semanal.py`` file and added a unit test in
``test-data/unit/check.final.test``

---------

Co-authored-by: Jelle Zijlstra <[email protected]>
Co-authored-by: Stanislav Terliakov
  • Loading branch information
GiorgosPapoutsakis and JelleZijlstra committed Jun 4, 2024
1 parent 16d5aaf commit ad0e180
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 1 deletion.
3 changes: 2 additions & 1 deletion mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -3505,7 +3505,8 @@ def unwrap_final(self, s: AssignmentStmt) -> bool:
if self.loop_depth[-1] > 0:
self.fail("Cannot use Final inside a loop", s)
if self.type and self.type.is_protocol:
self.msg.protocol_members_cant_be_final(s)
if self.is_class_scope():
self.msg.protocol_members_cant_be_final(s)
if (
isinstance(s.rvalue, TempNode)
and s.rvalue.no_rhs
Expand Down
44 changes: 44 additions & 0 deletions test-data/unit/check-final.test
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,50 @@ class P(Protocol):
pass
[out]

[case testFinalInProtocol]
from typing import Final, Protocol, final

class P(Protocol):
var1 : Final[int] = 0 # E: Protocol member cannot be final

@final # E: Protocol member cannot be final
def meth1(self) -> None:
var2: Final = 0

def meth2(self) -> None:
var3: Final = 0

def meth3(self) -> None:
class Inner:
var3: Final = 0 # OK

@final
def inner(self) -> None: ...

class Inner:
var3: Final = 0 # OK

@final
def inner(self) -> None: ...

[out]

[case testFinalWithClassVarInProtocol]
from typing import Protocol, Final, final, ClassVar

class P(Protocol):
var1 : Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final
var2: ClassVar[int] = 1

@final # E: Protocol member cannot be final
def meth1(self) -> None:
...

def meth2(self) -> None:
var3: Final[ClassVar[int]] = 0 # E: Variable should not be annotated with both ClassVar and Final # E: ClassVar can only be used for assignments in class body

[out]

[case testFinalNotInLoops]
from typing import Final

Expand Down

0 comments on commit ad0e180

Please sign in to comment.