from __future__ import annotations
import sys
if sys.version_info >= (3, 11):
from enum import StrEnum
from typing import Self, override
else:
from backports.strenum import StrEnum
from typing_extensions import Self, override
class 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()`.
"""
@classmethod
def __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:
if not member.islower() or not member.value.islower():
msg = f"Member '{member}' and value {member.value} must be lowercase."
raise TypeError(msg)
@override
@staticmethod
def _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
@classmethod
def _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.
"""
if not isinstance(value, str):
return None
value = value.lower()
for member in cls:
if member.name.lower() == value:
return member
return NoneWhen 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
Example Usage
Here is an example of how to use the CaseInsensitiveStrEnum class:
from enum import auto
class 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 TypeError
except TypeError as 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.
Note
Download the whole code here.