Skip to content

Commit 10ccceb

Browse files
committed
Make enum values final - fixes python#11919
This fix allows using enum values in place of literals. For example: ``` def is_a(a: Literal["a"]): return None class MyStr(Enum): a = "a" b = "b" is_a(MyStr.a.value) ``` Currently this fails with ``` error: Argument 1 to "is_a" has incompatible type "str"; expected "Literal['a']" ``` The fix itself is simple - the special casing of final status for enums was being called too late to have an effect. Moving the function call up solves the problem.
1 parent 531a1f0 commit 10ccceb

File tree

3 files changed

+34
-19
lines changed

3 files changed

+34
-19
lines changed

mypy/semanal.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2107,10 +2107,10 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
21072107
s.is_final_def = self.unwrap_final(s)
21082108
self.analyze_lvalues(s)
21092109
self.check_final_implicit_def(s)
2110+
self.store_final_status(s)
21102111
self.check_classvar(s)
21112112
self.process_type_annotation(s)
21122113
self.apply_dynamic_class_hook(s)
2113-
self.store_final_status(s)
21142114
if not s.type:
21152115
self.process_module_assignment(s.lvalues, s.rvalue, s)
21162116
self.process__all__(s)

test-data/unit/check-enum.test

+30-15
Original file line numberDiff line numberDiff line change
@@ -618,8 +618,8 @@ reveal_type(A2.x.value) # N: Revealed type is "builtins.int"
618618
reveal_type(A2.x._value_) # N: Revealed type is "builtins.int"
619619
is_x(reveal_type(A3.x.name)) # N: Revealed type is "Literal['x']"
620620
is_x(reveal_type(A3.x._name_)) # N: Revealed type is "Literal['x']"
621-
reveal_type(A3.x.value) # N: Revealed type is "builtins.int"
622-
reveal_type(A3.x._value_) # N: Revealed type is "builtins.int"
621+
reveal_type(A3.x.value) # N: Revealed type is "Literal[1]?"
622+
reveal_type(A3.x._value_) # N: Revealed type is "Literal[1]?"
623623

624624
B1 = IntEnum('B1', 'x')
625625
class B2(IntEnum):
@@ -639,8 +639,8 @@ reveal_type(B2.x.value) # N: Revealed type is "builtins.int"
639639
reveal_type(B2.x._value_) # N: Revealed type is "builtins.int"
640640
is_x(reveal_type(B3.x.name)) # N: Revealed type is "Literal['x']"
641641
is_x(reveal_type(B3.x._name_)) # N: Revealed type is "Literal['x']"
642-
reveal_type(B3.x.value) # N: Revealed type is "builtins.int"
643-
reveal_type(B3.x._value_) # N: Revealed type is "builtins.int"
642+
reveal_type(B3.x.value) # N: Revealed type is "Literal[1]?"
643+
reveal_type(B3.x._value_) # N: Revealed type is "Literal[1]?"
644644

645645
# TODO: C1.x.value and C2.x.value should also be of type 'int'
646646
# This requires either a typeshed change or a plugin refinement
@@ -661,8 +661,8 @@ reveal_type(C2.x.value) # N: Revealed type is "builtins.int"
661661
reveal_type(C2.x._value_) # N: Revealed type is "builtins.int"
662662
is_x(reveal_type(C3.x.name)) # N: Revealed type is "Literal['x']"
663663
is_x(reveal_type(C3.x._name_)) # N: Revealed type is "Literal['x']"
664-
reveal_type(C3.x.value) # N: Revealed type is "builtins.int"
665-
reveal_type(C3.x._value_) # N: Revealed type is "builtins.int"
664+
reveal_type(C3.x.value) # N: Revealed type is "Literal[1]?"
665+
reveal_type(C3.x._value_) # N: Revealed type is "Literal[1]?"
666666

667667
D1 = Flag('D1', 'x')
668668
class D2(Flag):
@@ -680,8 +680,8 @@ reveal_type(D2.x.value) # N: Revealed type is "builtins.int"
680680
reveal_type(D2.x._value_) # N: Revealed type is "builtins.int"
681681
is_x(reveal_type(D3.x.name)) # N: Revealed type is "Literal['x']"
682682
is_x(reveal_type(D3.x._name_)) # N: Revealed type is "Literal['x']"
683-
reveal_type(D3.x.value) # N: Revealed type is "builtins.int"
684-
reveal_type(D3.x._value_) # N: Revealed type is "builtins.int"
683+
reveal_type(D3.x.value) # N: Revealed type is "Literal[1]?"
684+
reveal_type(D3.x._value_) # N: Revealed type is "Literal[1]?"
685685

