lib.enum: honor enum.nonmember.
Use _EnumDict._member_names to determine which members to consider. This way we don't need to redo sunder/dunder checks, and `nonmember`s (introduced in py3.11) are correctly excluded. This is a defacto public API, given it remains usable from py3.8 until py3.12 inclusive. (_member_names changes from a list to a keys-only dict for performance reasons in py3.11, but they iterate the same.) In current Python main (i.e. what will most likely be 3.13), a "member_names" property is added which returns those keys.
This commit is contained in:
parent
890e099ec3
commit
c40cfc9fb5
|
@ -41,9 +41,9 @@ class EnumMeta(ShapeCastable, py_enum.EnumMeta):
|
||||||
# Prepare enumeration members for instantiation. This logic is unfortunately very
|
# Prepare enumeration members for instantiation. This logic is unfortunately very
|
||||||
# convoluted because it supports two very different code paths that need to share
|
# convoluted because it supports two very different code paths that need to share
|
||||||
# the emitted warnings.
|
# the emitted warnings.
|
||||||
for member_name, member_value in namespace.items():
|
# TODO(py3.13): can use `namespace.member_names` property.
|
||||||
if py_enum._is_sunder(member_name) or py_enum._is_dunder(member_name):
|
for member_name in namespace._member_names:
|
||||||
continue
|
member_value = namespace[member_name]
|
||||||
# If a shape is specified ("Amaranth mode" of amaranth.lib.enum.Enum), then every
|
# 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
|
# member value must be a constant-castable expression. Otherwise ("Python mode" of
|
||||||
# amaranth.lib.enum.Enum) any value goes, since all enumerations accepted by
|
# amaranth.lib.enum.Enum) any value goes, since all enumerations accepted by
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import enum as py_enum
|
import enum as py_enum
|
||||||
import operator
|
import operator
|
||||||
import sys
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
from amaranth import *
|
from amaranth import *
|
||||||
from amaranth.lib.enum import Enum, EnumMeta, Flag, IntEnum, EnumView, FlagView
|
from amaranth.lib.enum import Enum, EnumMeta, Flag, IntEnum, EnumView, FlagView
|
||||||
|
@ -288,3 +289,23 @@ class EnumTestCase(FHDLTestCase):
|
||||||
B = 1
|
B = 1
|
||||||
a = Signal(EnumA)
|
a = Signal(EnumA)
|
||||||
assert isinstance(a, CustomView)
|
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)
|
||||||
|
|
Loading…
Reference in a new issue