Skip to content

Commit eef316d

Browse files
authored
Understand the self-destructing nature of Enum._ignore_ (#12128)
1 parent 554606b commit eef316d

File tree

6 files changed

+30
-11
lines changed

6 files changed

+30
-11
lines changed

mypy/checkmember.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
from mypy.types import (
77
Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike,
88
TypeVarLikeType, Overloaded, TypeVarType, UnionType, PartialType, TypeOfAny, LiteralType,
9-
DeletedType, NoneType, TypeType, has_type_vars, get_proper_type, ProperType, ParamSpecType
9+
DeletedType, NoneType, TypeType, has_type_vars, get_proper_type, ProperType, ParamSpecType,
10+
ENUM_REMOVED_PROPS
1011
)
1112
from mypy.nodes import (
1213
TypeInfo, FuncBase, Var, FuncDef, SymbolNode, SymbolTable, Context,
@@ -831,8 +832,8 @@ def analyze_enum_class_attribute_access(itype: Instance,
831832
name: str,
832833
mx: MemberContext,
833834
) -> Optional[Type]:
834-
# Skip "_order_" and "__order__", since Enum will remove it
835-
if name in ("_order_", "__order__"):
835+
# Skip these since Enum will remove it
836+
if name in ENUM_REMOVED_PROPS:
836837
return mx.msg.has_no_attr(
837838
mx.original_type, itype, name, mx.context, mx.module_symbol_table
838839
)

mypy/semanal_enum.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
)
1414
from mypy.semanal_shared import SemanticAnalyzerInterface
1515
from mypy.options import Options
16-
from mypy.types import get_proper_type, LiteralType
16+
from mypy.types import get_proper_type, LiteralType, ENUM_REMOVED_PROPS
1717

1818
# Note: 'enum.EnumMeta' is deliberately excluded from this list. Classes that directly use
1919
# enum.EnumMeta do not necessarily automatically have the 'name' and 'value' attributes.
2020
ENUM_BASES: Final = frozenset((
2121
'enum.Enum', 'enum.IntEnum', 'enum.Flag', 'enum.IntFlag', 'enum.StrEnum',
2222
))
2323
ENUM_SPECIAL_PROPS: Final = frozenset((
24-
'name', 'value', '_name_', '_value_', '_order_', '__order__',
24+
'name', 'value', '_name_', '_value_', *ENUM_REMOVED_PROPS,
2525
# Also attributes from `object`:
2626
'__module__', '__annotations__', '__doc__', '__slots__', '__dict__',
2727
))

mypy/typeops.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
TupleType, Instance, FunctionLike, Type, CallableType, TypeVarLikeType, Overloaded,
1515
TypeVarType, UninhabitedType, FormalArgument, UnionType, NoneType,
1616
AnyType, TypeOfAny, TypeType, ProperType, LiteralType, get_proper_type, get_proper_types,
17-
copy_type, TypeAliasType, TypeQuery, ParamSpecType
17+
copy_type, TypeAliasType, TypeQuery, ParamSpecType,
18+
ENUM_REMOVED_PROPS
1819
)
1920
from mypy.nodes import (
2021
FuncBase, FuncItem, FuncDef, OverloadedFuncDef, TypeInfo, ARG_STAR, ARG_STAR2, ARG_POS,
@@ -715,8 +716,8 @@ class Status(Enum):
715716
for name, symbol in typ.type.names.items():
716717
if not isinstance(symbol.node, Var):
717718
continue
718-
# Skip "_order_" and "__order__", since Enum will remove it
719-
if name in ("_order_", "__order__"):
719+
# Skip these since Enum will remove it
720+
if name in ENUM_REMOVED_PROPS:
720721
continue
721722
new_items.append(LiteralType(name, typ))
722723
# SymbolTables are really just dicts, and dicts are guaranteed to preserve

mypy/types.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@
132132
'typing_extensions.reveal_type',
133133
)
134134

135+
# Attributes that can optionally be defined in the body of a subclass of
136+
# enum.Enum but are removed from the class __dict__ by EnumMeta.
137+
ENUM_REMOVED_PROPS: Final = (
138+
'_ignore_',
139+
'_order_',
140+
'__order__',
141+
)
142+
135143
NEVER_NAMES: Final = (
136144
'typing.NoReturn',
137145
'typing_extensions.NoReturn',

mypyc/irbuild/classdef.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
ClassDef, FuncDef, OverloadedFuncDef, PassStmt, AssignmentStmt, CallExpr, NameExpr, StrExpr,
99
ExpressionStmt, TempNode, Decorator, Lvalue, MemberExpr, RefExpr, TypeInfo, is_class_var
1010
)
11-
from mypy.types import Instance, get_proper_type
11+
from mypy.types import Instance, get_proper_type, ENUM_REMOVED_PROPS
1212
from mypyc.ir.ops import (
1313
Value, Register, Call, LoadErrorValue, LoadStatic, InitStatic, TupleSet, SetAttr, Return,
1414
BasicBlock, Branch, MethodCall, NAMESPACE_TYPE, LoadAddress
@@ -517,8 +517,8 @@ def add_non_ext_class_attr(builder: IRBuilder,
517517
if (
518518
cdef.info.bases
519519
and cdef.info.bases[0].type.fullname == 'enum.Enum'
520-
# Skip "_order_" and "__order__", since Enum will remove it
521-
and lvalue.name not in ('_order_', '__order__')
520+
# Skip these since Enum will remove it
521+
and lvalue.name not in ENUM_REMOVED_PROPS
522522
):
523523
# Enum values are always boxed, so use object_rprimitive.
524524
attr_to_cache.append((lvalue, object_rprimitive))

test-data/unit/check-enum.test

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,3 +1902,12 @@ def f2(c: C, a: Any) -> None:
19021902
y = {'y': a, 'x': c.value}
19031903
reveal_type(y) # N: Revealed type is "builtins.dict[builtins.str*, Any]"
19041904
[builtins fixtures/dict.pyi]
1905+
1906+
[case testEnumIgnoreIsDeleted]
1907+
from enum import Enum
1908+
1909+
class C(Enum):
1910+
_ignore_ = 'X'
1911+
1912+
C._ignore_ # E: "Type[C]" has no attribute "_ignore_"
1913+
[typing fixtures/typing-medium.pyi]

0 commit comments

Comments
 (0)