From d4e6d44e1d769111344269979e1f23453fbf6f5b Mon Sep 17 00:00:00 2001 From: C1-BA-B1-F3 Date: Sat, 27 Jun 2026 09:20:16 +0800 Subject: [PATCH] Fix attrs.frozen with enum.Enum subclasses When creating an Enum subclass of a frozen attrs class, the frozen __setattr__ would prevent Enum from creating members. Now, Enum instances can bypass the frozen check to allow proper member creation. Fixes #1287 --- changelog.d/1287.change.md | 3 ++ src/attr/_make.py | 6 ++++ tests/test_functional.py | 66 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 changelog.d/1287.change.md diff --git a/changelog.d/1287.change.md b/changelog.d/1287.change.md new file mode 100644 index 000000000..22b38c912 --- /dev/null +++ b/changelog.d/1287.change.md @@ -0,0 +1,3 @@ +Fixed `attrs.frozen` classes not working with `enum.Enum` subclasses. + +When creating an Enum subclass of a frozen attrs class, the frozen `__setattr__` would prevent Enum from creating members. Now, Enum instances can bypass the frozen check to allow proper member creation. diff --git a/src/attr/_make.py b/src/attr/_make.py index 793bfd89d..35d1c8b41 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -570,6 +570,12 @@ def _frozen_setattrs(self, name, value): BaseException.__setattr__(self, name, value) return + # Allow Enum subclasses to set attributes during member creation. + # Enum needs to set _name_, _value_, etc. when creating members. + if isinstance(self, enum.Enum): + object.__setattr__(self, name, value) + return + raise FrozenInstanceError diff --git a/tests/test_functional.py b/tests/test_functional.py index b8dfa4593..2955478b3 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -9,6 +9,7 @@ import pickle from copy import deepcopy +from enum import Enum import pytest @@ -816,3 +817,68 @@ def test_invalid_field_name(self): with pytest.raises(TypeError): copy.replace(inst, z=42) + + +class TestEnum: + """ + Tests for Enum + attrs interaction (issue #1287). + """ + + def test_define_enum(self): + """ + attrs.define works with Enum subclasses. + + Enum members can be created and attrs fields are accessible. + """ + + @attr.define + class AttrsDefine: + x: int + y: float + + class AttrsDefineEnum(AttrsDefine, Enum): + A = (1, 2.0) + B = (3, 4.0) + + assert 1 == AttrsDefineEnum.A.x + assert 2.0 == AttrsDefineEnum.A.y + assert 3 == AttrsDefineEnum.B.x + assert 4.0 == AttrsDefineEnum.B.y + + def test_frozen_enum(self): + """ + attrs.frozen works with Enum subclasses. + + Enum members can be created even with frozen base classes. + """ + + @attr.frozen + class AttrsFrozen: + x: int + y: float + + class AttrsFrozenEnum(AttrsFrozen, Enum): + A = (1, 2.0) + B = (3, 4.0) + + assert 1 == AttrsFrozenEnum.A.x + assert 2.0 == AttrsFrozenEnum.A.y + assert 3 == AttrsFrozenEnum.B.x + assert 4.0 == AttrsFrozenEnum.B.y + + def test_frozen_enum_is_hashable(self): + """ + attrs.frozen Enum subclasses are hashable. + """ + + @attr.frozen + class AttrsFrozen: + x: int + + class FrozenEnum(AttrsFrozen, Enum): + A = (1,) + B = (2,) + + # Should not raise TypeError + hash(FrozenEnum.A) + hash(FrozenEnum.B)