From ba435ac0cfcc4d8d84cabe8b4fbe3bc0e8e866a8 Mon Sep 17 00:00:00 2001 From: "Terence D. Honles" Date: Thu, 2 May 2024 17:01:14 +0200 Subject: [PATCH] Fix enum attributes are not members This adds on to the change in https://github.com/python/mypy/pull/17182 and fixes enum attributes being used as members. --- mypy/typeops.py | 16 +++---------- mypy/types.py | 9 +++++++- test-data/unit/check-python310.test | 36 +++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/mypy/typeops.py b/mypy/typeops.py index a59bd3739562a..568014b155e7b 100644 --- a/mypy/typeops.py +++ b/mypy/typeops.py @@ -30,7 +30,6 @@ ) from mypy.state import state from mypy.types import ( - ENUM_REMOVED_PROPS, AnyType, CallableType, ExtraAttrs, @@ -878,18 +877,9 @@ class Status(Enum): return make_simplified_union(items, contract_literals=False) elif isinstance(typ, Instance) and typ.type.fullname == target_fullname: if typ.type.is_enum: - new_items = [] - for name, symbol in typ.type.names.items(): - if not isinstance(symbol.node, Var): - continue - # Skip these since Enum will remove it - if name in ENUM_REMOVED_PROPS: - continue - # Skip private attributes - if name.startswith("__"): - continue - new_items.append(LiteralType(name, typ)) - return make_simplified_union(new_items, contract_literals=False) + return make_simplified_union( + [LiteralType(name, typ) for name in typ.get_enum_values()], contract_literals=False + ) elif typ.type.fullname == "builtins.bool": return make_simplified_union( [LiteralType(True, typ), LiteralType(False, typ)], contract_literals=False diff --git a/mypy/types.py b/mypy/types.py index 5573dc9efe0ee..92377cbbca56c 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1496,7 +1496,14 @@ def is_singleton_type(self) -> bool: def get_enum_values(self) -> list[str]: """Return the list of values for an Enum.""" return [ - name for name, sym in self.type.names.items() if isinstance(sym.node, mypy.nodes.Var) + name + for name, sym in self.type.names.items() + if ( + isinstance(sym.node, mypy.nodes.Var) + and name not in ENUM_REMOVED_PROPS + and not name.startswith("__") + and sym.node.has_explicit_value + ) ] diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index 2b56d2db07a9c..adbdb9c081f8c 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1484,6 +1484,42 @@ def g(m: Medal) -> int: reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" return 2 + +[case testMatchLiteralPatternEnumWithProperty] +from enum import Enum +from typing import NoReturn +def assert_never(x: NoReturn) -> None: ... + +class int: + def __new__(cls, value: int): pass + +class Medal(int, Enum): + prize: str + + def __new__(cls, value: int, prize: str) -> Medal: + enum = int.__new__(cls, value) + enum._value_ = value + enum.prize = prize + return enum + + gold = (1, 'cash prize') + silver = (2, 'sponsorship') + bronze = (3, 'nothing') + +m: Medal + +match m: + case Medal.gold: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]" + case Medal.silver: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]" + case Medal.bronze: + reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]" + case _ as unreachable: + assert_never(unreachable) + +[builtins fixtures/tuple.pyi] + [case testMatchLiteralPatternEnumCustomEquals-skip] from enum import Enum class Medal(Enum):