The case for StrEnum in Python 3.11.

With the update to Python 3.11, we ran into a subtle change in how Enum’s behave. This is thanks to PEP 663 - Standardizing Enum str(), repr(), and format() behaviors.

Before Python 3.11

Before Python 3.11, a string enum as shown below would return the value of an entry in the enum when used via format or an f-string but not when implicitly calling __str__().

# Python 3.10

from enum import Enum

class Foo(str, Enum):
    BAR = "bar"

x = Foo.BAR

x               # Outputs <Foo.BAR: 'bar'>
f"{x}"          # Outputs 'bar'
"{}".format(x)  # Outputs 'bar'
str(x)          # Outputs 'Foo.BAR'
x.value         # Outputs 'bar'

Python 3.11

In Python 3.11, the difference in using an Enum entry in a string context was changed, so now it returns the stringified reference instead. In our codebase, we had to change the use of enum entries to explicitly call Foo.BAR.value wherever we had used an enum entry in a format context.

# Python 3.11

from enum import Enum

class Foo(str, Enum):
    BAR = "bar"

x = Foo.BAR

x               # Outputs <Foo.BAR: 'bar'>
f"{x}"          # Outputs 'Foo.BAR'
"{}".format(x)  # Outputs 'Foo.BAR'
str(x)          # Outputs 'Foo.BAR'
x.value         # Outputs 'bar'

StrEnum to the rescue

In Python 3.11, StrEnum was added to the standard library. Using that instead of the aforementioned style of enums makes for more obvious behaviour.

# Python 3.11

from enum import StrEnum, auto

class Foo(StrEnum):
    BAR = auto()

x = Foo.BAR

x               # Outputs <Foo.BAR: 'bar'>
f"{x}"          # Outputs 'bar'
"{}".format(x)  # Outputs 'bar'
str(x)          # Outputs 'bar'
x.value         # Outputs 'bar'

Thanks to Lucy Linder for making me aware of the addition of StrEnum to the standard library in 3.11.