Skip to content
6 changes: 3 additions & 3 deletions mypy/checkmember.py
Original file line number Diff line number Diff line change
Expand Up @@ -1349,9 +1349,6 @@ def analyze_enum_class_attribute_access(
# Skip these since Enum will remove it
if name in EXCLUDED_ENUM_ATTRIBUTES:
return report_missing_attribute(mx.original_type, itype, name, mx)
# Dunders and private names are not Enum members
if name.startswith("__") and name.replace("_", "") != "":
return None

node = itype.type.get(name)
if node and node.type:
Expand All @@ -1364,6 +1361,9 @@ def analyze_enum_class_attribute_access(
):
return proper.args[0]

if name not in itype.type.enum_members:
return None

enum_literal = LiteralType(name, fallback=itype)
return itype.copy_modified(last_known_value=enum_literal)

Expand Down
2 changes: 1 addition & 1 deletion mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ def analyze_unbound_type_without_type_info(
isinstance(sym.node, Var)
and sym.node.info
and sym.node.info.is_enum
and not sym.node.name.startswith("__")
and sym.node.name in sym.node.info.enum_members
):
value = sym.node.name
base_enum_short_name = sym.node.info.name
Expand Down
50 changes: 49 additions & 1 deletion test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,8 @@ class F(Generic[T], Enum): # E: Enum class cannot be generic
x: T
y: T

reveal_type(F[int].x) # N: Revealed type is "__main__.F[builtins.int]"
reveal_type(F[int].x) # E: Access to generic instance variables via class is ambiguous \
# N: Revealed type is "builtins.int"
[builtins fixtures/enum.pyi]

[case testEnumFlag]
Expand Down Expand Up @@ -1422,6 +1423,53 @@ class Comparator(enum.Enum):
reveal_type(Comparator.__foo__) # N: Revealed type is "builtins.dict[builtins.str, builtins.int]"
[builtins fixtures/dict.pyi]

[case testEnumClassAttributeUnannotated]
import enum
from typing import ClassVar, Literal

class MyEnum(enum.Enum):
foo: ClassVar[str]
bar: str

VALUE_A = 1
VALUE_B = 2

reveal_type(MyEnum.foo) # N: Revealed type is "builtins.str"
reveal_type(MyEnum.bar) # N: Revealed type is "builtins.str"
x: Literal[MyEnum.foo] # E: Parameter 1 of Literal[...] is invalid
y: Literal[MyEnum.bar] # E: Parameter 1 of Literal[...] is invalid
[builtins fixtures/enum.pyi]

[case testEnumAttributesInheritedFromMixin]
from enum import Enum
from typing import TYPE_CHECKING, ClassVar, Final, Literal

class A:
var1 = 1
var2: ClassVar[str]
var3: Final[int] = 3
var4: str

class E(A, Enum):
mem = 1

reveal_type(E.var1) # N: Revealed type is "builtins.int"
reveal_type(E.var2) # N: Revealed type is "builtins.str"
reveal_type(E.var3) # N: Revealed type is "builtins.int"
reveal_type(E.var4) # N: Revealed type is "builtins.str"
reveal_type(E.mem) # N: Revealed type is "Literal[__main__.E.mem]?"

E.var1.value # E: "int" has no attribute "value"
E.var2.name # E: "str" has no attribute "name"
E.mem.name

x1: Literal[E.var1] # E: Parameter 1 of Literal[...] is invalid
x2: Literal[E.var2] # E: Parameter 1 of Literal[...] is invalid
x3: Literal[E.var3] # E: Parameter 1 of Literal[...] is invalid
x4: Literal[E.var4] # E: Parameter 1 of Literal[...] is invalid
m: Literal[E.mem]
[builtins fixtures/enum.pyi]

[case testEnumWithInstanceAttributes]
from enum import Enum
class Foo(Enum):
Expand Down