Skip to content

Commit 6f11f35

Browse files
elazargmsullivan
authored andcommitted
Use EnumMeta instead of Enum to mark enum classes (#4319)
Implement #4311.
1 parent 1fd5a30 commit 6f11f35

File tree

6 files changed

+48
-27
lines changed

6 files changed

+48
-27
lines changed

mypy/nodes.py

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,6 @@ def get_column(self) -> int:
8585
LITERAL_TYPE = 1
8686
LITERAL_NO = 0
8787

88-
# Hard coded name of Enum baseclass.
89-
ENUM_BASECLASS = "enum.Enum"
90-
9188
node_kinds = {
9289
LDEF: 'Ldef',
9390
GDEF: 'Gdef',
@@ -2152,7 +2149,6 @@ def calculate_mro(self) -> None:
21522149
mro = linearize_hierarchy(self)
21532150
assert mro, "Could not produce a MRO at all for %s" % (self,)
21542151
self.mro = mro
2155-
self.is_enum = self._calculate_is_enum()
21562152
# The property of falling back to Any is inherited.
21572153
self.fallback_to_any = any(baseinfo.fallback_to_any for baseinfo in self.mro)
21582154
self.reset_subtype_cache()
@@ -2178,17 +2174,6 @@ def is_metaclass(self) -> bool:
21782174
return (self.has_base('builtins.type') or self.fullname() == 'abc.ABCMeta' or
21792175
self.fallback_to_any)
21802176

2181-
def _calculate_is_enum(self) -> bool:
2182-
"""
2183-
If this is "enum.Enum" itself, then yes, it's an enum.
2184-
If the flag .is_enum has been set on anything in the MRO, it's an enum.
2185-
"""
2186-
if self.fullname() == ENUM_BASECLASS:
2187-
return True
2188-
if self.mro:
2189-
return any(type_info.is_enum for type_info in self.mro)
2190-
return False
2191-
21922177
def has_base(self, fullname: str) -> bool:
21932178
"""Return True if type has a base type with the specified name.
21942179

mypy/semanal.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,8 +1085,6 @@ def analyze_base_classes(self, defn: ClassDef) -> None:
10851085
# the MRO. Fix MRO if needed.
10861086
if info.mro and info.mro[-1].fullname() != 'builtins.object':
10871087
info.mro.append(self.object_type().type)
1088-
if defn.info.is_enum and defn.type_vars:
1089-
self.fail("Enum class cannot be generic", defn)
10901088

10911089
def update_metaclass(self, defn: ClassDef) -> None:
10921090
"""Lookup for special metaclass declarations, and update defn fields accordingly.
@@ -1223,6 +1221,11 @@ def analyze_metaclass(self, defn: ClassDef) -> None:
12231221
# do not declare explicit metaclass, but it's harder to catch at this stage
12241222
if defn.metaclass is not None:
12251223
self.fail("Inconsistent metaclass structure for '%s'" % defn.name, defn)
1224+
else:
1225+
if defn.info.metaclass_type.type.has_base('enum.EnumMeta'):
1226+
defn.info.is_enum = True
1227+
if defn.type_vars:
1228+
self.fail("Enum class cannot be generic", defn)
12261229

12271230
def object_type(self) -> Instance:
12281231
return self.named_type('__builtins__.object')

mypy/subtypes.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,6 @@ def visit_instance(self, left: Instance) -> bool:
183183
if isinstance(item, AnyType):
184184
return True
185185
if isinstance(item, Instance):
186-
# Special-case enum since we don't have better way of expressing it
187-
if (is_named_instance(left, 'enum.EnumMeta')
188-
and is_named_instance(item, 'enum.Enum')):
189-
return True
190186
return is_named_instance(item, 'builtins.object')
191187
if isinstance(right, CallableType):
192188
# Special case: Instance can be a subtype of Callable.

test-data/unit/check-enum.test

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,42 @@ class Medal(Enum):
66
gold = 1
77
silver = 2
88
bronze = 3
9+
reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal'
910
m = Medal.gold
10-
m = 1
11-
[out]
12-
main:7: error: Incompatible types in assignment (expression has type "int", variable has type "Medal")
11+
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal")
12+
13+
[case testEnumFromEnumMetaBasics]
14+
from enum import EnumMeta
15+
class Medal(metaclass=EnumMeta):
16+
gold = 1
17+
silver = "hello"
18+
bronze = None
19+
# Without __init__ the definition fails at runtime, but we want to verify that mypy
20+
# uses `enum.EnumMeta` and not `enum.Enum` as the definition of what is enum.
21+
def __init__(self, *args): pass
22+
reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal'
23+
m = Medal.gold
24+
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal")
25+
26+
[case testEnumFromEnumMetaSubclass]
27+
from enum import EnumMeta
28+
class Achievement(metaclass=EnumMeta): pass
29+
class Medal(Achievement):
30+
gold = 1
31+
silver = "hello"
32+
bronze = None
33+
# See comment in testEnumFromEnumMetaBasics
34+
def __init__(self, *args): pass
35+
reveal_type(Medal.bronze) # E: Revealed type is '__main__.Medal'
36+
m = Medal.gold
37+
m = 1 # E: Incompatible types in assignment (expression has type "int", variable has type "Medal")
38+
39+
[case testEnumFromEnumMetaGeneric]
40+
from enum import EnumMeta
41+
from typing import Generic, TypeVar
42+
T = TypeVar("T")
43+
class Medal(Generic[T], metaclass=EnumMeta): # E: Enum class cannot be generic
44+
q = None
1345

1446
[case testEnumNameAndValue]
1547
from enum import Enum

test-data/unit/lib-stub/enum.pyi

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
from typing import Any, TypeVar, Union
22

3-
class Enum:
3+
class EnumMeta(type):
4+
pass
5+
6+
class Enum(metaclass=EnumMeta):
47
def __new__(cls, value: Any) -> None: pass
58
def __repr__(self) -> str: pass
69
def __str__(self) -> str: pass

test-data/unit/merge.test

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,12 +1440,14 @@ TypeInfo<0>(
14401440
Bases(enum.Enum<1>)
14411441
Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
14421442
Names(
1443-
X<3> (builtins.int<4>)))
1443+
X<3> (builtins.int<4>))
1444+
MetaclassType(enum.EnumMeta<5>))
14441445
==>
14451446
TypeInfo<0>(
14461447
Name(target.A)
14471448
Bases(enum.Enum<1>)
14481449
Mro(target.A<0>, enum.Enum<1>, builtins.object<2>)
14491450
Names(
14501451
X<3> (builtins.int<4>)
1451-
Y<5> (builtins.int<4>)))
1452+
Y<6> (builtins.int<4>))
1453+
MetaclassType(enum.EnumMeta<5>))

0 commit comments

Comments
 (0)