Update enum from CPython 3.15.0rc2 (#8942)

Sync enum and its regression tests from CPython v3.15.0rc2 (435c9e5a798c99653e3ab64ce29baed0e4f3dfee). Preserve the two existing RustPython expected-failure decorators. Keep this first migration slice limited to enum without changing the declared Python target or native runtime.

AI assistance: Codex/OpenAI reconstructed the upstream sync, reviewed the diff, and ran validation. The runtime did not expose the exact model version.

Assisted-by: Codex:model-version-unavailable

Co-authored-by: youknowdot <336763669+youknowdot@users.noreply.github.com>
This commit is contained in:
youknowdotandyouknowdot authored and GitHub committed 2026-10-13 10:50:51 +09:00
1 parent 140761e80b
commit 85edb52b65
2 files changed
+80 -10

No files matched your search

Vendored
+36 -7
View File
@@ -10,14 +10,15 @@ __all__ = [
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
'global_flag_repr', 'global_enum_repr', 'global_str', 'global_enum',
'EnumCheck', 'CONTINUOUS', 'NAMED_FLAGS', 'UNIQUE',
'pickle_by_global_name', 'pickle_by_enum_name',
'pickle_by_global_name', 'pickle_by_enum_name', 'show_flag_values',
'bin',
]
# Dummy value for Enum and Flag as there are explicit checks for them
# before they have been created.
# This is also why there are checks in EnumType like `if Enum is not None`
Enum = Flag = EJECT = _stdlib_enums = ReprEnum = None
Enum = Flag = EJECT = ReprEnum = None
class nonmember(object):
"""
@@ -536,7 +537,7 @@ class EnumType(type):
# now set the __repr__ for the value
classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)
#
# Flag structures (will be removed if final class is not a Flag
# Flag structures (will be removed if final class is not a Flag)
classdict['_boundary_'] = (
boundary
or getattr(first_enum, '_boundary_', None)
@@ -545,6 +546,29 @@ class EnumType(type):
classdict['_singles_mask_'] = 0
classdict['_all_bits_'] = 0
classdict['_inverted_'] = None
# check for negative flag values and invert if found (using _proto_members)
if Flag is not None and bases and issubclass(bases[-1], Flag):
bits = 0
inverted = []
for n in member_names:
p = classdict[n]
if isinstance(p.value, int):
if p.value < 0:
inverted.append(p)
else:
bits |= p.value
elif p.value is None:
pass
elif isinstance(p.value, tuple) and p.value and isinstance(p.value[0], int):
if p.value[0] < 0:
inverted.append(p)
else:
bits |= p.value[0]
for p in inverted:
if isinstance(p.value, int):
p.value = bits & p.value
else:
p.value = (bits & p.value[0], ) + p.value[1:]
try:
classdict['_%s__in_progress' % cls] = True
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
@@ -752,12 +776,16 @@ class EnumType(type):
super().__delattr__(attr)
def __dir__(cls):
if issubclass(cls, Flag):
members = list(cls._member_map_.keys())
else:
members = cls._member_names_
interesting = set([
'__class__', '__contains__', '__doc__', '__getitem__',
'__iter__', '__len__', '__members__', '__module__',
'__name__', '__qualname__',
]
+ cls._member_names_
+ members
)
if cls._new_member_ is not object.__new__:
interesting.add('__new__')
@@ -1489,7 +1517,10 @@ class Flag(Enum, boundary=STRICT):
)
if value < 0:
neg_value = value
value = all_bits + 1 + value
if cls._boundary_ in (EJECT, KEEP):
value = all_bits + 1 + value
else:
value = singles_mask & value
# get members and unknown
unknown = value & ~flag_mask
aliases = value & ~singles_mask
@@ -2165,5 +2196,3 @@ def _old_convert_(etype, name, module, filter, source=None, *, boundary=None):
members.sort(key=lambda t: t[0])
cls = etype(name, members, module=module, boundary=boundary or KEEP)
return cls
_stdlib_enums = IntEnum, StrEnum, IntFlag
+44 -3
View File
@@ -1002,12 +1002,18 @@ class _FlagTests:
self.assertIs(~(A|B), OpenAB(252))
self.assertIs(~AB_MASK, OpenAB(0))
self.assertIs(~OpenAB(0), AB_MASK)
self.assertIs(OpenAB(~4), OpenAB(251))
else:
self.assertIs(~A, B)
self.assertIs(~B, A)
self.assertIs(OpenAB(~1), B)
self.assertIs(OpenAB(~2), A)
self.assertIs(~(A|B), OpenAB(0))
self.assertIs(~AB_MASK, OpenAB(0))
self.assertIs(~OpenAB(0), (A|B))
self.assertIs(OpenAB(~3), OpenAB(0))
self.assertIs(OpenAB(~4), OpenAB(3))
self.assertIs(OpenAB(~33), B)
#
class OpenXYZ(self.enum_type):
X = 4
@@ -1031,6 +1037,9 @@ class _FlagTests:
self.assertIs(~X, Y|Z)
self.assertIs(~Y, X|Z)
self.assertIs(~Z, X|Y)
self.assertIs(OpenXYZ(~4), Y|Z)
self.assertIs(OpenXYZ(~2), X|Z)
self.assertIs(OpenXYZ(~1), X|Y)
self.assertIs(~(X|Y), Z)
self.assertIs(~(X|Z), Y)
self.assertIs(~(Y|Z), X)
@@ -1038,6 +1047,28 @@ class _FlagTests:
self.assertIs(~XYZ_MASK, OpenXYZ(0))
self.assertTrue(~OpenXYZ(0), (X|Y|Z))
def test_assigned_negative_value(self):
class X(self.enum_type):
A = auto()
B = auto()
C = A | B
D = ~A
self.assertEqual(list(X), [X.A, X.B])
self.assertIs(~X.A, X.B)
self.assertIs(X.D, X.B)
self.assertEqual(X.D.value, 2)
#
class Y(self.enum_type):
A = auto()
B = auto()
C = A | B
D = ~A
E = auto()
self.assertEqual(list(Y), [Y.A, Y.B, Y.E])
self.assertIs(~Y.A, Y.B|Y.E)
self.assertIs(Y.D, Y.B|Y.E)
self.assertEqual(Y.D.value, 6)
class TestPlainEnumClass(_EnumTests, _PlainOutputTests, unittest.TestCase):
enum_type = Enum
@@ -3681,6 +3712,8 @@ class OldTestFlag(unittest.TestCase):
C = 4 | B
#
self.assertTrue(SkipFlag.C in (SkipFlag.A|SkipFlag.C))
self.assertTrue(SkipFlag.B in SkipFlag.C)
self.assertIs(SkipFlag(~1), SkipFlag.B)
self.assertRaisesRegex(ValueError, 'SkipFlag.. invalid value 42', SkipFlag, 42)
#
class SkipIntFlag(enum.IntFlag):
@@ -3689,6 +3722,8 @@ class OldTestFlag(unittest.TestCase):
C = 4 | B
#
self.assertTrue(SkipIntFlag.C in (SkipIntFlag.A|SkipIntFlag.C))
self.assertTrue(SkipIntFlag.B in SkipIntFlag.C)
self.assertIs(SkipIntFlag(~1), SkipIntFlag.B|SkipIntFlag.C)
self.assertEqual(SkipIntFlag(42).value, 42)
#
class MethodHint(Flag):
@@ -4728,6 +4763,8 @@ class TestVerify(unittest.TestCase):
BLUE = 4
WHITE = -1
# no error means success
self.assertEqual(list(Color.WHITE), [Color.RED, Color.GREEN, Color.BLUE])
self.assertEqual(Color.WHITE.value, 7)
class TestInternals(unittest.TestCase):
@@ -4851,7 +4888,7 @@ class TestInternals(unittest.TestCase):
def _generate_next_value_(name, start, count, last):
return name
def test_auto_order_wierd(self):
def test_auto_order_weird(self):
weird_auto = auto()
weird_auto.value = 'pathological case'
class Color(Enum):
@@ -5288,7 +5325,7 @@ class TestStdLib(unittest.TestCase):
class MiscTestCase(unittest.TestCase):
def test__all__(self):
support.check__all__(self, enum, not_exported={'bin', 'show_flag_values'})
support.check__all__(self, enum)
@cpython_only
def test_lazy_import(self):
@@ -5493,12 +5530,16 @@ class TestEnumDict(unittest.TestCase):
# helpers
def enum_dir(cls):
if issubclass(cls, Flag):
members = list(cls._member_map_.keys())
else:
members = cls._member_names_
interesting = set([
'__class__', '__contains__', '__doc__', '__getitem__',
'__iter__', '__len__', '__members__', '__module__',
'__name__', '__qualname__',
]
+ cls._member_names_
+ members
)
if cls._new_member_ is not object.__new__:
interesting.add('__new__')