When working with enums in Python, you might want to create an enum that is case-insensitive. This can be particularly useful when you want to allow users to input values without worrying about the case. Here’s how you can achieve this by subclassing StrEnum. To enforce that all enum members are defined in lowercase, we can use a hook __init_subclass__.
Implementation
Show the code
from __future__ import annotationsimport sysif sys.version_info >= (3, 11):from enum import StrEnumfrom typing import Self, overrideelse:from backports.strenum import StrEnumfrom typing_extensions import Self, overrideclass CaseInsensitiveStrEnum(StrEnum):"""A case-insensitive string enum. This class extends the `StrEnum` class to provide case-insensitive member lookup. It also provides a custom `_generate_next_value_` method that returns the lowercase version of the member name for use with `auto()`. """@classmethoddef__init_subclass__(cls, **kwargs: dict[str, Any]) ->None:"""Implement the special hook that is called when a class is subclassed. It allows us to customize the class creation process without using metaclasses. """super().__init_subclass__(**kwargs)for member in cls:ifnot member.islower() ornot member.value.islower(): msg =f"Member '{member}' and value {member.value} must be lowercase."raiseTypeError(msg)@override@staticmethoddef _generate_next_value_( name: str, start: int, count: int, last_values: list[str] ) ->str:"""Return the lower-cased version of the member name. This method is overridden to ensure that when `auto()` is used to create enum members, their values are the lowercase version of their names. """return name.lower()@override@classmethoddef _missing_(cls, value: object) -> Self |None:"""Provide case-insensitive member lookup. This method is called when a value is not found in the enum. It is overridden to perform a case-insensitive search for the member. """ifnotisinstance(value, str):returnNone value = value.lower()for member in cls:if member.name.lower() == value:return memberreturnNone
Example Usage
Here is an example of how to use the CaseInsensitiveStrEnum class:
Show the code
from enum import autoclass TestEnum(CaseInsensitiveStrEnum): test = auto() enumz = auto()print(f"List of members: {[member.name for member in TestEnum]}")print(f"List of values: {[member.value for member in TestEnum]}")print(f'Accessing "Test": {TestEnum("Test")}')print(f'Accessing "test": {TestEnum("test")}')print(f'Accessing "ENUMZ": {TestEnum("ENUMZ")}')try:class TestEnum(CaseInsensitiveStrEnum): TEST ="TEST"# This will raise a TypeErrorexceptTypeErroras e:print(e)
List of members: ['test', 'enumz']
List of values: ['test', 'enumz']
Accessing "Test": test
Accessing "test": test
Accessing "ENUMZ": enumz
Member 'TEST' and value TEST must be lowercase.