diff --git a/mypy/checkmember.py b/mypy/checkmember.py index f19a76ec6a34..7036b7165726 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1347,9 +1347,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: @@ -1362,6 +1359,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) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 81fb87fbf9ee..04e986491fa5 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -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 diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test index 3bcf9745a801..95105905806f 100644 --- a/test-data/unit/check-enum.test +++ b/test-data/unit/check-enum.test @@ -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] @@ -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):