The built-in enum provides a way to create enums of primitive types (IntEnum, StrEnum). I'd like to create an enum of structured objects. One way to do that is with dataclass, and it works: from dataclasses import dataclass from enum import Enum from typing_extensions import assert_never @dataclass(frozen=True) class _BaseInstTypeDataclass: instance_type: str display_name: str class InstTypeDataClass(_BaseInstTypeDataclass, Enum): t2_micro = ("t2.micro", "t2.micro: Cheap!") r7i_2xlarge = ("r7i.2xlarge", "r7i.2xlarge: Expensive!") assert list(InstTypeDataClass) == [ InstTypeDataClass.t2_micro, InstTypeDataClass.r7i_2xlarge, ] assert isinstance(InstTypeDataClass.t2_micro, InstTypeDataClass) # This function type checks def f_dataclass(e: InstTypeDataClass): if e == InstTypeDataClass.t2_micro: ... elif e == InstTypeDataClass.r7i_2xlarge: ... else: assert_never(e) Both the static- (via pyright) and runtime-behavior is as expected. However, with attrs...: import attrs @attrs.define(frozen=True) class _BaseInstTypeAttrs: instance_type: str display_name: str class InstTypeAttrs(_BaseInstTypeAttrs, Enum): t2_micro = ("t2.micro", "t2.micro: Cheap!") r7i_2xlarge = ("r7i.2xlarge", "r7i.2xlarge: Expensive!") # This function type checks def f_attrs(e: InstTypeAttrs): if e == InstTypeAttrs.t2_micro: ... elif e == InstTypeAttrs.r7i_2xlarge: ... else: assert_never(e) ... the type checker is happy, but at run-time...: $ python foo.py _BaseInstTypeDataclass(instance_type='foo', display_name='bar') Traceback (most recent call last): File "foo.py", line 58, in <module> class InstTypeAttrs(_BaseInstTypeAttrs, Enum): File "/Users/.../python3.10/enum.py", line 287, in __new__ enum_member._value_ = value File "/Users/.../python3.10/site-packages/attr/_make.py", line 551, in _frozen_setattrs raise FrozenInstanceError() attr.exceptions.FrozenInstanceError Is this because... Enum and attrs.define(frozen=True) don't play nice...? Continue reading...