Skip to content

Commit 8eb4218

Browse files
committed
Fix enum attributes are not members
This adds on to the change in #17182 and fixes enum attributes being used as members.
1 parent fbaa7e0 commit 8eb4218

File tree

4 files changed

+71
-14
lines changed

4 files changed

+71
-14
lines changed

mypy/semanal_enum.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ def build_enum_call_typeinfo(
143143
var = Var(item)
144144
var.info = info
145145
var.is_property = True
146+
# When an enum is created by its functional form `Enum(name, values)`
147+
# - if it is a string it is first split by commas/whitespace
148+
# - if it is an iterable of single items each item is assigned a value starting at `start`
149+
# - if it is an iterable of (name, value) then the given values will be used
150+
# either way, each item should be treated as if it has an explicit value.
151+
var.has_explicit_value = True
146152
var._fullname = f"{info.fullname}.{item}"
147153
info.names[item] = SymbolTableNode(MDEF, var)
148154
return info

mypy/typeops.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
)
3131
from mypy.state import state
3232
from mypy.types import (
33-
ENUM_REMOVED_PROPS,
3433
AnyType,
3534
CallableType,
3635
ExtraAttrs,
@@ -878,18 +877,9 @@ class Status(Enum):
878877
return make_simplified_union(items, contract_literals=False)
879878
elif isinstance(typ, Instance) and typ.type.fullname == target_fullname:
880879
if typ.type.is_enum:
881-
new_items = []
882-
for name, symbol in typ.type.names.items():
883-
if not isinstance(symbol.node, Var):
884-
continue
885-
# Skip these since Enum will remove it
886-
if name in ENUM_REMOVED_PROPS:
887-
continue
888-
# Skip private attributes
889-
if name.startswith("__"):
890-
continue
891-
new_items.append(LiteralType(name, typ))
892-
return make_simplified_union(new_items, contract_literals=False)
880+
return make_simplified_union(
881+
[LiteralType(name, typ) for name in typ.get_enum_values()], contract_literals=False
882+
)
893883
elif typ.type.fullname == "builtins.bool":
894884
return make_simplified_union(
895885
[LiteralType(True, typ), LiteralType(False, typ)], contract_literals=False

mypy/types.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,14 @@ def is_singleton_type(self) -> bool:
15121512
def get_enum_values(self) -> list[str]:
15131513
"""Return the list of values for an Enum."""
15141514
return [
1515-
name for name, sym in self.type.names.items() if isinstance(sym.node, mypy.nodes.Var)
1515+
name
1516+
for name, sym in self.type.names.items()
1517+
if (
1518+
isinstance(sym.node, mypy.nodes.Var)
1519+
and name not in ENUM_REMOVED_PROPS
1520+
and not name.startswith("__")
1521+
and sym.node.has_explicit_value
1522+
)
15161523
]
15171524

15181525

test-data/unit/check-python310.test

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,6 +1499,60 @@ def g(m: Medal) -> int:
14991499
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]"
15001500
return 2
15011501

1502+
1503+
[case testMatchLiteralPatternEnumWithTypedAttribute]
1504+
from enum import Enum
1505+
from typing import NoReturn
1506+
def assert_never(x: NoReturn) -> None: ...
1507+
1508+
class int:
1509+
def __new__(cls, value: int): pass
1510+
1511+
class Medal(int, Enum):
1512+
prize: str
1513+
1514+
def __new__(cls, value: int, prize: str) -> Medal:
1515+
enum = int.__new__(cls, value)
1516+
enum._value_ = value
1517+
enum.prize = prize
1518+
return enum
1519+
1520+
gold = (1, 'cash prize')
1521+
silver = (2, 'sponsorship')
1522+
bronze = (3, 'nothing')
1523+
1524+
m: Medal
1525+
1526+
match m:
1527+
case Medal.gold:
1528+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]"
1529+
case Medal.silver:
1530+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]"
1531+
case Medal.bronze:
1532+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]"
1533+
case _ as unreachable:
1534+
assert_never(unreachable)
1535+
1536+
[builtins fixtures/tuple.pyi]
1537+
1538+
[case testMatchLiteralPatternFunctionalEnum]
1539+
from enum import Enum
1540+
from typing import NoReturn
1541+
def assert_never(x: NoReturn) -> None: ...
1542+
1543+
Medal = Enum('Medal', 'gold silver bronze')
1544+
m: Medal
1545+
1546+
match m:
1547+
case Medal.gold:
1548+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]"
1549+
case Medal.silver:
1550+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver]"
1551+
case Medal.bronze:
1552+
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.bronze]"
1553+
case _ as unreachable:
1554+
assert_never(unreachable)
1555+
15021556
[case testMatchLiteralPatternEnumCustomEquals-skip]
15031557
from enum import Enum
15041558
class Medal(Enum):

0 commit comments

Comments
 (0)