Skip to content

Commit

Permalink
Fix false-positive error on multiple enum base classes (#12963)
Browse files Browse the repository at this point in the history
Fixes #12787.

Mypy currently emits a false-positive error for this snippet of code, even though it works fine at runtime (and this exact inheritance structure is used in enum.py in the stdlib):

```
from enum import Enum, Flag
class ReprEnum(Enum): ...
class MyFlag(ReprEnum, Flag): ...
```

This PR fixes that.
  • Loading branch information
AlexWaygood committed Jul 8, 2022
1 parent b0c1556 commit 1fedf2c
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 9 deletions.
19 changes: 17 additions & 2 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1965,14 +1965,29 @@ def is_final_enum_value(self, sym: SymbolTableNode) -> bool:
return False

def check_enum_bases(self, defn: ClassDef) -> None:
"""
Non-enum mixins cannot appear after enum bases; this is disallowed at runtime:
class Foo: ...
class Bar(enum.Enum, Foo): ...
But any number of enum mixins can appear in a class definition
(even if multiple enum bases define __new__). So this is fine:
class Foo(enum.Enum):
def __new__(cls, val): ...
class Bar(enum.Enum):
def __new__(cls, val): ...
class Baz(int, Foo, Bar, enum.Flag): ...
"""
enum_base: Optional[Instance] = None
for base in defn.info.bases:
if enum_base is None and base.type.is_enum:
enum_base = base
continue
elif enum_base is not None:
elif enum_base is not None and not base.type.is_enum:
self.fail(
f'No base classes are allowed after "{enum_base}"',
f'No non-enum mixin classes are allowed after "{enum_base}"',
defn,
)
break
Expand Down
34 changes: 27 additions & 7 deletions test-data/unit/check-enum.test
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,14 @@ class Third:
class Mixin:
pass

class EnumWithCustomNew(enum.Enum):
def __new__(cls, val):
pass

class SecondEnumWithCustomNew(enum.Enum):
def __new__(cls, val):
pass

# Correct Enums:

class Correct0(enum.Enum):
Expand All @@ -1920,6 +1928,9 @@ class Correct3(Mixin, enum.Enum):
class RegularClass(Mixin, First, Second):
pass

class Correct5(enum.Enum):
pass

# Correct inheritance:

class _InheritingDataAndMixin(Correct1):
Expand All @@ -1934,42 +1945,51 @@ class _CorrectWithDataAndMixin(Mixin, First, Correct0):
class _CorrectWithMixin(Mixin, Correct2):
pass

class _CorrectMultipleEnumBases(Correct0, Correct5):
pass

class _MultipleEnumBasesAndMixin(int, Correct0, enum.Flag):
pass

class _MultipleEnumBasesWithCustomNew(int, EnumWithCustomNew, SecondEnumWithCustomNew):
pass

# Wrong Enums:

class TwoDataTypesViaInheritance(Second, Correct2): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Correct2"
pass

class TwoDataTypesViaInheritanceAndMixin(Second, Correct2, Mixin): # E: No base classes are allowed after "__main__.Correct2" \
class TwoDataTypesViaInheritanceAndMixin(Second, Correct2, Mixin): # E: No non-enum mixin classes are allowed after "__main__.Correct2" \
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Correct2"
pass

class MixinAfterEnum1(enum.Enum, Mixin): # E: No base classes are allowed after "enum.Enum"
class MixinAfterEnum1(enum.Enum, Mixin): # E: No non-enum mixin classes are allowed after "enum.Enum"
pass

class MixinAfterEnum2(First, enum.Enum, Mixin): # E: No base classes are allowed after "enum.Enum"
class MixinAfterEnum2(First, enum.Enum, Mixin): # E: No non-enum mixin classes are allowed after "enum.Enum"
pass

class TwoDataTypes(First, Second, enum.Enum): # E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
pass

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

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

class ThreeDataTypesAndMixin(First, Second, Third, enum.Enum, Mixin): # E: No base classes are allowed after "enum.Enum" \
class ThreeDataTypesAndMixin(First, Second, Third, enum.Enum, Mixin): # E: No non-enum mixin classes are allowed after "enum.Enum" \
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second" \
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Third"
pass

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

class FromEnumAndOther2(Correct2, Second): # E: No base classes are allowed after "__main__.Correct2" \
class FromEnumAndOther2(Correct2, Second): # E: No non-enum mixin classes are allowed after "__main__.Correct2" \
# E: Only a single data type mixin is allowed for Enum subtypes, found extra "__main__.Second"
pass
[builtins fixtures/tuple.pyi]
Expand Down

0 comments on commit 1fedf2c

Please sign in to comment.