diff --git a/amaranth/lib/enum.py b/amaranth/lib/enum.py index 8cb026cfa..135bb58d8 100644 --- a/amaranth/lib/enum.py +++ b/amaranth/lib/enum.py @@ -41,9 +41,9 @@ def __new__(metacls, name, bases, namespace, shape=None, view_class=None, **kwar # Prepare enumeration members for instantiation. This logic is unfortunately very # convoluted because it supports two very different code paths that need to share # the emitted warnings. - for member_name, member_value in namespace.items(): - if py_enum._is_sunder(member_name) or py_enum._is_dunder(member_name): - continue + # TODO(py3.13): can use `namespace.member_names` property. + for member_name in namespace._member_names: + member_value = namespace[member_name] # If a shape is specified ("Amaranth mode" of amaranth.lib.enum.Enum), then every # member value must be a constant-castable expression. Otherwise ("Python mode" of # amaranth.lib.enum.Enum) any value goes, since all enumerations accepted by diff --git a/tests/test_lib_enum.py b/tests/test_lib_enum.py index 83dd03830..f4fad0d78 100644 --- a/tests/test_lib_enum.py +++ b/tests/test_lib_enum.py @@ -1,6 +1,7 @@ import enum as py_enum import operator import sys +import unittest from amaranth import * from amaranth.lib.enum import Enum, EnumMeta, Flag, IntEnum, EnumView, FlagView @@ -288,3 +289,23 @@ class EnumA(Enum, view_class=CustomView, shape=unsigned(2)): B = 1 a = Signal(EnumA) assert isinstance(a, CustomView) + + @unittest.skipUnless(hasattr(py_enum, "nonmember"), "Python<3.11 lacks nonmember") + def test_enum_member_nonmember(self): + with self.assertRaisesRegex( + TypeError, r"^Value \{\} of enumeration member 'x' must.*$" + ): + class EnumA(IntEnum, shape=4): + A = 1 + x = {} + + empty = {} + class EnumA(IntEnum, shape=4): + A = 1 + x = py_enum.nonmember(empty) + self.assertIs(empty, EnumA.x) + + class EnumB(IntEnum, shape=4): + A = 1 + B = py_enum.member(2) + self.assertIs(2, EnumB.B.value)