Repository navigation
Cannot define Enum class with a mixin class derived from ABC #119946
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Jun 2, 2024 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directory
on Jun 2, 2024 @nineteendo Can you come up with a case where it would break user code? It seems pretty safe to me.
If you do not use an ABC-derived mixin with
Enum, the only change is a few harmless additional ABC-related members of the metaclass for anEnum. None of the ABC machinery will fire, and the resultingEnumclass itself will be unchanged.If you do use an ABC-derived mixin, how did you get it to work until now? If you used the
@_simple_enumdecorator, nothing changed and it will still work. If you used the simple work-around of defining an empty metaclass that inherits from bothABCMetaandEnumType, everything will still work. Same if you used one of the older workarounds where you define a custom metaclass with some methods and use that.If you did some really deep metaclass hacking that modifies the way ABCs work AND you didn't override all of the metaclass methods AND you use your custom ABC machinery together with an
Enumclass, then this might require some tweaking. It seems to me that this very specialized corner case for very advanced users is a small cost compared to the surprising common failure we are fixing.If you can construct a plausible concrete case where this might actually break user code, it's likely we can tweak this solution to provide backwards compatibility.
We have this in
enum.py:EnumMeta = EnumType # keep EnumMeta name for backwards compatibility class Enum(metaclass=EnumType): ...
So in my example, we have that
Flowers.ROSESis an instance of the classFlowers, which is a subclass ofEnum. But we havetype(Flowers)isEnumType- which is a bit weird, because usually the type of class objects istype. That allows theFlowersclass object to have methods that provide enum features. When definingFlowerswith functional syntax in typed Python, you would writeFlowers: EnumType = Enum('Flowers', ...).After this change,
Flowersis also a descendant ofABCMeta. The effect of that is that there are a few ABC-related members (such as_abc_impl) that are only used when you use it as a metaclass to create an ABC or a subclass of an ABC. To use an ABC as a mixin for the enum - that is exactly what you need. In any other case, they are not used.Reacted by Nice ZombiesThanks, I think
EnumTypeis a confusing name for the meta class ofEnum.EnumTypesounds likeEnumType = Enumto me, like all types intypes.Flowers.ROSESis an instance ofFlowers, which is a subclass ofEnum.Enumis an instance ofEnumType, which is a subclass ofABCMeta.Reacted by ygaleAgreed. I am guessing that the motivation behind the name change was that it is now also used as a type in typed Python.
One of the big concerns for enums are their creation times. We'll need performance numbers with and without the ABC addition.
Reacted by Nice Zombies and ygaleI think that we should not complicate
EnumTypewhich is very complex as it is.But, instead you can solve this by adding several lines to your code:
>>> import abc >>> import enum >>> class A(abc.ABC): ... ... >>> class MyEnum(A, enum.Enum): ... a = A() ... Traceback (most recent call last): File "<python-input-3>", line 1, in <module> class MyEnum(A, enum.Enum): a = A() TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
Solution:
>>> class ABCEnum(abc.ABCMeta, enum.EnumType): ... ... ... >>> class MyEnum(A, enum.Enum, metaclass=ABCEnum): ... a = A() ... >>> MyEnum.a <MyEnum.a: <__main__.A object at 0x105210f80>>
Reacted by Alex Waygood, Zach Norris and Léo Bernard@sobolevn We are not complicating
EnumTypeat all. it's essentially a simple one-line change. And on the contrary, I think forcing users to hack on metaclasses for a simple use case is not reasonable.Reacted by Konstantinos KogkalidisOne of the big concerns for enums are their creation times. We'll need performance numbers with and without the ABC addition.
Yep, will do. I'm also considering disabling this for construction via the
@_simple_enumdecorator. But let's see what the benchmarks say.I found some discussion (in which you participated) in #93910. Any other pointers?
Another point that arises from #93910. In my example in the description, and also in my tests, I implemented the
color()method in what I thought was the simplest way:match self ... case self.ROSES .... But that uses theColor.RED.REDbehavior which now I see is controversial. I'll change them to match onself.name.@ygale Using
self.ROSESis the preferred way; I was stuck in a purist mode when I made those arguments, but practicality won out in the end (thank goodness!).Reacted by ygaleIn addition to enum creation time, Python start-up time is a concern.
Reacted by ygale@ethanfurman OK. While I'm at it, I might as well profile member access time as well.
I am having a similar problem, but when the base class is a
typing.Protocolbut all the enum members are implementations of that protocol. @sobolevn 's workaround doesn't seem to work in this case:from enum import Enum, EnumType from typing import Protocol class Example(Protocol): def method(self) -> None: ... class Impl(Example): def method(self) -> None: ... Impl() # OK class CompatEnumType(type(Protocol), EnumType): ... class ProtoEnum(Example, Enum, metaclass=CompatEnumType): impl = Impl() # ERROR 'Protocols cannot be instantiated'
Reacted by sobolevn and Finite State Machine- marked Cannot subclass from Protocol and Enum at the same time. #149828 as a duplicate of this issue
on May 15, 2026 - unmarked Cannot subclass from Protocol and Enum at the same time. #149828 as a duplicate of this issue
on May 15, 2026
Bug report
Bug description:
This results in:
There is a work-around by metaclass hacking. But these two basic Python features are expected to Just Work together in a simple, intuitive, and pythonic way.
The fix should be very simple - make
EnumTypea subclass ofABCMeta. But it's slightly more complicated than that, so that the_simple_enumdecorator and_test_simple_enumcontinue to work as expected. See the provided patch.CPython versions tested on:
3.12, 3.13
Operating systems tested on:
macOS
Linked PRs