diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 2c41f2e273cc..9644edcfcd07 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -1345,9 +1345,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: @@ -1360,6 +1357,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 204d3061c734..afe4a387f83d 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):