Repository: python/mypy

Issue:
Enums: false-positive "No base classes allowed after `ReprEnum`" error
**Bug Report**

Minimal repro on 3.11 ([playground link](https://mypy-play.net/?mypy=latest&python=3.11&flags=show-error-codes&gist=62551352af494b26b9d2c540cbf4dfac)):
```python
from enum import ReprEnum, Flag
class MyFlag(ReprEnum, Flag): ...
```

Minimal repro on <=3.10 ([playground link](https://mypy-play.net/?mypy=0.940&python=3.10&flags=show-error-codes&gist=ca82dddbe120822a0f6c7459b34784b7)):
```python
from enum import Enum, Flag
class ReprEnum(Enum): ...
class MyFlag(ReprEnum, Flag): ...
```

mypy output for both (with small line-number differences):

```
main.py:4: error: No base classes are allowed after "__main__.ReprEnum"  [misc]
```

**Expected Behavior**

No error should be reported. This use of multiple inheritance works fine at runtime on 3.10 and 3.11. In fact, in 3.11, the source code for the `enum` module in the stdlib uses [exactly this inheritance structure](https://github.com/python/cpython/blob/2e8f721c0f275d9d7c018b4a2a66f053cf34bb36/Lib/enum.py#L1574-L1577):

```python
class ReprEnum(Enum):
    """
    Only changes the repr(), leaving str() and format() to the mixed-in type.
    """

# Much further down...

class IntFlag(int, ReprEnum, Flag, boundary=EJECT):
    """
    Support for integer-based Flags
    """
```

**Your Environment**

I can reproduce this bug [on mypy 0.940+](https://mypy-play.net/?mypy=0.940&python=3.11&flags=show-error-codes&gist=ddb43d68672ad20d807c6312ebad2312) (0.940 was the first released version with @sobolevn's #12026, which introduced this error message).



Discussion:
An even simpler repro that doesn't involve `enum.Flag`:

```python
from enum import Enum

class Foo(Enum): ...
class Bar(Enum): ...
class Baz(Foo, Bar): ...
```

This works fine at runtime, but [mypy says](https://mypy-play.net/?mypy=latest&python=3.10&gist=c64feb26c039e72690a1f0dc620e7512):

```
main.py:5: error: No base classes are allowed after "__main__.Foo"
```

Resolving patch:
diff --git a/mypy/checker.py b/mypy/checker.py
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -1960,14 +1960,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


Test patch:
diff --git a/test-data/unit/check-enum.test b/test-data/unit/check-enum.test
--- a/test-data/unit/check-enum.test
+++ b/test-data/unit/check-enum.test
@@ -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):
@@ -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):
@@ -1934,25 +1945,34 @@ 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
 
@@ -1960,16 +1980,16 @@ class ThreeDataTypes(First, Second, Third, enum.Enum):  # E: Only a single data
                                                         # 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]