686686
# TODO: Generalize our enum functional API logic to work with subclasses of Enum
687687
# See https://github.com/python/mypy/issues/6037
@@ -699,8 +699,8 @@ reveal_type(E2.x.value) # N: Revealed type is "builtins.int"
699699
reveal_type(E2.x._value_) # N: Revealed type is "builtins.int"
700700
is_x(reveal_type(E3.x.name)) # N: Revealed type is "Literal['x']"
701701
is_x(reveal_type(E3.x._name_)) # N: Revealed type is "Literal['x']"
702-
reveal_type(E3.x.value) # N: Revealed type is "builtins.int"
703-
reveal_type(E3.x._value_) # N: Revealed type is "builtins.int"
702+
reveal_type(E3.x.value) # N: Revealed type is "Literal[1]?"
703+
reveal_type(E3.x._value_) # N: Revealed type is "Literal[1]?"
704704

705705

706706
# TODO: Figure out if we can construct enums using EnumMetas using the functional API.
@@ -737,9 +737,9 @@ from enum import Enum
737737
class SomeEnum(Enum):
738738
a = "foo"
739739
[out]
740-
main:2: note: Revealed type is "builtins.int"
740+
main:2: note: Revealed type is "Literal[1]?"
741741
[out2]
742-
main:2: note: Revealed type is "builtins.str"
742+
main:2: note: Revealed type is "Literal['foo']?"
743743

744744
[case testEnumReachabilityChecksBasic]
745745
from enum import Enum
@@ -1311,8 +1311,8 @@ class Foo(Enum):
13111311
B = 2
13121312

13131313
a = Foo.A
1314-
reveal_type(a.value) # N: Revealed type is "builtins.int"
1315-
reveal_type(a._value_) # N: Revealed type is "builtins.int"
1314+
reveal_type(a.value) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]"
1315+
reveal_type(a._value_) # N: Revealed type is "Union[Literal[1]?, Literal[2]?]"
13161316

13171317
[case testNewSetsUnexpectedValueType]
13181318
from enum import Enum
@@ -1437,7 +1437,7 @@ class Foo(Enum):
14371437
A = 1
14381438
A = 'a' # E: Attempted to reuse member name "A" in Enum definition "Foo" \
14391439
# E: Incompatible types in assignment (expression has type "str", variable has type "int")
1440-
reveal_type(Foo.A.value) # N: Revealed type is "builtins.int"
1440+
reveal_type(Foo.A.value) # N: Revealed type is "Literal[1]?"
14411441

14421442
class Bar(Enum):
14431443
A = 1
@@ -1788,3 +1788,18 @@ class C(Enum):
17881788
class D(C): # E: Cannot inherit from final class "C"
17891789
x: int # E: Cannot assign to final name "x"
17901790
[builtins fixtures/bool.pyi]
1791+
1792+
[case testEnumLiteralValues]
1793+
from enum import Enum
1794+
1795+
class A(Enum):
1796+
str = "foo"
1797+
int = 1
1798+
bool = False
1799+
tuple = (1,)
1800+
1801+
reveal_type(A.str.value) # N: Revealed type is "Literal['foo']?"
1802+
reveal_type(A.int.value) # N: Revealed type is "Literal[1]?"
1803+
reveal_type(A.bool.value) # N: Revealed type is "Literal[False]?"
1804+
reveal_type(A.tuple.value) # N: Revealed type is "Tuple[Literal[1]?]"
1805+
[builtins fixtures/tuple.pyi]

test-data/unit/merge.test

+3-3
Original file line numberDiff line numberDiff line change
@@ -1435,16 +1435,16 @@ TypeInfo<0>(
14351435
Bases(enum.Enum<1>)
14361436
Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
14371437
Names(
1438-
X<3> (builtins.int<4>))
1438+
X<3> (Literal[0]?<4>))
14391439
MetaclassType(enum.EnumMeta<5>))
14401440
==>
14411441
TypeInfo<0>(
14421442
Name(target.A)
14431443
Bases(enum.Enum<1>)
14441444
Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
14451445
Names(
1446-
X<3> (builtins.int<4>)
1447-
Y<6> (builtins.int<4>))
1446+
X<3> (Literal[0]?<4>)
1447+
Y<6> (Literal[1]?<4>))
14481448
MetaclassType(enum.EnumMeta<5>))
14491449

14501450
[case testLiteralMerge]

0 commit comments

Comments
 (0)