Introducing basicenum

In the summer of 2022, my partner was taking her machine learning course as part of UBC's Key Capabilities in Data Science certificate. I was Andrea's on-call tutor for any Python questions, so while Andrea was listening to lectures I decided to do a small project that I thought I could complete during the course.

At the time, the Python steering council had received a couple of asks on backwards-compatibility related to the enum module. I had also been told anecdotally that the enum module didn't perform fast enough for some to want to use it (typically around import costs). I had a look at the source code and noticed it was over 2000 lines long and used sys._getframe(). Obviously the enum module has multiple classes and such with a lot of subtle details to it, so it isn't necessarily a small API, but the use of sys._getframe() made me want to see if I could replicate API for enum.Enum in less code.

In the end, I got most of the API implemented in less than 200 lines via basicenum.compat.Enum. I couldn't get type(enum.variant) and restricted subclassing to work, but I managed to get everything else working (that I can think of; as I said, the API is surprisingly subtle). And according to benchmarking, creation – and thus importing – is way faster, while enum variant access and comparison – i.e. attribute access and equality – are the same.

The really tricky bit with this whole endeavour, though, is typing. Enums are special-cased by type checkers. And while @typing.dataclass_transform() exists to help make dataclass-like packages be treated like dataclass itself, no such decorator exists for enums. As such, you effectively have to lie to the type checkers that basicenum.compat.Enum is equivalent to enum.Enum during type checking:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from enum import Enum, auto
else:
    from basicenum.compat import Enum, auto

Have type checkers check against enum while execution uses basicenum.compat

As long as you don't rely on type(enum.variant) to be the same as how enum.Enum works, this trick should work (once again, assuming I didn't miss anything).

I honestly don't have any further plans for this package. While I namespaced things such that if I decided to create an enum type that wasn't compatible with enum.Enum to see how simple and/or fast I could make it I could without people getting confused as to what enum type is which, I have no plans to pursue that idea. But it was at least a fun challenge to see if I could at least pull off my goal of re-implementing most of enum.Enum.