-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix False positive - Final local scope variable in Protocol #17308
Fix False positive - Final local scope variable in Protocol #17308
Conversation
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests seem good but I do have a question about whether the code change is implying new behavior for ClassVars.
@@ -3505,7 +3509,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(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(at least as I'm reading it, this is the key change and hence my question about line 1623)
mypy/semanal.py
Outdated
@@ -1620,7 +1620,11 @@ def visit_decorator(self, dec: Decorator) -> None: | |||
if self.is_class_scope(): | |||
assert self.type is not None, "No type set at class scope" | |||
if self.type.is_protocol: | |||
self.msg.protocol_members_cant_be_final(d) | |||
if dec.var.is_classvar: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like this change is allowing classvars to be Final on protocols.
Is that intended? I don't see a test for it, and I'm also not entirely sure it makes sense... what would a Final protocol classvar even mean, that classes which implement it must have a final classvar of matching type?
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
@stroxler Thank you for your feedback. |
9b07d71
to
11a8027
Compare
This comment has been minimized.
This comment has been minimized.
I have squashed all of my commits into a single commit to simplify the commit history and make it easier to review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me with the simplified change, tests look solid.
You'll still need a maintainer review, but approving because that may make it quicker (I volunteered to start helping with reviews but I'm still new to MyPy).###
I feel this could theoretically still have bugs if there are nested classes involved (e.g., a class inside a protocol method). However, that's a much more obscure bug and I'm OK with merging this so we fix the reported bug. |
If it helps, I'd be happy to take a look at nested classes and either add tests proving this works or come up with a fix-forward as a follow-up. |
@JelleZijlstra as far as I understand, this implementation doesn't suffer from such problems. In case of a nested class (or a class defined in a method) we do not work in I cannot find any explicit documentation entries regarding
(it may be worth including this test to document the behaviour For reference, |
Thanks! Would you mind adding a test for this behavior? I missed that the implementation already looks only at the topmost level in the stack. I don't really know what an inner class directly in a protocol should mean, but that's a separate issue. |
Would be glad to, but it's not my fork, sorry, I just wanted to review something:) @stroxler would you consider incorporating my suggested testcase? |
Ah sorry, should have checked the usernames. I can push your test case to this branch. |
Co-Authored-By: Stanislav Terliakov
Interesting that PyRight allows this. Now that I think about it, there's arguably a use case for Final attributes in protocols (which is not to say I necessarily think MyPy has to support it). Final in a class attribute actually means two mostly unrelated things, which I think is actually a bit problematic:
This actually strikes me as a gap in the typing semantics because it makes perfect sense to talk about non-final non-mutable attributes. In the case of a Protocol, non-overrideability doesn't really make any sense, but non-mutability does. Maybe I'll bring it up on the typing discourse at some point. In principle we could extend |
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
You can also use an Agree that expanding ReadOnly to protocols could be useful. |
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:
Modified
semanal.py
file and added a unit test intest-data/unit/check.final.test