-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Enums with annotations and no values are fine to be subclassed #11579
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
Changes from all commits
7989a2c
901ca52
e4d4112
04afff5
af90ec4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1678,7 +1678,6 @@ class A(Enum): | |||||||||||||||||||||||||||||||||||||||||||
class B(A): pass # E: Cannot inherit from final class "A" | ||||||||||||||||||||||||||||||||||||||||||||
[builtins fixtures/bool.pyi] | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
[case testEnumFinalSpecialProps] | ||||||||||||||||||||||||||||||||||||||||||||
# https://github.com/python/mypy/issues/11699 | ||||||||||||||||||||||||||||||||||||||||||||
from enum import Enum, IntEnum | ||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1702,3 +1701,48 @@ class EI(IntEnum): | |||||||||||||||||||||||||||||||||||||||||||
E._order_ = 'a' # E: Cannot assign to final attribute "_order_" | ||||||||||||||||||||||||||||||||||||||||||||
EI.value = 2 # E: Cannot assign to final attribute "value" | ||||||||||||||||||||||||||||||||||||||||||||
[builtins fixtures/bool.pyi] | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
[case testEnumNotFinalWithMethodsAndUninitializedValues] | ||||||||||||||||||||||||||||||||||||||||||||
# https://github.com/python/mypy/issues/11578 | ||||||||||||||||||||||||||||||||||||||||||||
from enum import Enum | ||||||||||||||||||||||||||||||||||||||||||||
from typing import Final | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
class A(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x: int | ||||||||||||||||||||||||||||||||||||||||||||
def method(self) -> int: pass | ||||||||||||||||||||||||||||||||||||||||||||
class B(A): | ||||||||||||||||||||||||||||||||||||||||||||
x = 1 # E: Cannot override writable attribute "x" with a final one | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be fixed. I am working on it right now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But, on the other hand. Looks like it is the same for regular classes: from typing import Final
class A:
x: int
y: int
class B(A):
x: Final = 1
y: Final = 2
# out/ex.py:8: error: Cannot override writable attribute "x" with a final one
# out/ex.py:9: error: Cannot override writable attribute "y" with a final one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't look like PEP-591 prohibits There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Though this is pretty clear why this is the case (and makes sense) Lines 2505 to 2525 in d7c4e69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, let's make this a subject for a next PR. This would require some extra work. And might have unwanted consequences. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah. This is intended. I don't remember why we didn't mention it in the PEP (it may be we indeed just wanted to be terse). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should still be allowed if the variable is not initialized yet, right? Based on the PEP it looks like it would be expected to be initialized in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it shouldn't. Consider this class A:
x: int
class B(A):
x: Final = 1
# somewhere in modules that depend on the above, and therefore are being processed later
def set_x(a: A) -> None:
a.x = 42
set_x(B()) # ??? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That makes sense in general but I wonder if it makes sense to special case enums here. We may need more details from the use case in #11578. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The example given here #11578 (comment) is basically how the code is used. The only thing that the base class does is define a The interpreter doesn't complain about subclassing an enum, and as I realize now that's because there are no values defined on the base class. I do agree enums probably need a special case because the class that does define enum values is the concrete class that is also final whereas the example above the interpreter does not effectively define final like it would for enums. |
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
class A1(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x: int = 1 | ||||||||||||||||||||||||||||||||||||||||||||
class B1(A1): # E: Cannot inherit from final class "A1" | ||||||||||||||||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
class A2(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x = 2 | ||||||||||||||||||||||||||||||||||||||||||||
class B2(A2): # E: Cannot inherit from final class "A2" | ||||||||||||||||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
# We leave this `Final` without a value, | ||||||||||||||||||||||||||||||||||||||||||||
# because we need to test annotation only mode: | ||||||||||||||||||||||||||||||||||||||||||||
class A3(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x: Final[int] # type: ignore | ||||||||||||||||||||||||||||||||||||||||||||
class B3(A3): | ||||||||||||||||||||||||||||||||||||||||||||
x = 1 # E: Cannot override final attribute "x" (previously declared in base class "A3") | ||||||||||||||||||||||||||||||||||||||||||||
[builtins fixtures/bool.pyi] | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
[case testEnumNotFinalWithMethodsAndUninitializedValuesStub] | ||||||||||||||||||||||||||||||||||||||||||||
import lib | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
[file lib.pyi] | ||||||||||||||||||||||||||||||||||||||||||||
from enum import Enum | ||||||||||||||||||||||||||||||||||||||||||||
class A(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x: int | ||||||||||||||||||||||||||||||||||||||||||||
class B(A): # E: Cannot inherit from final class "A" | ||||||||||||||||||||||||||||||||||||||||||||
x = 1 # E: Cannot override writable attribute "x" with a final one | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
class C(Enum): | ||||||||||||||||||||||||||||||||||||||||||||
x = 1 | ||||||||||||||||||||||||||||||||||||||||||||
class D(C): # E: Cannot inherit from final class "C" | ||||||||||||||||||||||||||||||||||||||||||||
x: int # E: Cannot assign to final name "x" | ||||||||||||||||||||||||||||||||||||||||||||
[builtins fixtures/bool.pyi] |
Uh oh!
There was an error while loading. Please reload this page.