Skip to content

Commit feab209

Browse files
authored
Fix enum regression #12258 (#12260)
Closes #12258 Closes #12262
1 parent e784648 commit feab209

File tree

2 files changed

+89
-23
lines changed

2 files changed

+89
-23
lines changed

mypy/checker.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,7 @@ def visit_class_def(self, defn: ClassDef) -> None:
18381838
if base.is_enum and base.fullname not in ENUM_BASES:
18391839
self.check_final_enum(defn, base)
18401840
self.check_enum_bases(defn)
1841+
self.check_enum_new(defn)
18411842

18421843
def check_final_deletable(self, typ: TypeInfo) -> None:
18431844
# These checks are only for mypyc. Only perform some checks that are easier
@@ -1934,9 +1935,8 @@ def is_final_enum_value(self, sym: SymbolTableNode) -> bool:
19341935

19351936
def check_enum_bases(self, defn: ClassDef) -> None:
19361937
enum_base: Optional[Instance] = None
1937-
data_base: Optional[Instance] = None
19381938
for base in defn.info.bases:
1939-
if enum_base is None and base.type.fullname in ENUM_BASES:
1939+
if enum_base is None and base.type.is_enum:
19401940
enum_base = base
19411941
continue
19421942
elif enum_base is not None:
@@ -1946,23 +1946,36 @@ def check_enum_bases(self, defn: ClassDef) -> None:
19461946
)
19471947
break
19481948

1949-
# This might not be 100% correct, because runtime `__new__`
1950-
# and `__new__` from `typeshed` are sometimes different,
1951-
# but it is good enough.
1952-
new_method = base.type.get('__new__')
1953-
if (data_base is None
1954-
and enum_base is None # data type is always before `Enum`
1955-
and new_method
1956-
and new_method.node
1957-
and new_method.node.fullname != 'builtins.object.__new__'):
1958-
data_base = base
1959-
continue
1960-
elif data_base is not None:
1949+
def check_enum_new(self, defn: ClassDef) -> None:
1950+
def has_new_method(info: TypeInfo) -> bool:
1951+
new_method = info.get('__new__')
1952+
return bool(
1953+
new_method
1954+
and new_method.node
1955+
and new_method.node.fullname != 'builtins.object.__new__'
1956+
)
1957+
1958+
has_new = False
1959+
for base in defn.info.bases:
1960+
candidate = False
1961+
1962+
if base.type.is_enum:
1963+
# If we have an `Enum`, then we need to check all its bases.
1964+
candidate = any(
1965+
not b.is_enum and has_new_method(b)
1966+
for b in base.type.mro[1:-1]
1967+
)
1968+
else:
1969+
candidate = has_new_method(base.type)
1970+
1971+
if candidate and has_new:
19611972
self.fail(
19621973
'Only a single data type mixin is allowed for Enum subtypes, '
19631974
'found extra "{}"'.format(base),
19641975
defn,
19651976
)
1977+
elif candidate:
1978+
has_new = True
19661979

19671980
def check_protocol_variance(self, defn: ClassDef) -> None:
19681981
"""Check that protocol definition is compatible with declared

test-data/unit/check-enum.test

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,9 @@ class Mixin:
19051905

19061906
# Correct Enums:
19071907

1908+
class Correct0(enum.Enum):
1909+
pass
1910+
19081911
class Correct1(Mixin, First, enum.Enum):
19091912
pass
19101913

@@ -1917,8 +1920,29 @@ class Correct3(Mixin, enum.Enum):
19171920
class RegularClass(Mixin, First, Second):
19181921
pass
19191922

1923+
# Correct inheritance:
1924+
1925+
class _InheritingDataAndMixin(Correct1):
1926+
pass
1927+
1928+
class _CorrectWithData(First, Correct0):
1929+
pass
1930+
1931+
class _CorrectWithDataAndMixin(Mixin, First, Correct0):
1932+
pass
1933+
1934+
class _CorrectWithMixin(Mixin, Correct2):
1935+
pass
1936+
19201937
# Wrong Enums:
19211938

1939+
class TwoDataTypesViaInheritance(Second, Correct2): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Correct2"
1940+
pass
1941+
1942+
class TwoDataTypesViaInheritanceAndMixin(Second, Correct2, Mixin): # E: No base classes are allowed after "__main__.Correct2" \
1943+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Correct2"
1944+
pass
1945+
19221946
class MixinAfterEnum1(enum.Enum, Mixin): # E: No base classes are allowed after "enum.Enum"
19231947
pass
19241948

@@ -1928,27 +1952,56 @@ class MixinAfterEnum2(First, enum.Enum, Mixin): # E: No base classes are allowe
19281952
class TwoDataTypes(First, Second, enum.Enum): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
19291953
pass
19301954

1931-
class TwoDataTypesAndIntEnumMixin(First, Second, enum.IntEnum, Mixin): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second" \
1932-
# E: No base classes are allowed after "enum.IntEnum"
1955+
class TwoDataTypesAndIntEnumMixin(First, Second, enum.IntEnum, Mixin): # E: No base classes are allowed after "enum.IntEnum" \
1956+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
19331957
pass
19341958

19351959
class ThreeDataTypes(First, Second, Third, enum.Enum): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second" \
1936-
# E: # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Third"
1960+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Third"
19371961
pass
19381962

1939-
class ThreeDataTypesAndMixin(First, Second, Third, enum.Enum, Mixin): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second" \
1940-
# E: # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Third" \
1941-
# E: No base classes are allowed after "enum.Enum"
1963+
class ThreeDataTypesAndMixin(First, Second, Third, enum.Enum, Mixin): # E: No base classes are allowed after "enum.Enum" \
1964+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second" \
1965+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Third"
19421966
pass
19431967

1944-
class FromEnumAndOther1(Correct2, Second, enum.Enum): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
1968+
class FromEnumAndOther1(Correct2, Second, enum.Enum): # E: No base classes are allowed after "__main__.Correct2" \
1969+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
19451970
pass
19461971

1947-
class FromEnumAndOther2(Correct2, Second): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
1972+
class FromEnumAndOther2(Correct2, Second): # E: No base classes are allowed after "__main__.Correct2" \
1973+
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
19481974
pass
19491975
[builtins fixtures/tuple.pyi]
19501976

1951-
[case testEnumtValueUnionSimplification]
1977+
[case testRegression12258]
1978+
from enum import Enum
1979+
1980+
class MyEnum(Enum): ...
1981+
1982+
class BytesEnum(bytes, MyEnum): ... # Should be ok
1983+
[builtins fixtures/tuple.pyi]
1984+
1985+
[case testEnumWithNewHierarchy]
1986+
import enum
1987+
1988+
class A:
1989+
def __new__(cls, val): ...
1990+
class B(A):
1991+
def __new__(cls, val): ...
1992+
class C:
1993+
def __new__(cls, val): ...
1994+
1995+
class E1(A, enum.Enum): ...
1996+
class E2(B, enum.Enum): ...
1997+
1998+
# Errors:
1999+
2000+
class W1(C, E1): ... # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.E1"
2001+
class W2(C, E2): ... # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.E2"
2002+
[builtins fixtures/tuple.pyi]
2003+
2004+
[case testEnumValueUnionSimplification]
19522005
from enum import IntEnum
19532006
from typing import Any
19542007

0 commit comments

Comments
 (0)